I'm working on an OpenGL project that is basically just a 2D painting right now. You can add/remove "blocks" from the canvas and do some other basic stuff. I'm trying to implement multiple textures and am having a hard time doing so. To be clear, I don't need multiple shaders, just different textures. Here's what my Block class looks like:
#pragma once
#include "shader_s.h"
class Texture {
Shader* shader;
unsigned char *data;
int width, height, nrChannels;
//Shader shader;
public:
Texture(char* image) {
shader = new Shader("shader.vs", "shader.fs");
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_set_flip_vertically_on_load(true);
data = stbi_load(image, &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
shader->setInt("texture1", 0);
}
public:
Shader getShader() {
return *shader;
}
public:
void use() {
shader->use();
}
};
and this is where the shader_s header comes from(learnopengl).
Basically, the main problem is that the code in the constructor regarding the OpenGL stuff gets overriden by the next texture(I think that's the problem). The result is that all Texture classes other than the last one created are ignored. I have no idea how to fix this because I'm not 100% sure what all the OpenGL functions do.
Related
I had this problem when compiling my OpenGL code on lower-end PC's that don't support OpenGL 4.5 and macs. In my regular code, I would use functions like glCreateTextures and glTextureStorage2D, but they are not supported in other versions so I went the other glGenTextures path.
Here's the image generation code:
Texture::Texture(const std::string& path)
: m_Path(path)
{
int width, height, channels;
stbi_set_flip_vertically_on_load(1);
unsigned char* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
RW_CORE_ASSERT(data, "Failed to load image!");
m_Width = width;
m_Height = height;
GLenum internalFormat = 0, dataFormat = 0;
if (channels == 4)
{
internalFormat = GL_RGBA8;
dataFormat = GL_RGBA;
}
else if (channels == 3)
{
internalFormat = GL_RGB8;
dataFormat = GL_RGB;
}
m_InternalFormat = internalFormat;
m_DataFormat = dataFormat;
RW_CORE_ASSERT(internalFormat & dataFormat, "Format not supported!");
glGenTextures(1, &m_ID);
glBindTexture(GL_TEXTURE_2D, m_ID);
glTexParameteri(m_ID, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(m_ID, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(m_ID, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(m_ID, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(m_ID, 1, internalFormat, m_Width, m_Height, 0, dataFormat, GL_UNSIGNED_BYTE, data);
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(data);
}
And I want to bind my textures to specific slots on the GPU so I have this function:
void Texture::Bind(uint32_t slot) const
{
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, m_ID);
}
Here's the screenshot of what gets drawn:
To make sure it wasn't a rendering problem I decided to put it into ImGui's renderer.
And here's the picture I am supposed to get:
And image imports correctly, I get no errors and same importing code and paths work on higher-end PC and the only thing that's changed is that the higher-end PC has OpenGL 4.5 texture generation code.
It turns out I had to specify GL_TEXTURE_2D in these places instead of texture ID:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 1, internalFormat, m_Width, m_Height, 0, dataFormat, GL_UNSIGNED_BYTE, data);
I created a class for OpenGL textures and for some applications, a static object would be useful.
The problem is, when I create a texture as static or global object, my program crashes, while as local object, everything works fine. I have absolutely no idea what is going on.
This is my constructor:
Texture::Texture(std::string file, bool bitmap):
textureName("tex"), transparent(false) {
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
int texWidth, texHeight;
unsigned char *data = SOIL_load_image(file.c_str(), &texWidth, &texHeight, 0, SOIL_LOAD_RGBA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if(bitmap) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
glBindTexture(GL_TEXTURE_2D, 0);
for(int i = 3; i < texWidth * texHeight * 4; i+= 4) {
if(data[i] != 0xff) {
transparent = true;
break;
}
}
textureSize = glm::vec2(texWidth, texHeight);
SOIL_free_image_data(data);
}
OpenGL must be initialized before you can call any OpenGL functions. (More accurately: you need to have "made an OpenGL context current")
The way you do that depends on which libraries you are using.
One thing that all the libraries have in common is: there are some functions you need to call, which you usually call in main. (for example, glfwCreateWindow and glfwMakeContextCurrent in GLFW)
Since global objects are created before main is called, you are trying to call OpenGL functions before you have an OpenGL context. This doesn't work.
Hello everyone I'm trying to load a texture (normal map) using OpenGL.
GLuint loadTexture(const char* fileName) {
GLuint textureID;
glGenTextures(1, &textureID);
// load file - using core SDL library
SDL_Surface* tmpSurface;
tmpSurface = SDL_LoadBMP(fileName);
if (tmpSurface == nullptr) {
std::cout << "Error loading bitmap" << std::endl;
}
// bind texture and set parameters
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tmpSurface->w, tmpSurface->h, 0,
GL_BGR, GL_UNSIGNED_BYTE, tmpSurface->pixels);
glGenerateMipmap(GL_TEXTURE_2D);
SDL_FreeSurface(tmpSurface);
return textureID;
}
then if I try to render it, it gives me that :
Instead of :
But I can render this normaly :
Do you have an idea ? color deep is 32 for the one that is not working and 24 for the working one
Use SDL_ConvertSurfaceFormat to convert the surface format and load the converted curface:
SDL_Surface* tmpSurface = SDL_LoadBMP(fileName);
SDL_Surface* formattedSurface = SDL_ConvertSurfaceFormat(
tmpSurface, SDL_PIXELFORMAT_ABGR8888, 0);
SDL_FreeSurface(tmpSurface);
// [...]
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tmpSurface->w, tmpSurface->h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, formattedSurface->pixels);
SDL_FreeSurface(formattedSurface);
Of course you can evaluate the format attribute of the SDL_Surface and set a proper format attribute when two-dimensional texture image is specified by glTexImage2D.
I am trying to load OBJ files to use them in OpenGL. Every object in the OBJ file has its own texture. In my code I split up every object of the file into a struct that is defended like this:
struct ObjModelComponent {
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> uvs;
std::vector<glm::vec3> normals;
Texture tex;
ArrayBuffer vertex, uv, normal;
GLuint VAO;
};
The Texture class is a simple class to make loading and handling a texture easier.
In the class of the model I have a std::vector of my ObjModelComponent.
I'm rendering the Object like this:
void ObjModel::render() {
for(int i = 0; i < components.size(); i++) {
components[i].vertex.activate();
components[i].uv.activate();
components[i].normal.activate();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, components[i].tex.getTextureID());
shader->sendInt(0, components[i].tex.getTextureName());
glBindVertexArray(components[i].VAO);
shader->sendMat4(*data->projection, "projection");
shader->sendMat4(data->viewMat, "view");
shader->sendMat4(model, "model");
glDrawArrays(GL_TRIANGLES, 0, int(components[i].vertices.size()));
glBindVertexArray(0);
}
}
The Texture class looks like this:
Texture::Texture(std::string fileName, TextureType type):
textureName("tex"), fileName(fileName), type(type) {
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
unsigned char *image = SOIL_load_image(fileName.c_str(), &texWidth, &texHeight, 0, SOIL_LOAD_RGBA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
switch (type) {
case TEXTURE_GENERATE_MIPMAP:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
break;
case TEXTURE_NO_MIP_MAP:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
default:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
break;
}
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
}
Texture::Texture() {
}
Texture& Texture::operator=(const Texture &other) {
this->fileName = other.fileName;
this->textureName = other.textureName;
this->type = other.type;
// glDeleteTextures(1, &tex);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
unsigned char *image = SOIL_load_image(fileName.c_str(), &texWidth, &texHeight, 0, SOIL_LOAD_RGBA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
switch (type) {
case TEXTURE_GENERATE_MIPMAP:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
break;
case TEXTURE_NO_MIP_MAP:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
default:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
break;
}
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
return *this;
}
Texture::~Texture() {
glDeleteTextures(1, &tex);
}
GLuint Texture::getTextureID() {
return tex;
}
void Texture::setTextureName(std::string name) {
textureName = name;
}
std::string Texture::getTextureName() {
return textureName;
}
glm::vec2 Texture::getTextureSize() {
return glm::vec2(texWidth, texHeight);
}
This is how the components are generated:
for(int i = 0; i < fileLines.size(); i++) {
if(fileLines[i].substr(0, 2) == "o ") {
if(!first) {
tmpComp.tex = tmpTex;
components.emplace_back(tmpComp);
componentIndex++;
}
first = false;
tmpTex = Texture(hg::substr(path, 0, int(path.find_last_of("/"))) + "/" + hg::substr(fileLines[i], 2, int(fileLines[i].length())) + ".png");
}
}
This is how the VAOs and array buffers are generated:
for(int i = 0; i < components.size(); i++) {
glGenVertexArrays(1, &components[i].VAO);
glBindVertexArray(components[i].VAO);
components[i].vertex.setData(components[i].vertices.data(), sizeof(glm::vec3) * components[i].vertices.size(), 0);
components[i].uv.setData(components[i].uvs.data(), sizeof(glm::vec2) * components[i].uvs.size(), 1);
components[i].normal.setData(components[i].normals.data(), sizeof(glm::vec3) * components[i].normals.size(), 2);
components[i].vertex.activate();
components[i].uv.activate();
components[i].normal.activate();
glBindVertexArray(0);
}
You can find my complete code on GitHub: https://github.com/Kuechenzwiebel/SDL-OpenGL-Tests
The problem is, that the texture displayed on my object is that, that was used by the last object that was rendered.
Update: Removing the glDeleteTextures in the destructor of the Texture, yields in the result, that the texture displayed on all components is that that was used for the last component.
I really don't understand why this isn't working, I copied it from other primitives, that only use one texture. And there everything is working fine.
I found a workaround, instead of having separate ArrayBuffers and VAOs, I only use one of each and also store at which index a new object begins. There I change the texture that is used.
I'm new to opengl and I have this puppet (head,torso,legs,arms) and I would like to set the texture on only the torso part of this puppet.
I was under the assumption that before I draw the torso portion of my puppet I can simply call glEnable(GL_TEXTURE_2D) and after drawing call glDisable(GL_TEXTURE_2D), but I seem to be getting a GL_INVALID_ENUM error.
// Code for loading textures called in an init function at startup
void loadTextures()
{
// load and create a texture
// -------------------------
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); // all upcoming GL_TEXTURE_2D operations now have effect on this texture object
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture and generate mipmaps
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
}
// Code for drawing the puppet
void A3::renderNode(const SceneNode * root, mat4 view, mat4 model) {
if (root == nullptr) return;
mat4 tmp_model = model * root->get_transform();
if (root->m_nodeType == NodeType::GeometryNode) {
const GeometryNode * geometryNode = static_cast<const GeometryNode *>(root);
updateShaderUniforms(m_shader, *geometryNode, view, tmp_model , picking);
// Get the BatchInfo corresponding to the GeometryNode's unique MeshId.
BatchInfo batchInfo = m_batchInfoMap[geometryNode->meshId];
m_shader.enable();
if (root->m_name == "torso")
glEnable(GL_TEXTURE_2D);
glDrawArrays(GL_TRIANGLES, batchInfo.startIndex, batchInfo.numIndices);
glDisable(GL_TEXTURE_2D);
m_shader.disable();
}
for (const SceneNode * node : root->children) {
renderNode(node, view, tmp_model); // recursion
}
}