21 Texture Mapping

21.010 What are the basic steps for performing texture mapping?

At the bare minimum, a texture map must be specified, texture mapping must be enabled, and appropriate texture coordinates must be set at each vertex. While these steps will produce a texture mapped primitive, typically they don't meet the requirements of most OpenGL 1.2 applications. Use the following steps instead.

21.020 I'm trying to use texture mapping, but it doesn't work. What's wrong?

Check for the following:

21.030 Why doesn't lighting work when I turn on texture mapping?

There are many well-meaning texture map demos available on the Web that set the texture environment to GL_DECAL or GL_REPLACE. These environment modes effectively replace the primitive color with the texture color. Because lighting values are calculated before texture mapping (lighting is a per vertex operation, while texture mapping is a per fragment operation), the texture color replaces the colors calculated by lighting. The result is that lighting appears to stop working when texture mapping is enabled.

The default texture environment is GL_MODULATE, which multiplies the texture color by the primitive (or lighting) color. Most applications that use both OpenGL lighting and texture mapping use the GL_MODULATE texture environment.

Look for the following line in your code:

glTexEnv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); /* or GL_REPLACE */

You should change GL_DECAL to GL_MODULATE, or simply delete the line entirely (since GL_MODULATE is the default).

21.040 Lighting and texture mapping work pretty well, but why don't I see specular highlighting?

Your geometry may have a nice white specular highlight when it's not texture mapped, but when you apply a non-white texture suddenly the highlight goes away even though the geometry is still lit. This is because GL_MODULATE multiplies the primitive's lighting color components with the texture color components. For example, assume a white specular highlight is being multiplied by a red texture map. The final color is then (1.0*1.0, 1.0*0.0, 1.0*0.0) or (1.0, 0.0, 0.0), which is red. The white specular highlight isn't visible.

OpenGL 1.2 solves this problem by applying specular highlights after texture mapping. This separate specular lighting mode is turned on by:

glLightModel (GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR);

By default, it's set to GL_SINGLE_COLOR, which maintains backwards compatibility with OpenGL 1.1 and earlier.

If you're not using OpenGL 1.2, other solutions are available. Many vendors provide proprietary extensions for allowing you to apply the specular highlight after the texture map. See this example code for how to do this on HP systems. Many OpenGL vendors have settled on an the EXT_separate_specular_color extension.

Another method works on any OpenGL implementation, because it only uses regular OpenGL 1.0 functionality and doesn't depend on extensions. You need to render your geometry in two passes: first with normal lighting and texture mapping enabled, then the second pass will render the specular highlight. See this example code for a demonstration of how to do it.

21.050 How can I automatically generate texture coordinates?

Use the glTexGen() function.

21.060 Should I store texture maps in display lists?

See this question in the display list section.

21.070 How do texture objects work?

Texture objects store texture maps and their associated texture parameter state. They allow switching between textures with a single call to glBindTexture().

Texture objects were introduced in OpenGL 1.1. Prior to that, an application changed textures by calling glTexImage*(), a rather expensive operation. Some OpenGL 1.0 implementations simulated texture object functionality for texture maps that were stored in display lists.

Like display lists, a texture object has a GLuint identifier (the textureName parameter to glBindTexture()). OpenGL supplies your application with texture object names when your application calls glGenTextures(). Also like display lists, texture objects can be shared across rendering contexts.

Unlike display lists, texture objects are mutable. When a texture object is bound, changes to texture object state are stored in the texture object, including changes to the texture map itself.

The following functions affect and store state in texture objects: glTexImage*(), glTexSubImage*(), glCopyTexImage*(), glCopyTexSubImage*(), glTexParameter*(), and glPrioritizeTextures(). Since the GLU routines for building mipmap pyramids ultimately call glTexImage*(), they also affect texture object state.Noticeably absent from this list are glTexEnv*() and glTexGen*(); they do not store state in texture objects.

Here is a summary of typical texture object usage:

21.080 Can I share textures between different rendering contexts?

Yes, if you use texture objects. Texture objects can be shared the same way display lists can. If you're using Microsoft Windows, see the wglShareLists() function. For a GLX platform, see the share parameter to glXCreateContext().

21.090 How can I apply multiple textures to a surface?

Note that EXT_multitexture and SGIS_multitexture are both obsolete. The preferred multitexturing extension is ARB_multitexture.

The ARB_multitexture spec is included in the OpenGL 1.2.1 spec: http://www.opengl.org/Documentation/Specs.html.

An example is on Michael Gold's Web page.

A useful snippet is available at http://reality.sgi.com/blythe/sig99/advanced99/notes/node48.html. It's part of a wider presentation entitled Advanced Graphics Programming Techniques Using OpenGL. It's a useful supplement to anyone starting OpenGL and 3D graphics in general.

21.100 How can I perform light mapping?

You can simulate lighting by creating a texture map that mimics the light pattern and by applying it as a texture to the lit surface. After you've created the light texture map, there's nothing special about how you apply it to the surface. It’s just like any other texture map. For this reason, this question really isn't specific to OpenGL.

The GLUT 3.7 distribution contains an example that uses texture mapping to simulate lighting called progs/advanced97/lightmap.c.

21.110 How can I turn my files, such as GIF, JPG, BMP, etc. into a texture map?

OpenGL doesn't provide support for this. With whatever libraries or home-brewed code you desire to read in the file, then by using the glTexImage2D call, transform the pixel data into something acceptable, and use it like any other texture map.

Source code for doing this with TGA files can be found here.

See the Miscellaneous section for info on reading and writing 2D image files.

21.120 How can I render into a texture map?

With OpenGL 1.1, you can use the glCopyTexImage2D() or glCopyTexSubImage2D() functions to assist with this task. glCopyTexImage2D() takes the contents of the framebuffer and sets it as the current texture map, while glCopyTexSubImage2D() only replaces part of the current texture with the contents of the framebuffer. There's a GLUT 3.7 example called multispheremap.c that does this.

21.130 What's the maximum size texture map my device will render hardware accelerated?

A good OpenGL implementation will render with hardware acceleration whenever possible. However, the implementation is free to not render hardware accelerated. OpenGL doesn't provide a mechanism to ensure that an application is using hardware acceleration, nor to query that it's using hardware acceleration. With this information in mind, the following may still be useful:

You can obtain an estimate of the maximum texture size your implementation supports with the following call:

GLint texSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);

If your texture isn't hardware accelerated, but still within the size restrictions returned by GL_MAX_TEXTURE_SIZE, it should still render correctly.

This is only an estimate, because the glGet*() function doesn't know what format, internalformat, type, and other parameters you'll be using for any given texture. OpenGL 1.1 and greater solves this problem by allowing texture proxy.

Here's an example of using texture proxy:

glTexImage2D(GL_PROXY_TEXTURE_2D, level, internalFormat,
        width, height, border, format, type, NULL);

Note the pixels parameter is NULL, because OpenGL doesn't load texel data when the target parameter is GL_PROXY_TEXTURE_2D. Instead, OpenGL merely considers whether it can accommodate a texture of the specified size and description. If the specified texture can't be accommodated, the width and height texture values will be set to zero. After making a texture proxy call, you'll want to query these values as follows:

GLint width;

glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0,
        GL_TEXTURE_WIDTH, &width);

if (width==0) {
   /* Can't use that texture */
}

21.140 How can I texture map a sphere, cylinder, or any other object with multiple facets?

Texture map these objects using fractional texture coordinates. Each facet of an approximated surface or object will only show one small part of the texture map. Fractional texture coordinates determine what part of the texture map is applied to which facet.