1
votes

I am writing an openGL program and have multiple models drawn into the environment, but I don't know how to apply different textures to each of those models, so for now they all have the same texture. I've read that I need to add multiple texture units into the program or use a texture atlas. A texture atlas seems more complicated, so I'm trying to add texture units.

The way I thought this process worked is:

  1. Generate two texture units with glGenTextures.
  2. Bind the data of the first image to the first texture unit with glBindTexture and glTexImage2D.
  3. Do the same with the second image.

From here, I thought I could tell openGL which texture unit I want to use using glActiveTexture. This seems to work with just one texture (i.e. skip step 3) but fails with two or more.

I'm sure I'm missing something, so could someone point me in the right direction?

//Generate textures
int texturec=2;
int w[texturec],h[texturec];
unsigned char *data[texturec];
data[0]=getImage(&w[0],&h[0],"resources/a.png");
data[1]=getImage(&w[1],&h[1],"resources/b.png");

//Apply textures
GLuint textures[texturec];
glGenTextures(texturec, textures);

//Bind a.png to the first texture unit
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[0], h[0], 0, GL_RGB, GL_UNSIGNED_BYTE, data[0]);

//Bind b.png to the second texture unit
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[1], h[1], 0, GL_RGB, GL_UNSIGNED_BYTE, data[1]);

glActiveTexture(GL_TEXTURE0);

//Not super clear on what this does, but it needs to be here.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

And here are my shaders:

Fragment:

#version 330 core

in vec2 UV;
out vec3 color;

uniform sampler2D textureSampler;

void main(){
    color=texture(textureSampler,UV).rgb;
}

Vertex:

#version 330 core

layout(location=0) in vec3 vertexPos;
layout(location=1) in vec2 vertexUV;

out vec2 UV;

uniform mat4 MVP;

void main(){
    gl_Position=MVP*vec4(vertexPos,1);
    UV=vertexUV;
}

Edit:

Here is my new code after applying Rabid76's suggestions:

//Generate textures
int texturec=2;
int w[texturec],h[texturec];
unsigned char *data[texturec];
data[0]=getImage(&w[0],&h[0],"resources/a.png");
data[1]=getImage(&w[1],&h[1],"resources/b.png");

//Apply textures
GLuint textures[texturec];
glGenTextures(texturec, textures);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[0], h[0], 0, GL_RGB, GL_UNSIGNED_BYTE, data[0]);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w[1], h[1], 0, GL_RGB, GL_UNSIGNED_BYTE, data[1]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

//Shaders
programID=loadShaders("shaders/vertex.shader","shaders/fragment.shader");
glUseProgram(programID);

//Use texture unit 1
glActiveTexture(GL_TEXTURE1);
GLint texLoc=glGetUniformLocation(programID, "textureSampler");
glUniform1i(texLoc, 1);
1

1 Answers

3
votes
  1. Generate two texture units with glGenTextures.

No. glGenTextures doesn't generate a texture unit.

glGenTextures reserves name value(s), which can be used for texture objects.

glBindTexture binds a named texture to a texturing target. When this function is called, then the texture object is bound to the current texture unit.
The current texture unit can be set by glActiveTexture:

e.g.

GLuint textures[texturec];
glGenTextures(texturec, textures);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
// [...]

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
// [...]

The texture objects name value and the texture unit are completely different things.
The texture unit is the binding point between the texture object and the shader program.
On the one side, the texture object has to be bound to the texture unit, on the other side the texture unit has to be set to the texture sampler uniform. So the texture unit is the "link" in between.

Since GLSL version 4.2, the texture unit can be set by a Layout Qualifier (GLSL) within the shader. The Binding point corresponds to the texture unit. e.g. binding = 1 means texture unit 1:

layout(binding = 1) uniform sampler2D textureSampler;

Alternatively the texture unit index can be assigned to the texture sampler uniform by glUniform1i:

GLint texLoc = glGetUniformLocation(programID, "textureSampler");
glUniform1i(texLoc, 1);

If the shader program uses 1 texture sampler uniform

uniform sampler2D textureSampler;

then it is sufficient to bind the proper texture object before the draw call:

glActiveTexture(GL_TEXTURE0); // this is default 
glBindTexture(GL_TEXTURE_2D, textures[0]);

But if the shader program uses multiple texture sampler uniforms (of the same target), then the different texture objects have to be bound to different texture units:

e.g.

layout(binding = 3) uniform sampler2D textureSampler1;
layout(binding = 4) uniform sampler2D textureSampler2;
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, textures[0]);

glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, textures[1]);

respectively

layout(location = 7) uniform sampler2D textureSampler1;
layout(location = 8) uniform sampler2D textureSampler2;
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, textures[0]);

glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, textures[1]);

glUseProgram(programID);
glUniform1i(7, 3); // location = 7 <- texture unit 3
glUniform1i(8, 4); // location = 8 <- texture unit 4