Imagine that OpenGL internally looks like this:
struct TextureUnit
{
GLuint targetTexture1D;
GLuint targetTexture2D;
GLuint targetTexture3D;
GLuint targetTextureCube;
...
};
TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS]
GLuint currentTextureUnit = 0;
glActiveTexture means this:
void glActiveTexture(GLenum textureUnit)
{
currentTextureUnit = GL_TEXTURE0 - textureUnit;
}
And glBindTexture does this:
void glBindTexture(GLenum textureTarget, GLuint textureObject)
{
TextureUnit *texUnit = &textureUnits[currentTextureUnit];
switch(textureTarget)
{
case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break;
case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break;
case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break;
case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break;
}
}
Obviously there would be error testing, but that’s the idea. All of the functions that modify the texture use the “currentTextureUnit” set by glActiveTexture. The target parameter they take tells which bound texture location within that texture unit to use.
So if you have:
glActiveTexture(GL_TEXTURE0 + 5);
glBindTexture(GL_TEXTURE_2D, object);
glTexImage2D(GL_TEXTURE_2D, ...);
The texture being uploaded to is the one stored in textureUnits[5]->targetTexture2D. Each texture that you bind has a texture target and a texture unit; this specifies its unique location in the context.
I don’t have that particular book, but one often binds a texture to the context just to upload some data or to modify it. It doesn’t matter at that point which texture unit you bind it to, so there’s no need to set the current texture unit. glTexImage2D doesn’t care if the current active texture is 0, 1, 40, or whatever.
However, texture units have special meaning to the rendering of objects. When you bind textures for the purpose of rendering with them, you need to bind them to a particular texture unit. Therefore, you need to set the current texture unit before you do the bind.