O'Reilly logo

iPhone 3D Programming by Philip Rideout

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Image Composition and a Taste of Multitexturing

Sometimes it’s desirable to split a sprite sheet into multiple layers, as shown in Figure 7-13. The left sheet has the animation frames for Noop’s body; the right sheet has his eyes and shiny highlights. This allows the application to vary the colors of the layers independently. For example, my game can draw Noop using a yellowish hue most of the time but sometimes renders him in orange to convey that he’s hurt. In both cases, the eyes and highlights are white.

Noop layers

Figure 7-13. Noop layers

We discussed how to shift the apparent color of a texture in Shifting Texture Color with Per-Vertex Color. You can use a luminance or luminance-alpha texture rather than a full-blown RGBA texture and then modulate the texture’s color using per-vertex color (for example, by calling glColor4f).

The obvious way of composing Noop’s eyes with his body is to render the same quad in two passes with blending enabled. The first pass uses texture coordinates for the body; the second pass uses coordinates for the eyes and highlights. Example 7-13 shows an example of this procedure.

Example 7-13. Rendering Noop in two passes

// Enable Blending:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Draw Noop's body in a yellowish hue:
glColor4f(1, 0.83f, 0.33f, 1);
glBindTexture(GL_TEXTURE_2D, bodyTexture);
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, sourceRectangle);
glDrawTexfOES(x, y, 0, width, height);

// Draw Noop's eyes in white:
glColor4f(1, 1, 1, 1);
glBindTexture(GL_TEXTURE_2D, eyesTexture);
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, sourceRectangle);
glDrawTexfOES(x, y, 0, width, height);

Note that Example 7-13 is valid only for ES 1.1; under ES 2.0, we need to replace the DrawTex-related lines with calls to glDrawArrays or glDrawElements, and we need to replace glColor4f with glVertexAttrib4f. See Example 7-14.

Example 7-14. Two-pass Noop with ES 2.0

// Enable Blending:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Draw Noop's body in a yellowish hue:
glVertexAttrib4f(MyColorAttribute, 1, 0.83f, 0.33f, 1);
glBindTexture(GL_TEXTURE_2D, bodyTexture);
glDrawArrays(GL_TRIANGLES, 0, 6); // draw a rectangle with two triangles

// Draw Noop's eyes in white:
glVertexAttrib4f(MyColorAttribute, 1, 1, 1, 1);
glBindTexture(GL_TEXTURE_2D, eyesTexture);
glDrawArrays(GL_TRIANGLES, 0, 6); // draw a rectangle with two triangles

Both OpenGL ES 1.1 and ES 2.0 provide a way to combine simple two-pass operations like this into a single draw call. It’s called multitexturing. Multitexturing allows you to set up more than one texture stage. Example 7-15 shows the sample code for rendering Noop with multitexturing; note there’s only one call to glDrawTexfOES.

Example 7-15. One-pass Noop with multitexturing

glColor4f(1, 0.83f, 0.33f, 1);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, bodyTexture);
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, sourceRectangle);

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, eyesTexture);
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, sourceRectangle);
glDrawTexfOES(x, y, 0, width, height);

The key lines in Example 7-15 are the calls to glActiveTexture, which sets the current texture stage and affects all subsequent texture-related calls, including glEnable(GL_TEXTURE_2D). This allows individual stages to be independently turned on or off.

I should warn you that Example 7-15 alone is not quite enough; you also need to tell OpenGL how to combine the color values from the two texture stages. With ES 1.1, this is quite a hassle; see Example 7-16. This sets up the second texture stage so that it works in a way similar to typical alpha blending. Thankfully, you can often perform this type of configuration only once, when your application first starts up.

Example 7-16. Horrific texture stage configuration with ES 1.1

glActiveTexture(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

OpenGL ES 2.0 simplifies this by allowing you to combine colors from within your fragment shader. We’ll discuss this further in the next chapter, and I’ll explain glTexEnv in greater detail—I just wanted to whet your appetite!

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required