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. Its 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.
|