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

Chapter 8. Advanced Lighting and Texturing

There are two kinds of light—the glow that illumines and the glare that obscures.

James Thurber

At this point in this book, you may have written a couple simple OpenGL demos to impress your co-workers and family members. But, your app may need that extra little something to stand out from the crowd. This chapter goes over a small selection of more advanced techniques that can give your app an extra oomph.

The selection of effects dealt with in this chapter is by no means comprehensive. I encourage you to check out other graphics books, blogs, and academic papers to learn additional ways of dazzling your users. For example, this book does not cover rendering shadows; there are too many techniques for rendering shadows (or crude approximations thereof), so I can’t cover them while keeping this book concise. But, there’s plenty of information out there, and now that you know the fundamentals, it won’t be difficult to digest it.

This chapter starts off by detailing some of the more obscure texturing functionality in OpenGL ES 1.1. In a way, some of these features—specifically texture combiners, which allow textures to be combined in a variety of ways—are powerful enough to serve as a substitute for very simple fragment shaders.

The chapter goes on to cover normal maps and DOT3 lighting, useful for increasing the amount of perceived detail in your 3D models. (DOT3 simply refers to a three-component dot product; despite appearances, it’s not an acronym.) Next we discuss a technique for creating reflective surfaces that employs a special cube map texture, supported only in ES 2.0. We’ll then briefly cover anisotropic texturing, which improves texturing quality in some cases. Finally, we’ll go over an image-processing technique that adds a soft glow to the scene called bloom. The bloom effect may remind you of a camera technique used in cheesy 1980s soap operas, and I claim no responsibility if it compels you to marry your nephew in order to secure financial assistance for your father’s ex-lover.

Texture Environments under OpenGL ES 1.1

Multitexturing was briefly introduced in the previous chapter (Image Composition and a Taste of Multitexturing), but there’s a lot more to explain. See Figure 8-1 for a high-level overview of the iPhone’s texturing capabilities.

Note

This section doesn’t have much in the way of example code; if you’re not interested in the details of texture combination under OpenGL ES 1.1, skip to the next section (Bump Mapping and DOT3 Lighting).

Texture pipeline for OpenGL ES 1.1

Figure 8-1. Texture pipeline for OpenGL ES 1.1

Here are a few disclaimers regarding Figure 8-1. First, the diagram assumes that both texture stages are enabled; if stage 1 is disabled, the “previous color” gets passed on to become the “final color.” Second, the diagram shows only two texture stages. This is accurate for first- and second-generation devices, but newer devices have eight texture units.

In Figure 8-1, the “primary” color comes from the interpolation of per-vertex colors. Per-vertex colors are produced by lighting or set directly from the application using glColor4f or GL_COLOR_ARRAY.

The two lookup colors are the postfiltered texel colors, sampled from a particular texture image.

Each of the two texture environments is configured to combine its various inputs and produce an output color. The default configuration is modulation, which was briefly mentioned in Chapter 5; this means that the output color results from a per-component multiply of the previous color with the lookup color.

There are a whole slew of ways to configure each texture environment using the glTexEnv function. In my opinion, this is the worst function in OpenGL, and I’m thankful that it doesn’t exist in OpenGL ES 2.0. The expressiveness afforded by GLSL makes glTexEnv unnecessary.

glTexEnv has the following prototypes:

void glTexEnvi(GLenum target, GLenum pname, GLint param);
void glTexEnviv(GLenum target, GLenum pname, const GLint* params);
void glTexEnvf(GLenum target, GLenum pname, GLfloat param);
void glTexEnvfv(GLenum target, GLenum pname, const GLfloat* params);

Note

There are actually a couple more variants for fixed-point math, but I’ve omitted them since there’s never any reason to use fixed-point math on the iPhone. Because of its chip architecture, fixed-point numbers require more processing than floats.

The first parameter, target, is always set to GL_TEXTURE_ENV, unless you’re enabling point sprites as described in Rendering Confetti, Fireworks, and More: Point Sprites. The second parameter, pname, can be any of the following:

GL_TEXTURE_ENV_COLOR

Sets the constant color. As you’ll see later, this used only if the mode is GL_BLEND or GL_COMBINE.

GL_COMBINE_RGB

Sets up a configurable equation for the RGB component of color. Legal values of param are discussed later.

GL_COMBINE_ALPHA

Sets up a configurable equation for the alpha component of color. Legal values of param are discussed later.

GL_RGB_SCALE

Sets optional scale on the RGB components that takes place after all other operations. Scale can be 1, 2, or 4.

GL_ALPHA_SCALE

Sets optional scale on the alpha component that takes place after all other operations. Scale can be 1, 2, or 4.

GL_TEXTURE_ENV_MODE

Sets the mode of the current texture environment; the legal values of param are shown next.

If pname is GL_TEXTURE_ENV_MODE, then param can be any of the following:

GL_REPLACE

Set the output color equal to the lookup color:

OutputColor = LookupColor
GL_MODULATE

This is the default mode; it simply does a per-component multiply of the lookup color with the previous color:

OutputColor = LookupColor * PreviousColor
GL_DECAL

Use the alpha value of the lookup color to overlay it with the previous color. Specifically:

OutputColor = PreviousColor * (1 - LookupAlpha) + LookupColor * LookupAlpha
GL_BLEND

Invert the lookup color, then modulate it with the previous color, and then add the result to a scaled lookup color:

OutputColor = PreviousColor * (1 - LookupColor) + LookupColor * ConstantColor
GL_ADD

Use per-component addition to combine the previous color with the lookup color:

OutputColor = PreviousColor + LookupColor
GL_COMBINE

Generate the RGB outputs in the manner configured by GL_COMBINE_RGB, and generate the alpha output in the manner configured by GL_COMBINE_ALPHA.

The two texture stages need not have the same mode. For example, the following snippet sets the first texture environment to GL_REPLACE and the second environment to GL_MODULATE:

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, myFirstTextureObject);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mySecondTextureObject);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

Texture Combiners

If the mode is set to GL_COMBINE, you can set up two types of combiners: the RGB combiner and the alpha combiner. The former sets up the output color’s RGB components; the latter configures its alpha value.

Each of the two combiners needs to be set up using at least five additional (!) calls to glTexEnv. One call chooses the arithmetic operation (addition, subtraction, and so on), while the other four calls set up the arguments to the operation. For example, here’s how you can set up the RGB combiner of texture stage 0:

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, myTextureObject);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

// Tell OpenGL which arithmetic operation to use: 
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, <operation>);

// Set the first argument:
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, <source0>);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, <operand0>);

// Set the second argument:
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, <source1>);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, <operand1>);

Setting the alpha combiner is done in the same way; just swap the RGB suffix with ALPHA, like this:

// Tell OpenGL which arithmetic operation to use: 
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, <operation>);

// Set the first argument:
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, <source0>);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, <operand0>);

// Set the second argument:
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, <source1>);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, <operand1>);

The following is the list of arithmetic operations you can use for RGB combiners (in other words, the legal values of <operation> when pname is set to GL_COMBINE_RGB):

GL_REPLACE
OutputColor = Arg0
GL_MODULATE
OutputColor = Arg0 * Arg1
GL_ADD
OutputColor = Arg0 + Arg1
GL_ADD_SIGNED
OutputColor = Arg0 + Arg1 - 0.5
GL_INTERPOLATE
OutputColor = Arg0 ∗ Arg2 + Arg1 ∗ (1 − Arg2)
GL_SUBTRACT
OutputColor = Arg0 - Arg1
GL_DOT3_RGB
OutputColor = 4 * Dot(Arg0 - H, Arg1 - H) where H = (0.5, 0.5, 0.5)
GL_DOT3_RGBA
OutputColor = 4 * Dot(Arg0 - H, Arg1 - H) where H = (0.5, 0.5, 0.5)

Note that GL_DOT3_RGB and GL_DOT3_RGBA produce a scalar rather than a vector. With GL_DOT3_RGB, that scalar is duplicated into each of the three RGB channels of the output color, leaving alpha untouched. With GL_DOT3_RGBA, the resulting scalar is written out to all four color components. The dot product combiners may seem rather strange, but you’ll see how they come in handy in the next section.

GL_INTERPOLATE actually has three arguments. As you’d expect, setting up the third argument works the same way as setting up the others; you use GL_SRC2_RGB and GL_OPERAND2_RGB.

For alpha combiners (GL_COMBINE_ALPHA), the list of legal arithmetic operations is the same as RGB combiners, except that the two dot-product operations are not supported.

The <source> arguments in the preceding code snippet can be any of the following:

GL_TEXTURE

Use the lookup color.

GL_CONSTANT

Use the constant color that’s set with GL_TEXTURE_ENV_COLOR.

GL_PRIMARY_COLOR

Use the primary color (this is the color that comes from lighting or glColor4f).

GL_PREVIOUS

Use the output of the previous texture stage. For stage 0, this is equivalent to GL_PRIMARY_COLOR.

For RGB combiners, the <operand> arguments can be any of the following:

GL_SRC_COLOR

Pull the RGB components from the source color.

GL_ONE_MINUS_SRC_COLOR

Use the inverted RGB components from the source color.

GL_SRC_ALPHA

Pull the alpha component from the source color.

GL_ONE_MINUS_SRC_ALPHA

Use the inverted alpha component from the source color.

For alpha combiners, only the last two of the preceding list can be used.

By now, you can see that combiners effectively allow you to set up a set of equations, which is something that’s much easier to express with a shading language!

All the different ways of calling glTexEnv are a bit confusing, so I think it’s best to go over a specific example in detail; we’ll do just that in the next section, with an in-depth explanation of the DOT3 texture combiner and its equivalent in GLSL.

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