Texture mapping applies colors from an image to primitives on a perfragment basis. During rasterization, OpenGL interpolates texture coordinates and assigns them to each generated fragment. OpenGL uses these coordinates as indices to obtain texels (texture elements) from a texture image. The texel colors modify the fragment's primary color based on texture state parameters.
OpenGL supports four basic texture map types:
1D textures A 1D texture map is a 1D array of texels. Applications specify a single s texture coordinate per vertex.
2D textures A 2D texture map is an image or other 2D array of texel values. This is the most common form of texture mapping. Applications specify both s and t texture coordinates per vertex.[1]
[1] The depth-mapping feature requires three texture coordinatess, t, and reven though the depth map is a 2D texture. See the section "Depth Maps" later in this chapter.
3D textures 3D texture maps commonly used in volume visualization applications are a 3D volume of texels.
Cube map textures Cube map textures are a set of six 2D texture maps. Each of the six maps represents a 90-degree field of view of the world from the viewpoint of the geometry using the texture. Cube maps are used for environment mapping and specular highlights.
This chapter covers 2D textures and cube maps. For information on 1D and 3D textures, see Chapter 9, "Texture Mapping," of OpenGL® Programming Guide.
OpenGL version 1.1 introduced the concept of texture objects, which encapsulate both texture images and their associated state parameters. This feature allows OpenGL to cache textures in a high-performance working set. Because modern applications almost universally use texture objects, OpenGL® Distilled doesn't describe version 1.0-style texture mapping.
If the OpenGL version is 1.3 or later, you can apply multiple textures to a single primitive with the multitexturing feature. Applications frequently use this feature to enhance image realism by storing shadows or other lighting effects in a second texture.
Environment mapping (Blinn 1976) simulates the reflection of an environment on a shiny surface. OpenGL provides two primary mechanisms to support environment mapping. OpenGL version 1.0 allows sphere mapping, but due to its inherent limitations, cube mapping was added to the OpenGL version 1.3 specification. Environment mapping simulates the display of a primitive's surroundings reflected on its surface. Because specular highlights are light-source reflections, applications often use cube mapping to improve the appearance of specular highlights on low-resolution geometry.
OpenGL version 1.4 contains support for the depth-map shadow algorithm first described by Lance Williams (Williams 1978), refined and popularized in Pixar's short film Luxo Jr. (Reeves 1987). Although the original algorithm suffers from aliasing artifacts, it's the simplest mechanism available in OpenGL's fixed-functionality pipeline for producing shadows.
To use texture mapping, perform the following steps in your application:
1.
Obtain an unused texture object identifier with glGenTextures(), and create a texture object using glBindTexture().
2.
Set texture-object state parameters.
3.
Specify the texture image using glTexImage2D() or gluBuild2DMipmaps().
4.
Before rendering geometry that uses the texture object, bind the texture object with glBindTexture().
5.
Before rendering geometry, enable texture mapping.
6.
Send geometry to OpenGL with appropriate texture coordinates per vertex.
6.1.1. Texture Objects
Texture objects store a texture image and its associated state. Most OpenGL implementations support a limited working set of texture objects, commonly implemented by storing the texture image in dedicated graphics hardware memory. Applications activate texture objects by issuing the glBindTexture() command.
Before the introduction of texture objects in OpenGL version 1.1, applications specified the texture image and state before each use, which typically was implemented by transmitting the image over the system bus. OpenGL still supports this usage, but modern OpenGL applications should use texture objects instead.
To use texture objects, your application must obtain, store, and eventually dispose of texture-object identifiers. For each texture object your application uses, you must store a texture image in it, along with the texture state parameters. Finally, when you want to render geometry that uses the texture, you must pass the texture-object identifier to glBindTexture().
To obtain unused texture-object identifiers, use glGenTextures(). To dispose of texture-object identifiers when they are no longer needed, call glDeleteTextures().
void glGenTextures( GLsizei n, GLuint* textures ); void glDeleteTextures( GLsizei n, const GLuint* textures ); GLboolean glIsTexture( GLuint texture );
Use these functions to obtain or dispose of texture-object identifiers. glGenTextures() returns n unused texture identifiers, marks them as used, and stores them in textures.
glDeleteTextures() deletes the texture objects associated with the n texture identifiers in textures and returns the identifiers to the unused texture identifier pool. If any of the texture identifiers is currently bound (see glBindTexture() below), glDeleteTextures() unbinds it.
glIsTexture() returns GL_TRUE if texture is an existing texture object.
For example, the following code obtains a single texture-object identifier:
GLuint texId; glGenTextures( 1, &texId );
glGenTextures() returns a texture-object identifier, but OpenGL doesn't create the texture object associated with that identifier until you bind the texture object for the first time with glBindTexture().
Specifies the active texture object. target must be GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, or GL_TEXTURE_CUBE_MAP, depending on whether your texture object stores a 1D, 2D, 3D, or cube map texture, respectively. texture specifies the texture-object identifier to bind. If texture hasn't been previously bound, glBindTexture() initializes it to default values.
glBindTexture()is available in version 1.1 and later. GL_TEXTURE_3Dis available in version 1.2 and later. GL_TEXTURE_CUBE_MAPis available in version 1.3 and later.
The first time your application calls glBindTexture() on a given texture, OpenGL creates and initializes a 1D, 2D, 3D, or cube map texture object (depending on the target parameter) and associates that texture object with the identifier in texture. The new texture object has default parameters and a NULL texture image. Applications usually obtain texture-object identifiers and create texture objects during initialization.
Subsequent calls to glBindTexture() on a given texture activate the texture and texture state stored in the texture object. Applications call glBindTexture() at render time just before specifying geometry that uses the texture object.
It's an error to use a different target in subsequent calls to bind the same texture.glBindTexture() generates the error GL_INVALID_OPERATION if you bind a texture with a different target.
6.1.1.1. Texture Object State
Set texture object state with the glTexParameteri() command.
void glTexParameter[if]( GLenum target, GLenum pname, TYPE param );
Sets the texture state parameter specified by pname to the value specified by param in the texture object currently bound to target. Table 6-1 lists valid values for pname and param. target is the same as for glBindTexture().
You can use glTexParameter*() to set other texture parameters not covered in this book. For more information, see "glTexParameter" in OpenGL® Reference Manual.
1.0 and later. Many pname and param values, however, are available only in more recent versions. See Table 6-1 for details.
Table 6-1. Valid pname and param Values
pname
Valid param Values
GL_DEPTH_TEXTURE_MODE
GL_LUMINANCE, GL_INTENSITY, or GL_ALPHA to format the result of the depth comparison as a luminance, intensity, or alpha texel. For more information, see the section "Depth Maps" later in this chapter.
GL_GENERATE_MIPMAP
GL_TRUE or GL_FALSE to enable or disable mipmap generation.
GL_TEXTURE_COMPARE_FUNC
GL_LEQUAL or GL_GEQUAL[a] to control the depth comparison. For more information, see the section "Depth Maps" later in this chapter.
GL_TEXTURE_COMPARE_MODE
GL_COMPARE_R_TO_TEXTURE or GL_NONE to enable or disable the depth comparison. For more information, see the section "Depth Maps" later in this chapter.
GL_TEXTURE_MAG_FILTER
GL_NEAREST or GL_LINEAR to set the filter method for texture magnification.
GL_TEXTURE_MIN_FILTER
GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, or GL_LINEAR_MIPMAP_LINEAR to set the filter method for texture minification.
GL_CLAMP, GL_REPEAT, or GL_CLAMP_TO_EDGE to set the wrap behavior for texture coordinates outside the range 0.0 to 1.0.
[c]
[a] OpenGL version 1.5 expands the list of valid param values for GL_TEXTURE_COMPARE_FUNC, which this book does not cover. For more information, see Section 3.8.4, "Texture Parameters," of The OpenGL Graphics System.
[b]GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T are in OpenGL version 1.0, whereas GL_TEXTURE_WRAP_R was not introduced until OpenGL version 1.2.
[c]GL_CLAMP_TO_EDGE was added in OpenGL version 1.2. Other wrap modes exist in later versions.
After obtaining a texture-object identifier with glGenTextures() and creating its texture object with the first call to glBindTexture(), applications usually call glTexParameteri() to set texture state parameters for the new texture object.
The texture parameters covered in OpenGL® Distilled fall into three categories:
Mipmap and filter parameters
Depth-map parameters
Texture-coordinate wrap parameters
For an explanation of the depth-map parameters GL_DEPTH_TEXTURE_MODE, GL_TEXTURE_COMPARE_FUNC, and GL_TEXTURE_COMPARE_MODE, see the section "Depth Maps" later in this chapter. For an explanation of the texture-coordinate wrap parameters GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, and GL_TEXTURE_WRAP_R, see the section "Texture Coordinates" later in this chapter.
The texture parameters GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER control the selection of texels when there isn't a one-to-one relationship between the generated fragments and the texels.
During rasterization, if a fragment covers less area than a texel, OpenGL uses the value of GL_TEXTURE_MAG_FILTER to determine the resulting texture color. When set to GL_NEAREST, OpenGL uses the texel that contains the fragment center. Most applications, however, use the default value, GL_LINEAR, which causes OpenGL to interpolate between texel values to arrive at the texture color for the fragment.
If a fragment covers more area than a texel, OpenGL uses the value of GL_TEXTURE_MIN_FILTER to determine the resulting texture color. In addition to GL_NEAREST and GL_LINEAR modes, OpenGL supports mipmap modes that cause OpenGL to interpolate between different levels of detail. The default value is GL_NEAREST_MIPMAP_LINEAR, which instructs OpenGL to select single texels from the two closest mipmap levels and then linearly interpolate between them. Most applications use mipmapped textures but specify a GL_TEXTURE_MIN_FILTER of GL_LINEAR_MIPMAP_LINEAR instead of the default. This filter mode produces antialiased results when texels cover less area than fragments.
Do not use mipmapping on depth-map textures. Instead, specify a GL_TEXTURE_MIN_FILTER of GL_LINEAR. See the section "Depth Maps" later in this chapter for more information.
If your application uses a mipmapped value for GL_TEXTURE_MIN_FILTER (for example, GL_LINEAR_MIPMAP_LINEAR), you must specify a complete set of mipmap levels when you specify the texture image. OpenGL or GLU can do this for you, however, as described in the next section.
6.1.1.2. Specifying Textures
When you bind your texture object for the first time and set its state, you should also call glTexImage2D() to specify the texture image.
If your application uses a mipmapped value for GL_TEXTURE_MIN_FILTER (for example, GL_LINEAR_MIPMAP_LINEAR), you must specify all texture images in the mipmap pyramid. Applications rarely do this manually. If the OpenGL version is 1.4 or higher, first bind the texture object and then set the GL_GENERATE_MIPMAP texture parameter to GL_TRUE before specifying the texture with glTexImage2D(). For example:
If your OpenGL version is earlier than 1.4, GL_GENERATE_MIPMAP is unavailable. To create all mipmap levels, use the GLU routine gluBuild2DMipmaps() as described later in this section.
Specifies a 2D texture map or one of the six textures used in a cube map. If your texture object is a 2D texture, specify GL_TEXTURE_2D for target. If your texture object is a cube map, see the section "Environment Maps" later in this chapter.
level indicates the mipmap level of this image. For mipmap generation, or if you're not using a mipmap, specify a level of 0.
internalformat specifies a base internal format for the image. For backward compatibility with version 1.0, many applications simply specify the number of components per pixel, such as 4 for an RGBA texture, 3 for RGB, or 1 for a depth map or light map. internalformat can also be an enumerant indicating the texture format, such as GL_RGBA or GL_DEPTH_COMPONENT. Many such enumerants are available; see "glTexImage" in OpenGL® Reference Manual for details.
width and height are the texture dimensions. If the OpenGL version is less than 2.0, these must both be powers of 2. If the texture is part of a cube map, width and height must be equal.
border indicates a texture border width, which is useful for eliminating seams when tiling textures together. Set border to 0 if not using texture borders. This book doesn't cover texture borders. See OpenGL® Programming Guide for information on using texture borders.
format, type, and data are the same as for the glDrawPixels() command described in Chapter 5, "Pixel Rectangles."
OpenGL interprets pixel data that is sent as a texture image the same way that it interprets pixel data sent with glDrawPixels(). Each row must start on a 4-byte boundary by default.
When GL_GENERATE_MIPMAP is set to GL_TRUE, glTexImage2D() filters the texture image into successively lower-detail images to complete the mipmap pyramid. Furthermore, OpenGL recalculates mipmap images if a future change affects the base texture image. Unfortunately, only OpenGL versions 1.4 and later support this functionality. To create mipmap levels in older versions of OpenGL, use the GLU routine gluBuild2DMipmaps().
int gluBuild2DMipmaps( GLenum target, GLint components, GLsizei width, GLint height, GLenum format, GLenum type, const void* data );
Creates mipmapped texture images. components is the number of components per image element. All other parameters are the same as for glTexImage2D().
gluBuild2DMipmaps() creates copies of the texture image specified by data at successively lower resolutions to complete a pyramid of mipmapped images. It calls glTexImage2D() for each image, passing in a different value for level.
When specifying a mipmapped texture, check the OpenGL version. If the version is 1.4 or greater, use GL_GENERATE_MIPMAP with glTexImage2D(); otherwise, use gluBuild2DMipmaps(). The following pseudocode illustrates this:
Mipmap generation in OpenGL version 1.4 differs from gluBuild2DMipmaps() in one very important way: Mipmap generation is state in a texture object. If your application sets GL_GENERATE_MIPMAP to GL_TRUE and then changes the base-level texture image (with a call to glTexImage2D() or glCopyTexImage2D()), OpenGL recomputes texture images for each level of the mipmap pyramid.
6.1.2. Texture Coordinates
Applications assign texture coordinates to each vertex to associate regions of a texture with a primitive. During rasterization, OpenGL uses a perspective-correct interpolation algorithm to assign appropriate texture coordinates to each generated fragment.
In simple 2D texture mapping, texture coordinates map to texels as though the texture image were a Cartesian coordinate system consisting of s and t axes, as shown in Figure 6-1. The (s, t) origin, (0, 0), maps to the bottom-left corner of the texture image or the first pixel specified in the data parameter to glTexImage2D(). Texture-coordinate location (1, 1) maps to the top-right corner of the image or the last pixel in the data block. Therefore, you can access any texel in a texture image by using normalized 2D texture coordinates s and t in the range 0.0 to 1.0, with s accessing texels horizontally and t accessing texels vertically.
Figure 6-1. OpenGL displays the full texture image using a normalized coordinate system.
If your application specifies texture coordinates outside the range 0.0 to 1.0, OpenGL uses the GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T state parameters to control texel lookup. The default GL_REPEAT value for these parameters causes the texture map pattern to repeat. In other words, OpenGL performs a modulo function on the texture coordinates, so the effective texture coordinates are always in the range 0.0 to 1.0. Figure 6-2 shows the effect of GL_REPEAT.
Figure 6-2. GL_REPEAT texture-coordinate wrap mode. This figure shows a single GL_QUADS primitive. Starting with the vertex at the bottom-left corner and proceeding counterclockwise, the texture coordinates are (1, 1), (2, 1), (2, 2), and (1, 2).
GL_REPEAT is inappropriate for many algorithms, especially depth mapping (see the section "Depth Maps" later in this chapter). When performing depth mapping, applications often want s and t values to clamp when outside the range 0.0 to 1.0. The following code demonstrates how to clamp s and t to the edge of the texture:
A third mode, GL_CLAMP,[2] is useful if your application tiles textures or uses texture borders. Additional modes, such as GL_MIRRORED_REPEAT and GL_CLAMP_TO_BORDER, are available in more recent versions of OpenGL. See Chapter 9, "Texture Mapping," of OpenGL® Programming Guide for information on texture borders and other wrap modes.
[2] Some implementations interpret GL_CLAMP as GL_CLAMP_TO_EDGE by default, with a device-driver mode switch to change this behavior so that it complies with the OpenGL specification.
Texture coordinates are actually composed of four values: s, t, r, and q. When applications specify 2D (s, t ) texture coordinates, OpenGL implicitly sets r to 0.0 and q to 1.0. OpenGL transforms each texture-coordinate vector [ s t r q ] by the texture matrix. If GL_TEXTURE_2D is enabled, OpenGL uses the transformed coordinate (s/ q, t/ q ) to actually look up the texel values. The division by q has applications in projective texture mapping. The section "Depth Maps" later in this chapter describes how to use s/ q, t/ q, and r/ q as perspective-correct coordinates in light space.
6.1.2.1. Specifying Texture Coordinates
Applications commonly specify texture-coordinate data explicitly, as described in the section "Vertex Array Data" in Chapter 2, "Drawing Primitives." In summary, applications create an array of texture coordinates, store them in a buffer object, and use glTexCoordPointer() to index into the buffer object.
Explicit texture coordinates are inappropriate for some algorithms, however. In environment mapping, for example, texture coordinates derive from surface reflection vectors. Computing these coordinates and dynamically updating the buffer object each frame is inefficient and computationally prohibitive. For this reason, OpenGL features several texture-coordinate-generation modes, as described in the following section.
6.1.2.2. Texture-Coordinate Generation
Enable or disable texture-coordinate generation individually for texture-coordinate values s, t, r, and q with glEnable() and glDisable(). The following code enables texture-coordinate generation for s, t, r, and q:
Texture-coordinate generation is disabled by default.
OpenGL features a variety of algorithms for generating texture coordinates. You control how OpenGL generates the coordinates with glTexGeni() and glTexGendv().
Specifies how OpenGL generates texture coordinates. In both forms of glTexGen(), coord must be GL_S, GL_T, GL_R, or GL_Q to specify the relevant texture coordinate.
For glTexGen*(), pass GL_TEXTURE_GEN_MODE for pname, and params must be GL_OBJECT_LINEAR, GL_EYE_LINEAR, GL_SPHERE_MAP, GL_REFLECTION_MAP, or GL_NORMAL_MAP.
For glTexGen*v(),pname can be either GL_OBJECT_PLANE or GL_EYE_PLANE, and params points to a four-element array representing a plane equation.
1.0 and later.GL_REFLECTION_MAPandGL_NORMAL_MAPare available in version 1.3 and later.
OpenGL® Distilled doesn't cover all texture-coordinate-generation modes. For more information on texture-coordinate generation, see "glTexGen" in OpenGL® Reference Manual or Chapter 9, "Texture Mapping," of OpenGL® Programming Guide.
The above code enables texture-coordinate generation for both s and t. The code sets the generation mode to GL_OBJECT_LINEAR. This code causes OpenGL to derive s and t texture coordinates from the x and y object coordinates.
That's the short story. What OpenGL does under the hood is somewhat more complex. Both s and t coordinates have object plane equations, which you can change with the following code:
OpenGL computes the s texture coordinate by taking the dot product of the four-element xyzw object coordinate and the s coordinate object plane. OpenGL computes the t coordinate similarly. By default, the s and t object plane equations are (1, 0, 0, 0) and (0, 1, 0, 0), respectively.
Later in this chapter, the section "Depth Maps" describes how to use GL_EYE_LINEAR to generate texture coordinates in the light source's coordinate space for use with depth-map textures. The section "Environment Maps" describes how to use GL_REFLECTION_MAP to produce improved specular highlights.
6.1.2.3. The Texture Matrix
OpenGL transforms all texture coordinates, whether specified explicitly or generated with glTexGen(), by the top of the texture matrix stack before using the texture coordinates to index into the texture image.
Chapter 3, "Transformation and Viewing," describes the glMatrixMode() function. You can set the current matrix mode to GL_MODELVIEW or GL_PROJECTION to specify matrices that transform vertices and normals. You can also set the matrix mode to GL_TEXTURE, however. This command configures OpenGL so that subsequent matrix operations (glPushMatrix(), glPopMatrix(), glLoadMatrixf(), glMultMatrixd(), glRotatef(), and so on) affect the texture-matrix stack. By default, the top of the texture-matrix stack is an identity matrix. If applications don't use the texture-matrix stack, OpenGL uses their texture coordinates effectively untransformed.
Applications use the texture matrix for a variety of purposes. Computer games, for example, often animate clouds moving across the sky by translating sky-geometry texture coordinates.
The examples available on the OpenGL® Distilled Web site use texture matrices in two ways:
The SecondaryColor example program applies a small translation to the texture image so that it appears in the correct location on the torus.
The CubeMap example program uses the texture matrix to transform reflection vectors in response to changes in the eye-coordinate light position. For more information, see the section "Environment Maps" later in this chapter.
Each texture unit has its own texture-matrix stack. See the next section, "Multitexturing," for more information.
6.1.3. Multitexturing
If the OpenGL version is 1.3 or later, you can configure OpenGL to apply multiple textures per primitive by using the multitexturing feature. Many applications use multitexturing to add complex lighting and shadow effects to texture mapped objects with one image as a base texture and a second image as a single-channel luminance texture or depth map. Other applications for multitexturing include decals and detail textures.
The number of textures you can apply to a single primitive is determined by the number of supported texture units. Query the number of supported texture units with glGetIntegerv() as follows:
Each texture unit is numbered, starting with GL_TEXTURE0. The number of supported texture units is implementation dependent but must be at least two.
For each fragment produced by rasterization, OpenGL iterates over all enabled texture units in sequence, starting with GL_TEXTURE0, and applies the associated texture to the fragment.
To use multitexturing, you need to perform the following steps:
1.
Set the active texture unit.
2.
Set state specific to that texture unit.
3.
Specify geometry with a texture-coordinate set for each texture unit.
6.1.3.1. Setting the Active Texture Unit
Select the active texture unit with the glActiveTexture() command.
void glActiveTexture( GLenum texture );
Selects the active texture unit. texture must be GL_TEXTURE0, GL_TEXTURE1, and so on up to GL_TEXTUREi, where i is the value of GL_MAX_TEXTURE_UNITS minus 1.
glActiveTexture() selects the active texture unit in OpenGL. The default texture unit is GL_TEXTURE0. Applications set the active texture unit before issuing commands that affect texture unit state, as described in the next section.
6.1.3.2. Texture Unit State
Several OpenGL commands affect texture unit state, such as:
glBindTexture(), which associates a texture object with the active texture unit.
glTexGen(), which sets texture-generation parameters for the active texture unit.
Matrix commands, which affect the active texture-unit matrix stack when the current matrix mode (set with glMatrixMode()) is GL_TEXTURE. There is a separate texture-matrix stack for each texture unit.
glTexEnvi(), which controls how the active texture unit applies the texture image.
glEnable()/glDisable() for texture-coordinate generation, which determines whether the active texture unit generates texture coordinates.
glEnable()/glDisable() for GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP, which specifies whether the active texture unit applies a 2D or cube map texture.
All these state variables can be used without multitexturing. If your application never calls glActiveTexture(), the variables affect the default texture unit, GL_TEXTURE0, and all other texture units remain disabled.
Most of the state items listed above have already been discussed in this chapter. The one remaining state variable is glTexEnvi(), which controls how the texture unit combines the texture color with the primary color of the incoming fragment.
void glTexEnv[if]( GLenum target, GLenum pname, TYPE param );
When target is GL_TEXTURE_ENV and pname is GL_TEXTURE_ENV_MODE, the value of param controls how the active texture unit applies texture color values to fragments. Valid values for param include GL_MODULATE, GL_REPLACE, GL_DECAL, and GL_ADD.
Although OpenGL supports other values for target, pname, and param, they aren't covered in this book. You can also use similar functions glTexEnvf(), glTexEnviv(), and glTexEnvfv() to set other texture environment parameters. For more information, see "glTexEnv" in OpenGL® Reference Manual.
1.0 and later. GL_ADDis available in version 1.3 and later.
To replace the incoming fragment's color value completely with the texture color, set param to GL_REPLACE as follows:
Although GL_REPLACE has some applications, keep in mind that it eliminates nearly all lighting effects computed with glEnable( GL_LIGHTING ). Because lighting effects are part of the fragment's primary color before OpenGL performs texturing, GL_REPLACE completely replaces the (lit) primary color value with the (unlit) texture color.
The default texture environment mode is GL_MODULATE, which replaces the fragment's primary color with the result of a componentwise multiplication of the texture color and the incoming primary color. Applications often use GL_MODULATE in conjunction with lighting, because it preserves ambient and diffuse lighting effects. GL_MODULATE mutes specular highlights, however. OpenGL provides two mechanisms to improve the appearance of specular highlights on texture mapped surfaces: the separate specular color feature, described in the section "Specular Highlights" later in this chapter, and cube maps, described in the section "Environment Maps" later in this chapter.
To add the texture color to the primary color, use GL_ADD. Applications often use GL_ADD in environment mapping to add specular highlights or other reflections to a surface. GL_ADD is available only in OpenGL in version 1.3 or later.
6.1.3.3. Texture-Coordinate Sets
When using multitexturing, applications typically specify a set of texture coordinates for each enabled texture unit. Texture-coordinate sets can be identical or different for each texture unit, or specified explicitly for one unit and generated for another.
glActiveTexture() selects the active texture unit for server-side texture unit state. To specify vertex arrays with multiple texture-coordinate sets, OpenGL provides the glClientActiveTexture() command. When your application issues the glTexCoordPointer() command to specify an array of texture coordinates (either explicitly or sourced from a buffer object), OpenGL assigns that texture-coordinate set to the texture unit specified with glClientActiveTexture(). glEnableClientState( GL_TEXTURE_COORD_ARRAY ) and glDisableClientState( GL_TEXTURE_COORD_ARRAY ) also reference the client active texture unit.
When rendering, applications typically specify several texture-coordinate sets, as Listing 6-1 shows.
Listing 6-1. Code for setting multiple texture-coordinate sets.
In the above code, numTextures is a local variable that specifies the number of texture units required by the application. texCoordBuffer is the identifier of a buffer object containing an array of packed floating-point s and t texture coordinates.
6.1.4. Texture Mapping Example
The SimpleTextureMapping example, available from this book's Web site, renders a simple texture mapped quadrilateral. The goal of this example is to illustrate how to create a minimal texture object and map the texture to a primitive.
Figure 6-4 shows a single texture mapped quadrilateral. The four vertices use texture coordinates (0, 0), (1, 0), (1, 1), and (0, 1) to show the entire texture image.
Figure 6-4. A screen shot from the SimpleTextureMapping example program.
Listing 6-2 contains the code for this example program.
Listing 6-2. Code for the SimpleTextureMapping example
#include <GL/glut.h> #include <GL/glu.h> #include <GL/gl.h> #include "OGLDPixels.h" #include "OGLDPlane.h" Static const int QUIT_VALUE( 99 ); ogld::Plane plane; GLuint texId( 0 ); static void display() { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); gluLookAt( 0., 0., 2., 0., 0., 0., 0., 1., 0. ); glBindTexture( GL_TEXTURE_2D, texId ); plane.draw(); glutSwapBuffers(); } static void reshape( int w, int h ) { glViewport( 0, 0, w, h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 40., (double)w/(double)h, 1., 10. ); /* Leave us in modelview mode for our display routine */ glMatrixMode( GL_MODELVIEW ); } static void mainMenuCB( int value ) { if (value == QUIT_VALUE) { // Demonstrates how to delete a texture object. // Unnecessary in this simple case, since the following // call to exit() will destroy the rendering context // and all associated texture objects. glDeleteTextures( 1, &texId ); exit( 0 ); } } static void init() { // Use the ogld::Pixels class to load the image and obtain a pointer // to the pixel data. ogld::Pixels image; image.loadImage( std::string( "NewspaperRock.tif" ) ); int width, height; image.getWidthHeight( width, height ); // Obtain a texture ID and create/init the texture object glGenTextures( 1, &texId ); glBindTexture( GL_TEXTURE_2D, texId ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexImage2D( GL_TEXTURE_2D, 0, image.getFormat(), width, height, 0, image.getFormat(), image.getType(), image.getPixels() ); glEnable( GL_TEXTURE_2D ); glClearColor( .9, .9, .9, 0. ); glDisable( GL_DITHER ); glutDisplayFunc( display ); glutReshapeFunc( reshape ); int mainMenu = glutCreateMenu( mainMenuCB ); glutAddMenuEntry( "Quit", QUIT_VALUE ); glutAttachMenu( GLUT_RIGHT_BUTTON ); } int main( int argc, char** argv ) { glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE ); glutInitWindowSize( 300, 300 ); glutCreateWindow( "Simple Texture Mapping" ); init (); glutMainLoop (); return 0; }
The code employs the ogld::Plane class to render the quadrilateral, which hides the texture-coordinate specification. Listing 6-1 earlier in this chapter shows how to specify texture-coordinate sets. You can also download the example code to see how this is done within ogld::Plane.
The code declares a global GLuint variable, texId, which holds the texture-object identifier. The display() callback function references this identifier in the call to glBindTexture(). After the code binds the texture object, it renders the ogld::Plane.
Later in the code, the init() function is responsible for creating and initializing the texture object. It employs the ogld::Pixels class to load the texture image from a TIFF file. Next, it initializes texId by obtaining an unused texture ID with a call to glGenTextures() and immediately calls glBindTexture( GL_TEXTURE_2D, texId ), which creates a 2D texture object with default state values.
The next three commands set state in the newly created texture object. The glTexParameteri() command changes the texture minification filter from the default GL_NEAREST_MIPMAP_LINEAR to GL_LINEAR, because this code doesn't use mipmapping. Next, the code specifies the texture image with the glTexImage2D() command. Finally, the code enables texturing.
Tip
Because the default minification filter is a mipmap filter, your code must change this to a nonmipmapped filter or specify a mipmapped texture image. Typically, applications use mipmapped images, but not always.
If you use a mipmap filter without a mipmapped image, OpenGL acts as though texture mapping is disabled. As an exercise, download the SimpleTextureMapping example and comment out the glTexParameteri() command, which leaves the minification filter set to its default value of GL_NEAREST_MIPMAP_LINEAR. After this change, the code produces a nontextured white quadrilateral.
Although this example doesn't use multitexturing, the ogld::Plane class uses multitexturing if available in OpenGL. By default, it uses one texture unit, GL_TEXTURE0, which is essentially the same as not using multitexturing.