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.

  • Create a texture object for each texture in use. The texture object stores the texture map and associated texture parameter state. See question 21.070 for more information on texture objects.
  • Store each texture map or mipmap pyramid in its texture object, along with parameters to control its use.
  • On systems with limited texure memory, set the priority of each texture object with glPrioritizeTextures() to minimize texture memory thrashing.
  • Whem your application renders the scene, bind each texture object before rendering the geomtry to be texture mapped. Enable and disable texture mapping as needed.

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

Check for the following:

  • Texture mapping should be enabled, and a texture map must be bound (when using texture objects) or otherwise submitted to OpenGL (for example, with a call to glTexImage2D()).
  • Make sure you understand the different wrap, environment, and filter modes that are available. Make sure you have set appropriate values.
  • Keep in mind that texture objects don't store some texture parameters. Texture objects bind to a target (either GL_TEXTURE_1D, GL_TEXTURE_2D, or GL_TEXTURE_3D), and the texture object stores changes to those targets. glTexGen(), for example, doesn't change the state of the texture target, and therefore isn't part of texture objects.
  • If you're using a mipmapping filter (e.g., you've called glTexParameter*(), setting a min or mag filter that has MIPMAP in its name), make sure you've set all levels of the mipmap pyramid. All levels must be set, or texture mapping won't occur. You can set all levels at the same time with the gluBuild2DMipmaps() function. All levels of the mipmap pyramid must have the same number of components.
  • Remember that OpenGL is a state machine. If you don't specify texture coordinates, either explicitly with glTexCoord*(), or generated automatically with glTexGen()), then OpenGL uses the current texture coordinate for all vertices. This may cause some primitives to be texture mapped with a single color or single texel value.
  • If you're using multiple rendering contexts and need to share texture objects between contexts, you must explicitly enable texture object sharing. This is done with the wglShareLists() function in Microsoft Windows and glXCreateContext() under X Windows.
  • Check for errors with glGetError().

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:

  • Get a textureName from glGenTextures(). You'll want one name for each of the texture objects you plan to use.
  • Initially bind a texture object with glBindTexture(). Specify the texture map, and any texture parameters. Repeat this for all texture objects your application uses.
  • Before rendering texture mapped geometry, call glBindTexture() with the desired textureName. OpenGL will use the texture map and texture parameter state stored in that object for rendering.

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. ARB_multitexture is the preferred multitexturing extension.

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.

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.

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.