Chapter 9. Texture Mapping: Beyond the Basics

by Richard S. Wright, Jr.

WHAT YOU'LL LEARN IN THIS CHAPTER:

How To

Functions You'll Use

Add specular highlights to textured objects

glLightModel, glSecondaryColor

Use anisotropic texture filtering

glTexParameterf

Load and use compressed textures

glCompressedTexImage, glCompressedTexSubImage

Texture mapping is perhaps one of the most exciting features of OpenGL (well, close behind shaders anyway!) and is heavily relied on in the games and simulation industry. In Chapter 8, “Texture Mapping: The Basics,” you learned the basics of loading and applying texture maps to geometry. In this chapter, we expand on this knowledge and cover some of the finer points of texture mapping in OpenGL.

Secondary Color

Applying to geometry, in regards to how lighting works, causes a hidden and often undesirable side effect. In general, you set the texture environment to GL_MODLULATE, causing lit geometry to be combined with the texture map in such a way that the textured geometry also appears lit. Normally, OpenGL performs lighting calculations and calculates the color of individual fragments according to the standard light model. These fragment colors are then combined with the filtered texels being applied to the geometry. However, this process has the side effect of greatly reducing the visibility of specular highlights on the geometry.

For example, Figure 9.1 shows the original lit SPHEREWORLD sample from Chapter 5, “Color, Materials, and Lighting: The Basics.” In this figure, you can see clearly the specular highlights reflecting off the surface of the torus. In contrast, Figure 9.2 shows the SPHEREWORLD sample from the preceding chapter. In this figure, you can see the effects of having the texture applied after the lighting has been added.

Original SPHEREWORLD torus with specular highlights.

Figure 9.1. Original SPHEREWORLD torus with specular highlights.

Textured torus with muted highlights.

Figure 9.2. Textured torus with muted highlights.

The solution to this problem is to apply the specular highlights after texturing. This technique, called the secondary specular color, can be applied manually or automatically calculated by the lighting model. Usually, you do this using the normal OpenGL lighting model and simply turn it on using glLightModel, as shown here:

glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);

You can switch back to the normal lighting model by specifying GL_SINGLE_COLOR for the light model parameter:

glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_COLOR_SINGLE);

Figure 9.3 shows the output from this chapter's version of SPHEREWORLD with the restored specular highlights on the torus. We do not provide a listing for this sample because it simply contains the addition of the preceding single line of code.

Highlights restored to textured torus.

Figure 9.3. Highlights restored to textured torus.

You can also directly specify a secondary color after texturing when you are not using lighting (lighting is disabled) using the glSecondaryColor function. This function comes in many variations just as glColor does and is fully documented in the reference section. You should also note that if you specify a secondary color, you must also explicitly enable the use of the secondary color by enabling the GL_COLOR_SUM flag:

glEnable(GL_COLOR_SUM);

Anisotropic Filtering

Anisotropic texture filtering is not a part of the core OpenGL specification, but it is a widely supported extension that can dramatically improve the quality of texture filtering operations. Texture filtering was covered in the preceding chapter, where you learned about the two basic texture filters: nearest neighbor (GL_NEAREST) and linear (GL_LINEAR). When a texture map is filtered, OpenGL uses the texture coordinates to figure out where in the texture map a particular fragment of geometry falls. The texels immediately around that position are then sampled using either the GL_NEAREST or GL_LINEAR filtering operations.

This process works perfectly when the geometry being textured is viewed directly perpendicular to the viewpoint, as shown to the left in Figure 9.4. However, when the geometry is viewed from an angle more oblique to the point of view, a regular sampling of the surrounding texels results in the loss of some information in the texture (it looks blurry!). A more realistic and accurate sample would be elongated along the direction of the plane containing the texture. This result is shown to the right in Figure 9.4. Taking this viewing angle into account for texture filtering is called anisotropic filtering.

Normal texture sampling versus anisotropic sampling.

Figure 9.4. Normal texture sampling versus anisotropic sampling.

You can apply anisotropic filtering to any of the basic or mipmapped texture filtering modes; applying it requires three steps. First, you must determine whether the extension is supported. You can do this by querying for the extension string GL_EXT_texture_filter_anisotropic. You can use the glTools function named gltIsExtensionSupported for this task:

if(gltIsExtSupported("GL_EXT_texture_filter_anisotropic"))
    // Set Flag that extension is supported

After you determine that this extension is supported, you can find the maximum amount of anisotropy supported. You can query for it using glGetFloatv and the parameter GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:

GLfloat fLargest;
. . .
. . .
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);

The larger the amount of anisotropy applied, the more texels are sampled along the direction of greatest change (along the strongest point of view). A value of 1.0 represents normal texture filtering (called isotropic filtering). Bear in mind that anisotropic filtering is not free. The extra amount of work, including other texels, can sometimes result in substantial performance penalties. On modern hardware, this feature is getting quite fast and is becoming a standard feature of popular games, animation, and simulation programs.

Finally, you set the amount of anisotropy you want applied using glTexParameter and the constant GL_TEXTURE_MAX_ANISOTROPY_EXT. For example, using the preceding code, if you want the maximum amount of anisotropy applied, you would call glTexParameter as follows:

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);

This modifier is applied per texture object just like the standard filtering parameters.

The sample program ANISOTROPIC provides a striking example of anisotropic texture filtering in action. This program displays a tunnel with walls, a floor, and ceiling geometry. The arrow keys move your point of view (or the tunnel) back and forth along the tunnel interior. A right mouse click brings up a menu that allows you to select from the various texture filters, and turn on and off anisotropic filtering. Figure 9.5 shows the tunnel using trilinear filtered mipmapping. Notice how blurred the patterns become in the distance, particularly with the bricks.

ANISOTROPIC tunnel sample with trilinear filtering.

Figure 9.5. ANISOTROPIC tunnel sample with trilinear filtering.

Now compare Figure 9.5 with Figure 9.6, where anisotropic filtering has been enabled. The mortar between the bricks is now clearly visible all the way to the end of the tunnel. In fact, anisotropic filtering can also greatly reduce the visible mipmap transition patterns for the GL_LINEAR_MIPMAP_NEAREST and GL_NEAREST_MIPMAP_NEAREST mipmapped filters.

ANISOTROPIC tunnel sample with anisotropic filtering.

Figure 9.6. ANISOTROPIC tunnel sample with anisotropic filtering.

Texture Compression

Texture mapping can add incredible realism to any 3D rendered scene, with a minimal cost in vertex processing. One drawback to using textures, however, is that they require a lot of memory to store and process. Early attempts at texture compression were crudely storing textures as JPG files and decompressing the textures when loaded before calling glTexImage. These attempts saved disk space or reduced the amount of time required to transmit the image over the network (such as the Internet), but did little to alleviate the storage requirements of texture images loaded into graphics hardware memory.

Native support for texture compression was added to OpenGL with version 1.3. Earlier versions of OpenGL may also support texture compression via extension functions of the same name. You can test for this extension by using the GL_ARB_texture_compression string.

Texture compression support in OpenGL hardware can go beyond simply allowing you to load a compressed texture; in most implementations, the texture data stays compressed even in the graphics hardware memory. This allows you to load more texture into less memory and can significantly improve texturing performance due to fewer texture swaps (moving textures around) and fewer memory accesses during texture filtering.

Compressing Textures

Texture data does not have to be initially compressed to take advantage of OpenGL support for compressed textures. You can request that OpenGL compress a texture image when loaded by using one of the values in Table 9.1 for the internalFormat parameter of any of the glTexImage functions.

Table 9.1. Compressed Texture Formats

Compressed Format

Base Internal Format

GL_COMPRESSED_ALPHA

GL_ALPHA

GL_COMPRESSED_LUMINANCE

GL_LUMINANCE

GL_COMPRESSED_LUMINANCE_ALPHA

GL_LUMINANCE_ALPHA

GL_COMPRESSED_INTENSITY

GL_INTENSITY

GL_COMPRESSED_RGB

GL_RGB

GL_COMPRESSED_RGBA

GL_RGBA

Compressing images this way adds a bit of overhead to texture loads but can increase texture performance due to the more efficient usage of texture memory. If, for some reason, the texture cannot be compressed, OpenGL uses the base internal format listed instead and loads the texture uncompressed.

When you attempt to load and compress a texture in this way, you can find out whether the texture was successfully compressed by using glGetTexLevelParameteriv with GL_TEXTURE_COMPRESSED as the parameter name:

GLint compFlag;
. . .
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &compFlag);

The glGetTexLevelParameteriv function accepts a number of new parameter names pertaining to compressed textures. These parameters are listed in Table 9.2.

Table 9.2. Compressed Texture Parameters Retrieved with glGetTexLevelParameter

Parameter

Returns

GL_TEXTURE_COMPRESSED

The value 1 if the texture is compressed, 0 if not

GL_TEXTURE_COMPRESSED_IMAGE_SIZE

The size in bytes of the compressed texture

GL_TEXTURE_INTERNAL_FORMAT

The compression format used

GL_NUM_COMPRESSED_TEXTURE_FORMATS

The number of supported compressed texture formats

GL_COMPRESSED_TEXTURE_FORMATS

An array of constant values corresponding to each supported compressed texture format

GL_TEXTURE_COMPRESSION_HINT

The value of the texture compression hint (GL_NICEST/GL_FASTEST)

When textures are compressed using the values listed in Table 9.1, OpenGL chooses the most appropriate texture compression format. You can use glHint to specify whether you want OpenGL to choose based on the fastest or highest quality algorithm:

glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);

The exact compression format varies from implementation to implementation. You can obtain a count of compression formats and a list of the values by using GL_NUM_COMPRESSED_TEXTURE_FORMATS and GL_COMPRESSED_TEXTURE_FORMATS. To check for support for a specific set of compressed texture formats, you need to check for a specific extension for those formats. For example, one of the most popular (on both PC and Mac) is the GL_EXT_texture_compression_s3tc texture compression format. If this extension is supported, the compressed texture formats listed in Table 9.3 are all supported (these constants are defined in glext.h), but only for two-dimensional textures.

Table 9.3. Compression Formats for GL_EXT_texture_compression_s3tc

Format

Description

GL_COMPRESSED_RGB_S3TC_DXT1

RGB data is compressed; alpha is always 1.0.

GL_COMPRESSED_RGBA_S3TC_DXT1

RGB data is compressed; alpha is either 1.0 or 0.0.

GL_COMPRESSED_RGBA_S3TC_DXT3

RGB data is compressed; alpha is stored as 4 bits.

GL_COMPRESSED_RGBA_S3TC_DXT5

RGB data is compressed; alpha is a weighted average of 8-bit values.

Loading Compressed Textures

Using the functions in the preceding section, you can have OpenGL compress textures in a natively supported format, retrieve the compressed data with the glGetCompressedTexImage function (identical to the glGetTexImage function in the preceding chapter), and save it to disk. On subsequent loads, the raw compressed data can be used, resulting in substantially faster texture loads.

To load precompressed texture data, use one of the following functions:

void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalFormat,
                        GLsizei width,
                        GLint border, GLsizei imageSize, void *data);
void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
                        GLsizei width, GLsizei height,
                        GLint border, GLsizei imageSize, void *data);
void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
                        GLsizei width, GLsizei height, GLsizei depth,
                        GLint border, Glsizei imageSize, GLvoid *data);

These functions are virtually identical to the glTexImage functions from the preceding chapter. The only difference is that the internalFormat parameter must specify a supported compressed texture image. If the implementation supports the GL_EXT_texture_compression_s3tc extension, this would be one of the values from Table 9.3. There is also a corresponding set of glCompressedTexSubImage functions for updating a portion or all of an already-loaded texture that mirrors the glTexSubImage functionality from the preceding chapter.

Texture Coordinate Generation

In Chapter 8, you learned that textures are mapped to geometry using texture coordinates. Often, when you are loading models (see Chapter 11, “It's All About the Pipeline: Faster Geometry Throughput”), texture coordinates are provided for you. If necessary, you can easily map texture coordinates manually to some surfaces such as spheres or flat planes. Sometimes, however, you may have a complex surface for which it is not so easy to manually derive the coordinates. OpenGL can automatically generate texture coordinates for you within certain limitations.

Texture coordinate generation is enabled on the S, T, R, and Q texture coordinates using glEnable:

glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);

When texture coordinate generation is enabled, any calls to glTexCoord are ignored, and OpenGL calculates the texture coordinates for each vertex for you. In the same manner that texture coordinate generation is turned on, you turn it off by using glDisable:

glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);

You set the function or method used to generate texture coordinates with the following functions:

void glTexGenf(GLenum coord, GLenum pname, GLfloat param);
void glTexGenfv(GLenum coord, GLenum pname, GLfloat *param);

The first parameter, coord, specifies which texture coordinate this function sets. It must be either GL_S, GL_T, GL_R, or GL_Q. The second parameter, pname, must be either GL_TEXTURE_GEN_MODE, GL_OBJECT_PLANE, or GL_EYE_PLANE. The last parameter sets the values of the texture generation function or mode. Note that integer (GLint) and double (GLdouble) versions of these functions are also used.

The sample program TEXGEN is presented in Listing 9.1. This program displays a torus that can be manipulated (rotated around) using the arrow keys. A right-click brings up a context menu that allows you to select from the first three texture generation modes we will discuss: Object Linear, Eye Linear, and Sphere Mapping.

Example 9.1. Source Code for the TEXGEN Sample Program

#include "../../Common/OpenGLSB.h"  // System and OpenGL Stuff
#include "../../Common/gltools.h"   // gltools library

// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;

GLuint toTextures[2];       // Two texture objects
int iRenderMode = 3;        // Sphere Mapped is default

///////////////////////////////////////////////////////////////////////////////
// Reset flags as appropriate in response to menu selections
void ProcessMenu(int value)
    {
    // Projection plane
    GLfloat zPlane[] = { 0.0f, 0.0f, 1.0f, 0.0f };

    // Store render mode
    iRenderMode = value;

    // Set up textgen based on menu selection
    switch(value)
        {
        case 1:
            // Object Linear
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
            glTexGenfv(GL_S, GL_OBJECT_PLANE, zPlane);
            glTexGenfv(GL_T, GL_OBJECT_PLANE, zPlane);
            break;

        case 2:
            // Eye Linear
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGenfv(GL_S, GL_EYE_PLANE, zPlane);
            glTexGenfv(GL_T, GL_EYE_PLANE, zPlane);
            break;

        case 3:
        default:
            // Sphere Map
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
            break;
        }

    glutPostRedisplay();    // Redisplay
    }



///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
    {
    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Switch to orthographic view for background drawing
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0.0f, 1.0f, 0.0f, 1.0f);

    glMatrixMode(GL_MODELVIEW);
    glBindTexture(GL_TEXTURE_2D, toTextures[1]);    // Background texture

    // We will specify texture coordinates
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);

    // No depth buffer writes for background
    glDepthMask(GL_FALSE);

    // Background image
    glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 0.0f);
        glVertex2f(0.0f, 0.0f);

        glTexCoord2f(1.0f, 0.0f);
        glVertex2f(1.0f, 0.0f);

        glTexCoord2f(1.0f, 1.0f);
        glVertex2f(1.0f, 1.0f);

        glTexCoord2f(0.0f, 1.0f);
        glVertex2f(0.0f, 1.0f);
    glEnd();

    // Back to 3D land
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

    // Turn texgen and depth writing back on
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glDepthMask(GL_TRUE);

    // May need to switch to stripe texture
    if(iRenderMode != 3)
        glBindTexture(GL_TEXTURE_2D, toTextures[0]);

    // Save the matrix state and do the rotations
    glPushMatrix();
    glTranslatef(0.0f, 0.0f, -2.0f);
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);

    // Draw the tours
    gltDrawTorus(0.35, 0.15, 61, 37);

    // Restore the matrix state
    glPopMatrix();

    // Display the results
    glutSwapBuffers();
    }

///////////////////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context.
void SetupRC()
    {
    GLbyte *pBytes;                     // Texture bytes
    GLint iComponents, iWidth, iHeight; // Texture sizes
    GLenum eFormat;                     // Texture format

    glEnable(GL_DEPTH_TEST);    // Hidden surface removal
    glFrontFace(GL_CCW);        // Counterclockwise polygons face out
    glEnable(GL_CULL_FACE);     // Do not calculate inside of jet

    // White background
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f );

    // Decal texture environment
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    // Two textures
    glGenTextures(2, toTextures);

    ///////////////////////////////////////////
    // Load the main texture
    glBindTexture(GL_TEXTURE_2D, toTextures[0]);
    pBytes = gltLoadTGA("stripes.tga", &iWidth, &iHeight,
                                                &iComponents, &eFormat);
    glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0,
                              eFormat, GL_UNSIGNED_BYTE, (void *)pBytes);
    free(pBytes);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glEnable(GL_TEXTURE_2D);

    ///////////////////////////////////////////
    // Load environment map
    glBindTexture(GL_TEXTURE_2D, toTextures[1]);
    pBytes = gltLoadTGA("Environment.tga", &iWidth, &iHeight,
                                                 &iComponents, &eFormat);
    glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0,
                              eFormat, GL_UNSIGNED_BYTE, (void *)pBytes);
    free(pBytes);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glEnable(GL_TEXTURE_2D);

    // Turn on texture coordinate generation
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);

    // Sphere Map will be the default
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    }

/////////////////////////////////////////////////////
// Handle arrow keys
void SpecialKeys(int key, int x, int y)
    {
    if(key == GLUT_KEY_UP)
        xRot-= 5.0f;

    if(key == GLUT_KEY_DOWN)
        xRot += 5.0f;

    if(key == GLUT_KEY_LEFT)
        yRot -= 5.0f;

    if(key == GLUT_KEY_RIGHT)
        yRot += 5.0f;

    if(key > 356.0f)
        xRot = 0.0f;

    if(key < -1.0f)
        xRot = 355.0f;

    if(key > 356.0f)
        yRot = 0.0f;

    if(key < -1.0f)
        yRot = 355.0f;

    // Refresh the Window
    glutPostRedisplay();
    }


//////////////////////////////////////////////////////////
// Reset projection and light position
void ChangeSize(int w, int h)
    {
    GLfloat fAspect;

    // Prevent a divide by zero
    if(h == 0)
        h = 1;

    // Set Viewport to window dimensions
    glViewport(0, 0, w, h);

    // Reset coordinate system
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    fAspect = (GLfloat) w / (GLfloat) h;
    gluPerspective(45.0f, fAspect, 1.0f, 225.0f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    }

//////////////////////////////////////////////////////////////////////////////
// Program Entry Point
int main(int argc, char* argv[])
    {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800,600);
    glutCreateWindow("Texture Coordinate Generation");
    glutReshapeFunc(ChangeSize);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);
    SetupRC();

    // Create the Menu
    glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("Object Linear",1);
    glutAddMenuEntry("Eye Linear",2);
    glutAddMenuEntry("Sphere Map",3);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    glutMainLoop();

    // Don't forget the texture objects
    glDeleteTextures(2, toTextures);

    return 0;
    }

Object Linear Mapping

When the texture generation mode is set to GL_OBJECT_LINEAR, texture coordinates are generated using the following function:

coord = P1*X + P2*Y + P3*Z + P4*W

The X, Y, Z, and W values are the vertex coordinates from the object being textured, and the P1–P4 values are the coefficients for a plane equation. The texture coordinates are then projected onto the geometry from the perspective of this plane. For example, to project texture coordinates for S and T from the plane Z = 0, we would use the following code from the TEXGEN sample program:

// Projection plane
GLfloat zPlane[] = { 0.0f, 0.0f, 1.0f, 0.0f };
. . .
. . .
// Object Linear
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, zPlane);
glTexGenfv(GL_T, GL_OBJECT_PLANE, zPlane);

Note that the texture coordinate generation function can be different for each coordinate. Here, we simply use the same function for both the S and T coordinates.

This technique maps the texture to the object in object coordinates, regardless of any ModelView transformation in effect. Figure 9.7 shows the output for TEXGEN when the Object Linear mode is selected. No matter how you reorient the torus, the mapping remains fixed to the geometry.

Torus mapped with object linear coordinates.

Figure 9.7. Torus mapped with object linear coordinates.

Eye Linear Mapping

When the texture generation mode is set to GL_EYE_LINEAR, texture coordinates are generated in a similar manner to GL_OBJECT_LINEAR. The coordinate generation looks the same, except that now the X, Y, Z, and W coordinates indicate the location of the point of view (where the camera or eye is located). The plane equation coefficients are also inverted before being applied to the equation.

The texture, therefore, is basically projected from the plane onto the geometry. As the geometry is transformed by the ModelView matrix, the texture will appear to slide across the surface. We set up this capability with the following code from the TEXGEN sample program:

// Projection plane
GLfloat zPlane[] = { 0.0f, 0.0f, 1.0f, 0.0f };
. . .
. . .
// Eye Linear
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, zPlane);
glTexGenfv(GL_T, GL_EYE_PLANE, zPlane);

The output of the TEXGEN program when the Eye Linear menu option is selected is shown in Figure 9.8. As you move the torus around with the arrow keys, note how the projected texture slides about on the geometry.

An example of eye linear texture mapping.

Figure 9.8. An example of eye linear texture mapping.

Sphere Mapping

When the texture generation mode is set to GL_SPHERE_MAP, OpenGL calculates texture coordinates in such a way that the object appears to be reflecting the current texture map. This is the easiest mode to set up, with just these two lines from the TEXGEN sample program:

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

You usually can make a well-constructed texture by taking a photograph through a fish-eye lens. This texture then lends a convincing reflective quality to the geometry. For more realistic results, sphere mapping has largely been replaced by cube mapping (discussed next). However, sphere mapping still has some uses.

In particular, sphere mapping requires only a single texture instead of six, and if true reflectivity is not required, you can obtain adequate results from sphere mapping. Even without a well-formed texture taken through a fish-eye lens, you can also use sphere mapping for an approximate environment map. Many surfaces are shiny and reflect the light from their surroundings, but are not mirror-like in their reflective qualities. In the TEXGEN sample program, we use a suitable environment map for the background (all modes show this background), as well as the source for the sphere map. Figure 9.9 shows the environment-mapped torus against a similarly colored background. Moving the torus around with the arrow keys produces a reasonable approximation of a reflective surface.

An environment map using sphere map.

Figure 9.9. An environment map using sphere map.

Cube Mapping

The last two texture generation modes, GL_REFLECTION_MAP and GL_NORMAL_MAP, require the use of a new type of texture environment: the cube map. A cube map is not a single texture, but rather a set of six textures that make up the six sides of a cube. Figure 9.10 shows the layout of six square textures comprising a cube map for the CUBEMAP sample program.

Cube map for SphereWorld in the CUBEMAP sample program.

Figure 9.10. Cube map for SphereWorld in the CUBEMAP sample program.

These six tiles represent the view of SphereWorld from six different directions (forward, backward, left, right, up, and down). Using the texture generation mode GL_REFLECTION_MAP, you can then create a truly accurate reflective surface.

Loading Cube Maps

Cube maps add six new values that can be passed into glTexImage2D: GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, and GL_TEXTURE_CUBE_MAP_NEGATIVE_Z. These constants represent the direction in world coordinates of the cube face surrounding the object being mapped. For example, to load the map for the positive X direction, you might use a function that looks like this:

glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, iWidth, iHeight,
                                        0, GL_RGBA, GL_UNSIGNED_BYTE, pImage);

To take this example further, look at the following code segment from the CUBEMAP sample program. Here, we store the name and identifiers of the six cube maps in arrays and then use a loop to load all six images into a single texture object:

const char *szCubeFaces[6] = { "right.tga", "left.tga", "up.tga", "down.tga",
                                              "backward.tga", "forward.tga" };

GLenum  cube[6] = {  GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                     GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                     GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                     GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                     GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                     GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };

. . .
. . .
glBindTexture(GL_TEXTURE_CUBE_MAP, textureObjects[CUBE_MAP]);

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_REPEAT);

    // Load Cube Map images
    for(i = 0; i < 6; i++)
        {
        GLubyte *pBytes;
        GLint iWidth, iHeight, iComponents;
        GLenum eFormat;

        // Load this texture map
        // glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE);
        pBytes = gltLoadTGA(szCubeFaces[i], &iWidth, &iHeight,
                                                      &iComponents, &eFormat);
        glTexImage2D(cube[i], 0, iComponents, iWidth, iHeight, 0, eFormat,
                                                    GL_UNSIGNED_BYTE, pBytes);
        free(pBytes);
        }

Notice how the first parameter to glBindTexture is now GL_TEXTURE_CUBE_MAP instead of GL_TEXTURE_2D. The same value is also used in glEnable to enable cube mapping:

glEnable(GL_TEXTURE_CUBE_MAP);

If both GL_TEXTURE_CUBE_MAP and GL_TEXTURE_2D are enabled, GL_TEXTURE_CUBE_MAP has precedence. Also, notice that the texture parameter values (set with glTexParameter) affect all six images in a single cube texture. For all intents and purposes, cube maps are treated like a single 3D texture map, using S, T, and R coordinates to interpolate texture coordinate values.

Using Cube Maps

The most common use of cube maps is to create an object that reflects its surroundings. The six images used for the CUBEMAP sample program were made from one of the SphereWorld samples with the torus and revolving sphere removed. The point of view was changed six times and captured (using a 90-degree field of view). These views were then assembled into a single cube map using the code from the preceding section, and appear something like that shown in Figure 9.9.

The CUBEMAP sample sets the texture generation mode to GL_REFLECTION_MAP for all three texture coordinates:

glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);

Then, when the torus is drawn, 2D texturing is disabled and cube mapping is enabled. Texture coordinate generation is enabled and the torus is drawn. Afterward, the normal texturing state is restored:

glDisable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureObjects[CUBE_MAP]);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
gltDrawTorus(0.35, 0.15, 61, 37);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_CUBE_MAP);
glEnable(GL_TEXTURE_2D);

Figure 9.11 shows the output of the CUBEMAP sample program. Notice how the ground is reflected correctly off the bottom surfaces of the torus, with the gray sky reflected on the upper surfaces. In the same manner, you can see the spheres scattered throughout SphereWorld reflected in the sides of the torus.

Output from the CUBEMAP sample program.

Figure 9.11. Output from the CUBEMAP sample program.

Multitexture

Most modern OpenGL hardware implementations support the ability to apply two or more textures to geometry simultaneously. All OpenGL implementations support at least a single texture being applied to geometry. If an implementation supports more than one texture unit, you can query with GL_MAX_TEXTURE_UNITS to see how many texture units are available:

GLint iUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &iUnits);

Textures are applied from the base texture unit (GL_TEXTURE0), up to the maximum number of texture units in use (GL_TEXTUREn, where n is the number of texture units in use). Each texture unit has its own texture environment that determines how fragments are combined with the previous texture unit. Figure 9.12 shows three textures being applied to geometry, each with its own texture environment.

Multitexture order of operations.

Figure 9.12. Multitexture order of operations.

By default, the first texture unit is the active texture unit. All texture commands, with the exception of glTexCoord, affect the currently active texture unit. You can change the current texture unit by calling glActiveTexture with the texture unit identifier as the argument. For example, to switch to the second texture unit and enable 2D texturing on that unit, you would call the following:

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);

To disable texturing on the second texture unit and switch back to the first (base) texture unit, you would make these calls:

glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);

All calls to texture functions such as glTexParameter, glTexEnv, glTexGen, glTexImage, and glBindTexture are bound only to the current texture unit. When geometry is rendered, texture is applied from all enabled texture units using the texture environment and parameters previously specified. The only exception is texture coordinates.

Multiple Texture Coordinates

Occasionally, you might apply all active textures using the same texture coordinates for each texture, but this is rarely the case. When using multiple textures, you can still specify texture coordinates with glTexCoord; however, these texture coordinates are used only for the first texture unit (GL_TEXTURE0). To specify texture coordinates separately for each texture unit, you need a new texture coordinate function:

glMultiTexCoord2f(GLenum texUnit, GLfloat s, GLfloat t);

The texUnit parameter is GL_TEXTURE0, GL_TEXTURE1, and so on up to the maximum number of supported texturing units. In this version of glMultiTexCoord, you specify the s and t coordinates of a two-dimensional texture. Just like glTexCoord, many variations of glMultiTexCoord enable you to specify one-, two-, three-, and four-dimensional texture coordinates in a number of different data formats. You can also use texture coordinate generation on one or more texture units.

A Multitextured Example

Listing 9.2 presents the code for the sample program MULTITEXTURE. This program is similar to the CUBEMAP program, and only the changed functions are listed here. In the CUBEMAP program, we removed the wood texture from the torus and replaced it with a cube map, giving the appearance of a perfectly reflective surface. In MULTITEXTURE, we have put the wood texture back on the torus but moved the cube map to the second texture unit. We use the GL_MODULATE texture environment to blend the two textures together. We have also lightened the textures for the cube map to make the surface brighter and the wood easier to see. Figure 9.13 shows the output from the MULTITEXTURE program.

Example 9.2. Source Code for the MULTITEXTURE Sample Program

#include "../../Common/OpenGLSB.h"   // System and OpenGL Stuff
#include "../../Common/GLTools.h"    // OpenGL toolkit
#include <math.h>

#define NUM_SPHERES      30
GLTFrame    spheres[NUM_SPHERES];
GLTFrame    frameCamera;

// Light and material Data
GLfloat fLightPos[4]   = { -100.0f, 100.0f, 50.0f, 1.0f };  // Point source
GLfloat fNoLight[] = { 0.0f, 0.0f, 0.0f, 0.0f };
GLfloat fLowLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat fBrightLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };

#define GROUND_TEXTURE  0
#define SPHERE_TEXTURE  1
#define WOOD_TEXTURE    2
#define CUBE_MAP        3
#define NUM_TEXTURES    4
GLuint  textureObjects[NUM_TEXTURES];

const char *szTextureFiles[] = {"grass.tga", "orb.tga", "wood.tga"};

const char *szCubeFaces[6] = { "right.tga", "left.tga", "up.tga",
                         "down.tga", "backward.tga", "forward.tga" };

GLenum  cube[6] = {  GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                     GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
                     GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
                     GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
                     GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
                     GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };


//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context.
void SetupRC()
    {
    int iSphere;
    int i;

    // Grayish background
    glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);

    // Cull backs of polygons
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

    // Setup light Parameters:
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
    glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
    glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, fBrightLight);
    glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    // Mostly use material tracking
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glMateriali(GL_FRONT, GL_SHININESS, 128);

    gltInitFrame(&frameCamera);  // Initialize the camera

    // Randomly place the sphere inhabitants
    for(iSphere = 0; iSphere < NUM_SPHERES; iSphere++)
        {
        gltInitFrame(&spheres[iSphere]);    // Initialize the frame

        // Pick a random location between -20 and 20 at .1 increments
        spheres[iSphere].vLocation[0] = (float)((rand() % 400) - 200) * 0.1f;
        spheres[iSphere].vLocation[1] = 0.0f;
        spheres[iSphere].vLocation[2] = (float)((rand() % 400) - 200) * 0.1f;
        }

    // Set up texture maps
    glEnable(GL_TEXTURE_2D);
    glGenTextures(NUM_TEXTURES, textureObjects);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // Load regular textures
    for(i = 0; i < CUBE_MAP; i++)
        {
        GLubyte *pBytes;
        GLint iWidth, iHeight, iComponents;
        GLenum eFormat;

        glBindTexture(GL_TEXTURE_2D, textureObjects[i]);

        // Load this texture map
        glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
        pBytes = gltLoadTGA(szTextureFiles[i], &iWidth, &iHeight,
                                                   &iComponents, &eFormat);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, iWidth, iHeight,
                                     0, eFormat, GL_UNSIGNED_BYTE, pBytes);
        free(pBytes);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                                                   GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        }


    glActiveTexture(GL_TEXTURE1);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_TEXTURE_CUBE_MAP);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureObjects[CUBE_MAP]);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glEnable(GL_TEXTURE_GEN_R);

    glBindTexture(GL_TEXTURE_CUBE_MAP, textureObjects[CUBE_MAP]);

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_REPEAT);


    // Load Cube Map images
    for(i = 0; i < 6; i++)
        {
        GLubyte *pBytes;
        GLint iWidth, iHeight, iComponents;
        GLenum eFormat;

        // Load this texture map
       // glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE);
        pBytes = gltLoadTGA(szCubeFaces[i], &iWidth, &iHeight,
                                                     &iComponents, &eFormat);
        glTexImage2D(cube[i], 0, iComponents, iWidth, iHeight, 0,
                                          eFormat, GL_UNSIGNED_BYTE, pBytes);
        free(pBytes);
        }
    }


///////////////////////////////////////////////////////////////////////
// Draw random inhabitants and the rotating torus/sphere duo
void DrawInhabitants(void)
    {
    static GLfloat yRot = 0.0f;         // Rotation angle for animation
    GLint i;

    yRot += 0.5f;
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

    // Draw the randomly located spheres
    glBindTexture(GL_TEXTURE_2D, textureObjects[SPHERE_TEXTURE]);
    for(i = 0; i < NUM_SPHERES; i++)
        {
        glPushMatrix();
        gltApplyActorTransform(&spheres[i]);
        gltDrawSphere(0.3f, 21, 11);
        glPopMatrix();
        }

    glPushMatrix();
        glTranslatef(0.0f, 0.1f, -2.5f);

        // Torus alone will be specular
        glMaterialfv(GL_FRONT, GL_SPECULAR, fBrightLight);
        glRotatef(yRot, 0.0f, 1.0f, 0.0f);

        // Bind to Wood, first texture
        glBindTexture(GL_TEXTURE_2D, textureObjects[WOOD_TEXTURE]);

        glActiveTexture(GL_TEXTURE1);
        glEnable(GL_TEXTURE_CUBE_MAP);

        glActiveTexture(GL_TEXTURE0);
        gltDrawTorus(0.35, 0.15, 41, 17);

        glActiveTexture(GL_TEXTURE1);
        glDisable(GL_TEXTURE_CUBE_MAP);
        glActiveTexture(GL_TEXTURE0);

        glMaterialfv(GL_FRONT, GL_SPECULAR, fNoLight);
    glPopMatrix();
    }
Output from the MULTITEXTURE sample program.

Figure 9.13. Output from the MULTITEXTURE sample program.

Texture Combiners

In Chapter 6, “More on Colors and Materials,” you learned how to use the blending equation to control the way color fragments were blended together when multiple layers of geometry were drawn in the color buffer (typically back to front). OpenGL's texture combiners allow the same sort of control (only better) for the way multiple texture fragments are combined. By default, you can simply choose one of the texture environment modes (GL_DECAL, GL_REPLACE, GL_MODULATE, or GL_ADD) for each texture unit, and the results of each texture application are then added to the next texture unit. These texture environments were covered in Chapter 8.

Texture combiners add a new texture environment, GL_COMBINE, that allows you to explicitly set the way texture fragments from each texture unit are combined. To use texture combiners, you call glTexEnv in the following manner:

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

Texture combiners are controlled entirely through the glTexEnv function. Next, you need to select which texture combiner function you want to use. The combiner function selector, which can be either GL_COMBINE_RGB or GL_COMBINE_ALPHA, becomes the second argument to the glTexEnv function. The third argument becomes the texture environment function that you want to employ (for either RGB or alpha values). These functions are listed in Table 9.4. For example, to select the GL_REPLACE combiner for RGB values, you would call the following function:

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);

This combiner does little more than duplicate the normal GL_REPLACE texture environment.

Table 9.4. Texture Combiner Functions

Constant

Function

GL_REPLACE

Arg0

GL_MODULATE

Arg0 * Arg1

GL_ADD

Arg0 + Arg1

GL_ADD_SIGNED

Arg0 + Arg1 – 0.5

GL_INTERPOLATE

(Arg0 * Arg2) + (Arg1 * (1 – Arg2))

GL_SUBTRACT

Arg0 – Arg1

GL_DOT3_RGB/GL_DOT3_RGBA

4*((Arg0r – 0.5) * (Arg1r – 0.5) + (Arg0g – 0.5) * (Arg1g – 0.5) + (Arg0b – 0.5) * (Arg1b – 0.5))

The values of Arg0 – Arg2 are from source and operand values set with more calls to glTexEnv. The values GL_SOURCEx_RGB and GL_SOURCEx_ALPHA are used to specify the RGB or alpha combiner function arguments, where x is 0, 1, or 2. The values for these sources are given in Table 9.5.

Table 9.5. Texture Combiner Sources

Constant

Description

GL_TEXTURE

The texture bound to the current active texture unit

GL_TEXTUREx

The texture bound to texture unit x

GL_CONSTANT

The color (or alpha) value set by the texture environment variable GL_TEXTURE_ENV_COLOR

GL_PRIMARY_COLOR

The color (or alpha) value coming from the original geometry fragment

GL_PREVIOUS

The color (or alpha) value resulting from the previous texture unit's texture environment

For example, to select the texture from texture unit 0 for Arg0, you would make the following function call:

glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);

You also have some additional control over what values are used from a given source for each argument. To set these operands, you use the constant GL_OPERANDx_RGB or GL_OPERANDx_ALPHA, where x is 0, 1, or 2. The valid operands and their meanings are given in Table 9.6.

Table 9.6. Texture Combiner Operands

Constant

Meaning

GL_SRC_COLOR

The color values from the source. This may not be used with GL_OPERANDx_ALPHA.

GL_ONE_MINUS_SRC_COLOR

One's complement (1-value) of the color values from the source. This may not be used with GL_OPERANDx_ALPHA.

GL_SRC_ALPHA

The alpha values of the source.

GL_ONE_MINUS_SRC_ALPHA

One's complement (1-value) of the alpha values from the source.

For example, if you have two textures loaded on the first two texture units, and you want to multiply the color values from both textures during the texture application, you would set it up as follows:

// Tell OpenGL you want to use texture combiners
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

// Tell OpenGL which combiner you want to use (GL_MODULATE for RGB values)
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

// Tell OpenGL to use texture unit 0's color values for Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

// Tell OpenGL to use texture unit 1's color values for Arg1
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE1);
glTexenvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

Finally, with texture combiners, you can also specify a constant RGB or alpha scaling factor. The default parameters for these are as follows:

glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 1.0f);
glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1.0f);

Summary

In this chapter, we took texture mapping beyond the simple basics of applying a texture to geometry. You saw how to get improved filtering, obtain better performance and memory efficiency through texture compression, and generate automatic texture coordinates for geometry. You also saw how to add plausible environment maps with sphere mapping and more realistic and correct reflections using cube maps.

Finally, we discussed multitexture and texture combiners. The ability to apply more than one texture at a time is the foundation for many special effects, including hardware support for bump mapping. Using texture combiners, you have a great deal of flexibility in specifying how up to three textures are combined. While fragment programs exposed through the new OpenGL shading language do give you ultimate control over texture application, you can quickly and easily take advantage of the capabilities described in this chapter.

Reference

glActiveTexture

Purpose:

Sets the active texture unit.

Include File:

<gl.h>

Syntax:

void glActiveTexture(GLenum texUnit);

Description:

This function sets the active texture unit. The active texture unit is the target of all texture functions with the exception of glTexCoord and glTexCoordPointer.

Parameters:

texUnit

GLenumThe texture unit to be made current. It may be GL_TEXTURE0 through GL_TEXTUREn, where n is the number of supported texture units.

Returns:

None.

See Also:

glClientActiveTexture

glClientActiveTexture

Purpose:

Sets the active texture unit for vertex arrays.

Include File:

<gl.h>

Syntax:

void glClientActiveTexture(GLenum texUnit);

Description:

This function sets the active texture unit for vertex array texture coordinate specification. The glTexCoordPointer vertex array function (see Chapter 11), by default, specifies a pointer for the first texture unit (GL_TEXTURE0). You use this function to change the texture unit that is the target of glTexCoordPointer. Calling this function multiple times allows you to specify multiple arrays of texture coordinates for multitextured vertex array operations.

Parameters:

texUnit

GLenummThe texture unit to be made current. It may be GL_TEXTURE0 through GL_TEXTUREn, where n is the number of supported texture units.

Returns:

None.

See Also:

glActiveTexture

glCompressedTexImage

Purpose:

Loads a compressed texture image.

Include File:

<gl.h>

Variations:

void glCompressedTexImage1D(GLenum target, GLint 
GLenumm:level, GLenum internalFormat,
                        GLsizei width,
                        GLint border, GLsizei 
GLenumm:imageSize, void *data);
void glCompressedTexImage2D(GLenum target, GLint 
GLenumm:level, GLenum internalFormat,
                        GLsizei width, GLsizei height,
                        GLint border, GLsizei 
GLenumm:imageSize, void *data);
void glCompressedTexImage3D(GLenum target, GLint 
GLenumm:level, GLenum internalFormat,
                        GLsizei width, GLsizei 
GLenumm:height, GLsizei depth,
                        GLint border, Glsizei 
GLenumm:imageSize, GLvoid *data);

Description:

This function enables you to load a compressed texture image. This function is nearly identical in use to the glTexImage family of functions, with the exception that the internalFormat parameter must specify a supported compressed texture format.

Parameters:

target

GLenumIt must be GL_TEXTURE_1D for glCompressedTexImage1D, GL_TEXTURE_2D for glCompressedTexImage2D, or GL_TEXTURE_3D for glCompressedTexImage3D. For 2D cube maps only, it may also be GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_CUBE_MAP_POSITIVE_Y, GL_CUBE_MAP_NEGATIVE_Y, GL_CUBE_MAP_POSITIVE_Z, or GL_CUBE_MAP_NEGATIVE_Z.

level

GLintThe level of detail. It is usually 0 unless mipmapping is used.

internalFormat

GLintThe internal format of the compressed data. Specific values for texture compression formats will vary from implementation to implementation. Table 9.3 lists the formats for one commonly supported compression format on the PC and Macintosh.

width

GLsizeiThe width of the one-, two-, or three-dimensional texture image. It must be a power of 2 but may include a border.

height

GLsizeiThe height of the two- or three-dimensional texture image. It must be a power of 2 but may include a border.

depth

GLsizeiThe depth of a three-dimensional texture image. It must be a power of 2 but may include a border.

border

GLintThe width of the border. Not all texture compression formats support borders.

imageSize

GLsizeiThe size in bytes of the compressed data.

data

GLvoid *The compressed texture data.

Returns:

None.

See Also:

glCompressedTexSubImage, glTexImage

glCompressedTexSubImage

Purpose:

Replaces a portion of an existing compressed texture map.

Include File:

<gl.h>

Variations:

void glCompressedTexSubImage1D(GLenum target, 
GLvoid *:GLint level,
                                 GLint xOffset,
                                 GLsizei width,
                                 GLsizei imageSize
GLvoid *:, const void *data);
void glCompressedTexSubImage2D(GLenum target, 
GLvoid *:GLint level,
                                 GLint xOffset, 
GLvoid *:GLint yOffset,
                                 GLsizei width, 
GLvoid *:GLsizei height,
                                 GLsizei imageSize
GLvoid *:, const void *data);
void glCompressedTexSubImage3D(GLenum target, 
GLvoid *:GLint level,
                                 GLint xOffset, 
GLvoid *:GLint yOffset, GLint zOffset,
                                 GLsizei width, 
GLvoid *:GLsizei height, GLsizei depth,
                                 GLsizei imageSize
GLvoid *:, const void *data);

Description:

This function replaces a portion of an existing one-, two-, or three-dimensional compressed texture map. Updating all or part of an existing texture map may be considerably faster than reloading a texture image with glCompressedTexImage. You cannot perform an initial texture load with this function; it is used only to update an existing texture.

Parameters:

target

GLenumIt must be GL_TEXTURE_1D, GL_TEXTURE_2D, or GL_TEXTURE_3D.

level

GLintMipmap level to be updated.

xOffset

GLintOffset within the existing one-, two-, or three-dimensional texture in the x direction to begin the update.

yOffset

GLintOffset within the existing two- or three-dimensional texture in the y direction to begin the update.

zOffset

GLintOffset within the existing three-dimensional texture in the z direction to begin the update.

width

GLsizeiThe width of the one-, two-, or three-dimensional texture data being updated.

height

GLsizeiThe height of the two- or three-dimensional texture data being updated.

depth

GLsizeiThe depth of the three-dimensional texture data being updated.

imageSize

GLsizeiThe size in bytes of the compressed texture data.

data

const void*A pointer to the compressed texture data that is being used to update the texture target.

Returns:

None.

See Also:

glCompressedTexImage, glTexSubImage

glGetCompressedTexImage

Purpose:

Returns a compressed texture image.

Include File:

<gl.h>

Syntax:

void glGetCompressedTexImage(GLenum target, GLint 
const void*:level, void *pixels);

Description:

This function enables you to fill a data buffer with the data that comprises the current compressed texture. This function performs the reverse operation of glCompressedTexImage, which loads a compressed texture with a supplied data buffer. You cannot use this function to retrieve a compressed version of an uncompressed texture.

Parameters:

target

GLenumThe texture target to be copied. It must be GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.

level

GLintThe mipmap level to be read.

pixels

void*A pointer to a memory buffer to accept the compressed texture image.

Returns:

None.

See Also:

glCompressedTexImage, glCompressedTexSubImage, glGetTexImage

glMultiTexCoord

Purpose:

Specifies the current texture image coordinate for the specified texture unit.

Include File:

<gl.h>

Variations:

void glMultiTexCoord1d(GLenum texUnit, GLdouble s);
void glMultiTexCoord1dv(GLenum texUnit, GLdouble *v);
void glMultiTexCoord1i(GLenum texUnit, GLint s);
void glMultiTexCoord1iv(GLenum texUnit, GLint *v);
void glMultiTexCoord1s(GLenum texUnit, GLshort s);
void glMultiTexCoord1sv(GLenum texUnit, GLshort *v);
void glMultiTexCoord1f(GLenum texUnit, GLfloat s);
void glMultiTexCoord1fv(GLenum texUnit, GLfloat *v);
void glMultiTexCoord2i(GLenum texUnit, GLint s, 
void*:GLint t);
void glMultiTexCoord2iv(GLenum texUnit, GLint *v);
void glMultiTexCoord2s(GLenum texUnit, GLshort s, 
void*:GLshort t);
void glMultiTexCoord2sv(GLenum texUnit, GLshort *v);
void glMultiTexCoord2f(GLenum texUnit, GLfloat s, 
void*:GLfloat t);
void glMultiTexCoord2fv(GLenum texUnit, GLfloat *v);
void glMultiTexCoord2d(GLenum texUnit, GLdouble s,
void*: GLdouble t);
void glMultiTexCoord2dv(GLenum texUnit, GLdouble *v);
void glMultiTexCoord3s(GLenum texUnit, GLshort s, 
void*:GLshort t, GLshort r);
void glMultiTexCoord3sv(GLenum texUnit, GLshort *v);
void glMultiTexCoord3i(GLenum texUnit, GLint s, 
void*:GLint t, GLint r);
void glMultiTexCoord3iv(GLenum texUnit, GLint *v);
void glMultiTexCoord3f(GLenum texUnit, GLfloat s, 
void*:GLfloat t, GLfloat r);
void glMultiTexCoord3fv(GLenum texUnit, GLfloat *v);
void glMultiTexCoord3d(GLenum texUnit, GLdouble s,
void*: GLdouble t, GLdouble r);
void glMultiTexCoord3dv(GLenum texUnit, GLdouble *v);
void glMultiTexCoord4f(GLenum texUnit, GLfloat s, 
void*:GLfloat t, GLfloat r,
 GLfloat q);
void glMultiTexCoord4fv(GLenum texUnit, GLfloat *v);
void glMultiTexCoord4d(GLenum texUnit, GLdouble s,
void*: GLdouble t, GLdouble r,
 GLdouble q);
void glMultiTexCoord4dv(GLenum texUnit, GLdouble *v);
void glMultiTexCoord4i(GLenum texUnit, GLint s, 
void*:GLint t, GLint r, Glint q);
void glMultiTexCoord4iv(GLenum texUnit, GLint *v);
void glMultiTexCoord4s(GLenum texUnit, GLshort s, 
void*:GLshort t, GLshort r,
 GLshort q);
void glMultiTexCoord4sv(GLenum texUnit, GLshort *v);

Description:

These functions set the current texture image coordinate for a specific texture unit in one to four dimensions. Texture coordinates can be updated anytime between glBegin and glEnd, and correspond to the following glVertex call. The texture q coordinate is used to scale the s, t, and r coordinate values and, by default, is 1.0. Any valid matrix operation can be performed on texture coordinates by specifying GL_TEXTURE as the target of glMatrixMode.

Parameters:

texUnit

GLenumThe texture unit this texture coordinate applies to. It may be any value from GL_TEXTURE0 to GL_TEXTUREn, where n is the number of supported texture units.

s

GLdouble or GLfloat or GLint or GLshortThe horizontal texture image coordinate.

t

GLdouble or GLfloat or GLint or GLshortThe vertical texture image coordinate.

r

GLdouble or GLfloat or GLint or GLshortThe texture image depth coordinate.

q

GLdouble or GLfloat or GLint or GLshort: The texture image scaling value coordinate.

v

GLdouble* or GLfloat* or GLint* or GLshort*:

Returns:

None.

See Also:

glTexGen, glTexImage, glTexParameter, glTexCoord

glSecondaryColor

Purpose:

Specifies a secondary color.

Include File:

<gl.h>

Variations:

void glSecondaryColor3b(GLbyte red, GLbyte green, 
GLdouble or GLfloat or GLint or GLshortGLbyte blue);
void glSecondaryColor3s(GLshort red, GLshort green
GLdouble or GLfloat or GLint or GLshort, GLshort blue);
void glSecondaryColor3i(GLint red, GLint green, 
GLdouble or GLfloat or GLint or GLshortGLint blue);
void glSecondaryColor3f(GLclampf red, GLclampf 
GLdouble or GLfloat or GLint or GLshortgreen, GLclampf blue);
void glSecondaryColor3d(GLclampd red, GLclampd 
GLdouble or GLfloat or GLint or GLshortgreen, GLclampd blue);
void glSecondaryColor3ub(GLubyte red, GLubyte 
GLdouble or GLfloat or GLint or GLshortgreen, GLubyte blue);
void glSecondaryColor3us(GLushort red, GLushort 
GLdouble or GLfloat or GLint or GLshortgreen, GLushort blue);
void glSeconaryColor3ui(GLuint red, GLuint green, 
GLdouble or GLfloat or GLint or GLshortGLuint blue);
void glSeconaryColor3bv(GLbyte* values);
void glSecondaryColor3sv(GLshort* values);
void glSecondaryColor3iv(GLint* values);
void glSecondaryColor3fv(GLclampf* values);
void glSecondaryColor3dv(GLclampd* values);
void glSecondaryColor3ubv(GLubyte* values);
void glSecondaryColor3usv(GLushort* values);
void glSecondaryColor3uiv(GLuint* values);

Description:

When objects are texture mapped, specular highlights are typically muted by the application of texture after lighting. You can compensate for this effect by setting the GL_LIGHT_MODEL_COLOR_CONTROL light model parameter, which uses the specular lighting and material values for a secondary color added after the texture. This function allows you to specify a separate secondary color value that is added to fragments when lighting is not enabled—for example, if you were doing your own lighting calculations. Calls to glSecondaryColor have no effect when lighting is enabled. The color sum operation must be enabled with glEnable(GL_COLOR_SUM).

Parameters:

red

Specifies the red component of the color.

green

Specifies the green component of the color.

blue

Specifies the blue component of the color.

values

Specifies a pointer to the color component values.

Returns:

None.

See Also:

glColor

glTexGen

Purpose:

Defines parameters for texture coordinate generation.

Include File:

<gl.h>

Syntax:

void glTexGend(GLenum coord, GLenum pname, 
GLdouble or GLfloat or GLint or GLshortGLdouble param);
void glTexGenf(GLenum coord, GLenum pname, GLfloat
GLdouble or GLfloat or GLint or GLshort param);
void glTexGeni(GLenum coord, GLenum pname, GLint 
GLdouble or GLfloat or GLint or GLshortparam);
void glTexGendv(GLenum coord, GLenum pname, 
GLdouble or GLfloat or GLint or GLshortGLdouble *param);
void glTexGenfv(GLenum coord, GLenum pname, 
GLdouble or GLfloat or GLint or GLshortGLfloat *param);
void glTexGeniv(GLenum coord, GLenum pname, GLint 
GLdouble or GLfloat or GLint or GLshort*param);

Description:

This function sets parameters for texture coordinate generation when one or more of GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, or GL_TEXTURE_GEN_Q is enabled with glEnable. When GL_TEXTURE_GEN_MODE is set to GL_OBJECT_LINEAR, texture coordinates are generated by multiplying the current object (vertex) coordinates by the constant vector specified by GL_OBJECT_PLANE:

coordinate = v[0] * p[0] + v[1] * p[1] + v[2] *
GLdouble or GLfloat or GLint or GLshort p[2] + v[3] * p[3]

For GL_EYE_LINEAR, the eye coordinates (object coordinates multiplied through the GL_MODELVIEW matrix) are used. When GL_TEXTURE_GEN_MODE is set to GL_SPHERE_MAP, coordinates are generated in a sphere about the current viewing position or origin. When GL_TEXTURE_REFLECTION_MAP is specified, texture coordinates are calculated as a reflection from the current cube map. Finally, GL_TEXTURE_NORMAL_MAP also works with cube maps but transforms the object's normal to eye coordinates (instead of vertex) and uses them for texture coordinates.

Parameters:

coord

GLenumThe texture coordinate to map. It must be one of GL_S, GL_T, GL_R, or GL_Q.

pname

GLenumThe parameter to set. It must be one of GL_TEXTURE_GEN_MODE, GL_OBJECT_PLANE, or GL_EYE_PLANE.

param

The parameter value. For GL_TEXTURE_GEN_MODE, param is one of the following:

GL_OBJECT_LINEARTexture coordinates are calculated from object (vertex) coordinates.

GL_EYE_LINEARTexture coordinates are calculated by eye coordinates (object coordinates multiplied through the GL_MODELVIEW matrix).

GL_SPHERE_MAPTexture coordinates are generated in a sphere around the viewing position.

GL_REFLECTION_MAPTexture coordinates are generated using a cube map and are reflected from vertices.

GL_NORMAL_MAPTexture coordinates are generated using a cube map and are based on an objects normals transformed into eye coordinates.

For GL_OBJECT_PLANE and GL_EYE_PLANE, param is a four-element array used as a multiplier for object or eye coordinates.

Returns:

None.

See Also:

glTexCoord, glTexEnv, glTexImage1D, glTexImage2D, glTexParameter

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset