JavaScript EditorFree JavaScript Editor     Ajax Editor 



Main Page
Previous Page
Next Page

10.4. Cube Mapping Example

A technique called ENVIRONMENT MAPPING models reflections in a complex environment without resorting to ray-tracing. In this technique, one or more texture maps simulate the environment's reflections. This technique is best used for rendering objects that have some mirrorlike qualities.

The fundamental idea behind environment mapping is that we use the reflection vector from the surface of an object to look up the reflection color from an "environment" that is stored in a texture map. If environment mapping is done properly, the result looks as if the object being rendered is shiny and is reflecting its environment.

There are several ways to do environment mapping, including SPHERE MAPPING and CUBE MAPPING, both of which are supported in standard OpenGL. An example cube map is shown in Color Plate 10.

In this section, we describe OpenGL shaders that use a cube map to perform environment mapping on an object. The object is assumed to have an underlying layer that acts as a diffuse reflector. The result from the diffuse portion is combined with the environment reflection to produce a final value at each pixel.

A cube map is a texture that has six 2D textures that are organized to represent the faces of a cube. Cube maps are accessed with three texture coordinates that are treated as a direction vector emanating from the center of the cube. The cube map faces are differentiated by the sign along each of the three major axis directions. Think of the faces this way: The positive and negative x faces are the right and left sides of the cube; positive and negative y faces are the top and bottom sides of the cube; and positive and negative z faces are the back and front sides of the cube. Graphics hardware can use the three texture coordinates as a direction vector and automatically select the proper face and return a texel value where the direction vector intersects that face of the cube map.

10.4.1. Application Setup

The application needs to do very little to set up this shader. We use a simple lighting model, so the only lighting state that we need to pass in is a single light source position. We access the cube map through texture unit 4. baseColor defines the color of the diffuse underlayer of our object, and mixRatio sets the ratio of base color to environment map reflection. Here are the definitions for the uniform variables that we use:

LightPos

0.0, 0.0, 4.0

BaseColor

0.4, 0.4, 1.0

MixRatio

0.8

EnvMap

4


After the shaders have been installed and the uniform variables have been provided, the application is expected to send a normal and the vertex position for each vertex that is to be drawn. The current values for the modelview matrix, the modelview-projection matrix, and the normal matrix are all accessed from within the vertex shader.

10.4.2. Vertex Shader

Listing 10.5 comprises the vertex shader that is used to do environment mapping with a cube map.

Listing 10.5. Vertex shader used for environment mapping with a cube map

varying vec3  ReflectDir;
varying float LightIntensity;

uniform vec3  LightPos;

void main()
{
    gl_Position    = ftransform();
    vec3 normal    = normalize(gl_NormalMatrix * gl_Normal);
    vec4 pos       = gl_ModelViewMatrix * gl_Vertex;
    vec3 eyeDir    = pos.xyz;
    ReflectDir     = reflect(eyeDir, normal);
    LightIntensity = max(dot(normalize(LightPos - eyeDir), normal),0.0);
}

The goal of this vertex shader is to produce two values that will be interpolated across each primitive: a diffuse lighting value and a reflection direction. The reflection direction is used as the texture coordinate for accessing the cube map in the fragment shader.

We compute the transformed position of the vertex in the first line of the program in the usual way. We transform and normalize the incoming normal, and then we compute the eye direction, based on the current modelview matrix and the incoming vertex value. We pass these two values to the built-in function reflect to compute the reflection direction vector. Finally, we compute a diffuse lighting value in the same manner that we've done in previous examples.

10.4.3. Fragment Shader

Listing 10.6 contains the fragment shader that performs environment mapping with a cube map.

Listing 10.6. Fragment shader for doing environment mapping with a cube map

uniform vec3  BaseColor;
uniform float MixRatio;

uniform samplerCube EnvMap;

varying vec3  ReflectDir;
varying float LightIntensity;

void main()
{
    // Look up environment map value in cube map

    vec3 envColor = vec3(textureCube(EnvMap, ReflectDir));

    // Add lighting to base color and mix

    vec3 base = LightIntensity * BaseColor;
    envColor  = mix(envColor, base, MixRatio);

    gl_FragColor = vec4(envColor, 1.0);
}

The fragment shader for cube map environment mapping does three things. First, the computed and interpolated varying variable ReflectDir is used to access our cube map texture and return the texel value that is used to simulate the reflection from a shiny surface. Second, the base color of the object is modulated by the interpolated light intensity value. Finally, these two values are combined in the ratio defined by the uniform variable MixRatio. This uniform variable can be modified by the user to make the object vary between completely shiny and completely diffuse.


Previous Page
Next Page




JavaScript EditorAjax Editor     JavaScript Editor