Saturday, August 01, 2009

Multitexturing on Opengl ES

I wanted to create a random highlight animation effect on the buttons that glides over them every now and then. At first I was going to do it in a multipass approach, i.e. first draw the button polygons, then the highlight over them. However it should be more efficient (with loads of polygons anyway) if multitexturing is used. The iPhone has 2 Texture Units (TU) and so I can take advantage of that.

I never had done multitexturing in opengl before, so I had to learn the concept. If you understand the blending functions with the frame buffer, then multitexturing will be easy. The difference is that you can combine the results of texture units. Since we have two TUs we can make the first texture blend with the frame buffer, and then overlay the second texture by adding it to the result of the previous TU (GL_PREVIOUS).
You dictate how the TUs will combine by specifying whether GL_COMBINE_RGB is GL_MODULE, GL_ADD, GL_DECAL, and GL_REPLACE. One should take a look at what each one will calculate to, and also experiment a bit with them.

Here is more detailed information about texture combiners using a fixed pipeline, and what it would like if we used shaders. I must admit that shaders are easier to read, at least such simple shaders, but shaders are only supported in iPhone 3GS.

Before setting up the multitexturing, I set up the texture coordinates for both TUs:
glClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, sizeof(VertexDataMultiTextured), &vd[0].uv0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, sizeof(VertexDataMultiTextured), &vd[0].uv1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

Then I set up how the TUs should behave. In my case I had a texture for the button (in an atlas) and used the following openGL commands
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, [_texture name]);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
//blend the texture with the framebuffer(GL_PREVIOUS)
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_BLEND);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
//use the texture's alpha channel
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
//------------------------

And then I selected the second texture unit and added the color information of the glow texture (which was in the same atlas) to the result of the previous TU:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, [_texture name]);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
//add the previous color information with the texture's color information
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
//don't effect the alpha channel, use the result (GL_PREVIOUS) of the previous texture unit
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);

No comments: