In the previous tutorials we have created a textured 2D image that we can control and transform. We have talked about the texturing just a little bit. The shader parts will be explained in great detail in our future tutorials about shaders. The OpenGL ES 2.0 part of the code is what we are going to handle in this tutorial. I will have to warn you, this tutorial will be theory for the most part as it will try to explain how the texture system works in OpenGL ES 2.0.
As some of you may already have noticed is that you have to bind a texture before you can do stuff with it. This binding system is for some beginning programmers a bit confusing as it works different from what they know. OpenGL basically does all calls to a currently selected object and changes it state that way. Changing the state of an object means in this case changing variables, objects etc from that object.
The image above illustrates this process. You first active a texture unit. Once you have done this, you can do all sorts of function calls and they all change the state of that texture unit. We bind it to a target, we set all sorts of parameters and we attach texture data to it.
The reason for this design is to avoid as many data send over a call as possible. The slowest piece of hardware on video hardware is often the data bus. This is a part of the hardware chain to get your data to the hardware. Due to this, OpenGL decided to create a system that would allow as few data send over that as possible. With this system you can set a lot of the data needed beforehand and thus when you are doing calls when performance is needed, the data send is limited as most of the data is already present. To get a good grasp of why OpenGL is what it is, you should probably read this nice article.
OpenGL Texture system
The OpenGL texture system works this way with a little twist. When you bind a texture you bind it to a target as well. This results in OpenGL knowing more about the texture and setting up some stuff internally for it. When you bind a texture to a target you can not change the target of that texture anymore. This means that a texture target GL_TEXTURE_2D (2 dimensional texture) cannot be changed into GL_TEXTURE_1D (1 dimensional texture) or any of the other types of targets.
But targets are not the only part of textures. You have also texture units. The texture unit is basically the id of the texture so OpenGL knows which texture is which. So lets take a look at switching to a texture.
The function glActiveTexture takes only one integer. OpenGL gives us some constants regarding textures: GL_TEXTUREXX. This ranges from 0 to 31, which suggests that there can only be 32 textures. This is however not true! The amount of textures is hardware dependent. This means that phone type 1 supported 48 textures while phone type 2 only supports 24 textures. For both we still have the GL_TEXTUREXX range to 31, which in both cases is not correct. For phone type 1 we have to few constants, for phone type 2 we have to few actual texture room.
So we should know how many textures are available to us. To do this there are multiple functions that retrieve the amount of available textures. The trick is to use the correct one as all retrieve different maximum values due to different parts of the texture system. For example, you can retrieve the maximum amount of textures that can be used by a shader, may be useful if you need it but it won’t give us the total amount of textures. To get the total amount of textures we use the following code:
// Get the maximum of texture units we can use. IntBuffer i = IntBuffer.allocate(1); GLES20.glGetIntegerv(GLES20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, i);
So if those GL_TEXTUREXX constants are just for show or something, how do we active our textures? Plain and simple:
glActiveTexture(GL_TEXTURE0 + indx);
Just be sure that the indx variable does not go beyond the max possible textures units. Now, we know how many textures we can use and how we can use them all. But what about those targets? As you have seen, we only pass a texture unit id to OpenGL to activate a texture, so how do we tell what target is being used? In our rendering functions we specify which texture we want to use by using the GLES20.glUniform1i(SamplerLocation, TextureUnitId) function call. Shaders have access to all textures, the activity of the texture does not matter. Once we have passed the texture unit id to our shader it depends on the type of sampler used by the shader what target is used. So if the shader uses a Sampler2D it will get the texture from GL_TEXTURE_2D.
Now you should understand the inner workings a bit better!
- A real Open GL ES 2.0 2D tutorial part 1: Rendering a triangle
- A real Open GL ES 2.0 2D tutorial part 2: Rendering an Images
- A real Open GL ES 2.0 2D tutorial part 3: Handling Input
- A real Open GL ES 2.0 2D tutorial part 4: Transforming Images
- A real Open GL ES 2.0 2D tutorial part 5: Knowing the OpenGL texture system
- A real Open GL ES 2.0 2D tutorial part 6: Screens and dimensions
- A real Open GL ES 2.0 2D tutorial part 7: Texture Atlas
- A real Open GL ES 2.0 2D tutorial part 8: Rendering Text