I have a functioning OpenGL ES 3 program (iOS), but I've having a difficult time understanding OpenGL textures. I'm trying to render several quads to the screen, all with different textures. The textures are all 256 color images with a sperate palette.
This is C++ code that sends the textures to the shaders
// THIS CODE WORKS, BUT I'M NOT SURE WHY
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _renderQueue[idx]->TextureId);
glUniform1i(_glShaderTexture, 1); // what does the 1 mean here
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, _renderQueue[idx]->PaletteId);
glUniform1i(_glShaderPalette, 2); // what does the 2 mean here?
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
This is the fragment shader
uniform sampler2D texture; // New
uniform sampler2D palette; // A palette of 256 colors
varying highp vec2 texCoordOut;
void main()
{
highp vec4 palIndex = texture2D(texture, texCoordOut);
gl_FragColor = texture2D(palette, palIndex.xy);
}
As I said, the code works, but I'm unsure WHY it works. Several seemingly minor changes break it. For example, using GL_TEXTURE0
, and GL_TEXTURE1
in the C++ code breaks it. Changing the numbers in glUniform1i
to 0, and 1 break it. I'm guessing I do not understand something about texturing in OpenGL 3+ (maybe Texture Units???), but need some guidance to figure out what.
Since it's often confusing to newer OpenGL programmers, I'll try to explain the concept of texture units on a very basic level. It's not a complex concept once you pick up on the terminology.
The whole thing is motivated by offering the possibility of sampling multiple textures in shaders. Since OpenGL traditionally operates on objects that are bound with
glBind*()
calls, this means that an option to bind multiple textures is needed. Therefore, the concept of having one bound texture was extended to having a table of bound textures. What OpenGL calls a texture unit is an entry in this table, designated by an index.If you wanted to describe this state in a C/C++ style notation, you could define the table of bound texture as an array of texture ids, where the size is the maximum number of bound textures supported by the implementation (queried with
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, ...)
):If you bind a texture, it gets bound to the currently active texture unit. This means that the last call to
glActiveTexture()
determines which entry in the table of bound textures is modified. In a typical call sequence, which binds a texture to texture uniti
:this would correspond to modifying our imaginary data structure by:
That covers the setup. Now, the shaders can access all the textures in this table. Variables of type
sampler2D
are used to access textures in the GLSL code. To determine which texture eachsampler2D
variable accesses, we need to specify which table entry each one uses. This is done by setting the uniform value to the table index:specifies that the sampler uniform at location
samplerLoc
reads from table entryi
, meaning that it samples the texture with idBoundTextureIds[i]
.In the specific case of the question, the first texture was bound to texture unit 1 because
glActiveTexture(GL_TEXTURE1)
was called beforeglBindTexture()
. To access this texture from the shader, the shader uniform needs to be set to 1 as well. Same thing for the second texture, with texture unit 2.(The description above was slightly simplified because it did not take into account different texture targets. In reality, textures with different targets, e.g.
GL_TEXTURE_2D
andGL_TEXTURE_3D
, can be bound to the same texture unit.)