ATI SDK

ATI Product Information

Support for Alternate OS's

Hardware partners

Software partners

RenderMonkey

Drivers


 
 

Highlights


GPU MeshMapper (V1.0)

GPU PerfStudio (V1.2)

Samples: CrossFire Detect (update)

Samples: PostTonemapResolve

The Compressonator (version 1.41)

GPU Shader Analyzer (V1.42)

RenderMonkey™
(version 1.81) (New)


ATI Compress (version 1.6)

AMD Tootle 2.0 (New)

AMD OpenGL ES 2.0 Emulator (V1.1) (New)

HLSL2GLSL (V0.9)

AMD at GDC 2007

ATI SDK


 
 
ATI Developer - Source Code
 
Bump Mapping on ATI Rage 128

Sections
Rage128 Bump Mapping
Direct3D
OpenGL
References and Links

Introduction
Bump mapping is a shading technique introduced in 1978 by Jim Blinn [Blinn78] to simulate wrinkled or bumped surfaces. In general, bump mapping techniques perturb the illumination of a surface on a per-pixel basis to create the illusion of bumps. The underlying geometry of the surface is not altered in any way---bump mapping is purely a rasterization-level illumination trick. Bump mapping techniques supported by current mainstream 3D graphics accelerators diverge fairly significantly from Blinn's original method, but produce very acceptable results.

Rage128 Bump Mapping
The bump mapping technique supported by the majority of 3D graphics accelerators, including the Rage128, is a form of embossing. A good 2D discussion of embossing can be found in [Schlag84]. The basic idea of embossing comes from the observation that subracting a grayscale heightmap from a slightly shifted version of itself gives the appearance of a bumpy or embossed surface. The direction of the shift gives the appearance of illumination from a particular direction. Mathematically, this subtraction of the height map is an approximation of the derivative of the height map in the direction of the shift.

A 2D Example
Figure 1 below shows a grayscale heightmap of a brick wall and a magnified portion of the top-left corner, provided by the IBM Human Computer Interaction Texture Library. In keeping with image processing conventions, white texels represent high areas in the height map while black texels are low.
Click to enlarge Click to enlarge
Figure 1
Figure 2 below shows what the brick height map in Figure 1 looks like when subracted from a version of the height map that has been shifted down and to the left. Note that the image gives the impression of a light source emanating from the upper-right.
Click to enlarge Click to enlarge
Figure 2
Bump mapping is particularly effective under changing lighting conditions. In the following animation, the light source is moving in a circular pattern around the brick bump map. Downloading the AVI and setting it to loop during playback gives the best results.
Click to enlarge
Embossing in 3D
In a 3D context, this technique can be used to provide diffuse bumped illumination of polygons. The application offsets the height map relative to itself by adjusting the texture coordinates of the shifted heightmap based on the incident angle of the light source at each polygon vertex independently. The computation of this texture coordinate shift is the key to properly implementing emboss bump mapping.

Ultimately, our goal is to map a 3-space vector (the light vector incident at a particular vertex) to a 2-space vector (the perturbation of the texture coordinate at the vertex). It is convenient to define a coordinate space local to each vertex which uses as its basis the vertex normal, a tangent to the surface which represents the direction of increase of the t texture coordinate and their cross product (aka the binormal). [Blythe] refers to this coordinate space as tangent space. Figure 3 below shows the tangent space vectors for a specific vertex on a sphere with overlayed wireframe.


Figure 3 - Tangent space with normal, tangent and binormal axes
Figure 4 shows the tangent space vectors for a specific vertex on a sphere without the overlayed wireframe.


Figure 4 - Tangent space with normal, tangent and binormal axes
These vectors, expressed in object coordinates, represent a transformation from object space to tangent space.

Once you have defined tangent space for a given vertex, you must transform the light source into this space. This typically involves transforming the light source into object space and then using the tangent space basis vectors to transform from object to tangent space.


Figure 5 - normal, tangent, binormal and light vectors
Once the light vector is expressed in tangent space, the tangent and binormal components of this vector represent the direction of the texture coordinate shift for the emboss effect. The magnitude of the texture coordinate shift must be designed such that it doesn't cause the texture coordinates to offset too far. As an approximation to the derivative, this technique is actually taking a first difference in a particular direction. As such, too large an offset can cause strange aliasing artifacts. "Too far" is defined by the spatial frequency of the height map (i.e. the sharpness of the bump edges). In the pick-nearest-filtering case, this first differencing can be viewed as a classic Roberts filter, commonly used in image processing to detect diagonal edges in image data. Turning on bilinear filtering causes a prefiltering step to happen prior to the Roberts filter. We expect that developers will want to do a lot of experimenting in this area in order to tune the magnitude of the texture coordinate shift to their own style.

Another consideration when computing the texture coordinate shift is whether or not the vertex is facing the light source in the first place. If the vertex is not facing the light (the normal component of the light vector is negative), the texture coordinates should not be shifted at all.

Figures 3 through 5 showed only the bump and Gouraud components of an emboss bump mapped sphere. Naturally, we would also like to include a base color map in this process as shown in Figure 6 below.


Figure 6 - Diffuse bump and Gouraud shaded sphere with a base color map. This is done in a single pass on the Rage128.

Embossing with Hardware
Examining the areas of interest in the Rage128 3D Pixel Pipeline, we see that we can calculate the emboss bump mapping effect using the first two multitexture units and the alpha blending unit. Essentially, the math that we want to perform is as follows:

(height - shifted_height) * diffuse * base

(Some developers may also want to incoporate a 2X multiplier into this equation. This is also supported on the Rage128.)

In order to perform these operations as effectively as possible, we recognize that the height calculations are scalar while the other values are RGB vectors. With this we can exploit the parallel nature of the separate RGB and alpha pipes in the multitexture stages to perform some of the math in parallel. We associate a single texture map with both Texture Lighting Unit 0 and Texture Lighting Unit 1, but we use shifted texture coordinates for fetches in Unit 1. The texture map has its bump map stored as a height map in its alpha channel. Texture Lighting Unit 0 modulates base RGB with diffuse RGB and passes along this result as well as unmodified alpha texels. Texture Lighting Unit 1 leaves RGB untouched while doing a signed addition on the incoming alpha from Unit 0 and the inverted alpha texels from Unit 1. The multiplication of the alpha math with the RGB math is done in the alpha blender with src and dest factors of srcAlpha:Zero. This data flow is illustrated below:

RGB: MODULATE{2X}
alpha: Passthrough


RGB: Passthrough alpha: invert tex1.alpha, ADDSIGNED{2X}


src * srcAlpha + dst * zero

So far, we have outlined the math behind emboss bump mapping without referring to specific API syntax. In the following sections, we will express emboss bump mapping in the two most common APIs developers will be using---Direct3D and OpenGL.

Direct3D
In this Direct3D section we will use the multitexture API introduced in DirectX 6.0. For some background on the multitexturing abstraction, consult the DirectX SDK as well as the article "Multitexturing in DirectX 6" in the September 1998 issue of Game Developer Magazine.

The following code is used in the Rage128 SDK R128BumpMap2 Direct3D sample application and performs the multitexture operations outlined above.

//
// Program stage 0 to modulate diffuse.rgb with tex0.rgb
//

pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE ); // Can also MODULATE2X here
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );

//
// Program Stage 1 to pass through the RGB channels. The texture coordinates
// associated with Stage 1 are the shifted ones. Invert the alpha channel
// and do a signed add.
//

pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1 );
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );
pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_ADDSIGNED); // Can also ADDSIGNED2X here
pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE | D3DTA_COMPLEMENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );

// Use the same texture on both stages
if (pTexture != NULL)
{
pd3dDevice->SetTexture(0, pTexture );
pd3dDevice->SetTexture(1, pTexture );
}

//
// Set up the alpha blender to multiply the alpha channel (monochrome emboss)
// with the src color (lighted texture)
//

pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE );
pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA );
pd3dDevice->SetRenderState( D3DRENDERSTATE_DESTBLEND, D3DBLEND_ZERO );
pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2,
g_pMeshVertices, g_NumVertices,
g_pIndices, g_NumIndices, NULL );
//
// The result is (height - shifted_height) * tex.RGB * diffuse.RGB
//

OpenGL
This code, taken from the Rage128Bump sample application, illustrates the use of the ARB_multitexture and EXT_texture_env_combine extensions to perform emboss bump mapping.

//
// Program a display list to multiply the interpolators with the base texture and pass the texel alpha...
//

glNewList(BUMP_STAGE_0,GL_COMPILE);
{
// RGB
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_EXT);

glTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_RGB_EXT,GL_MODULATE);
glTexEnvf(GL_TEXTURE_ENV,GL_SOURCE0_RGB_EXT,GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_RGB_EXT,GL_SRC_COLOR);
glTexEnvf(GL_TEXTURE_ENV,GL_SOURCE1_RGB_EXT,GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND1_RGB_EXT,GL_SRC_COLOR);

// alpha
glTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_ALPHA_EXT,GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA_EXT,GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_EXT,GL_SRC_ALPHA);

}
glEndList();

//
// Program a display list for texture environment 1
//

glNewList(BUMP_STAGE_1,GL_COMPILE);
{
// RGB
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE_EXT);
glTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_RGB_EXT,GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV,GL_SOURCE0_RGB_EXT,GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_RGB_EXT,GL_SRC_COLOR);

// alpha
glTexEnvf(GL_TEXTURE_ENV,GL_COMBINE_ALPHA_EXT,GL_ADD_SIGNED_EXT);
glTexEnvf(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA_EXT,GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA_EXT,GL_ONE_MINUS_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV,GL_SOURCE1_ALPHA_EXT,GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV,GL_OPERAND1_ALPHA_EXT,GL_SRC_ALPHA);
}
glEndList();



//
// In your draw loop, use the display lists as follows...
//

glActiveTexture(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureName);
glCallList(BUMP_STAGE_0);

glActiveTexture(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureName);
glCallList(BUMP_STAGE_1);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ZERO); // src * srcAlpha + 0

DrawBumpedSphere();

glDisable(GL_BLEND);

References
  • James F. Blinn "Simulation of wrinkled surfaces," Computer Graphics, 123, pp. 286-292, 1978.
  • David Blythe, "Bump Mapping with Textures," SGI OpenGL tutorial, June 1998.
  • John Schlag, "Fast Embossing Effects on Raster Image Data," Graphics Gems IV, Academic Press, Inc. pp. 433-437, 1994
  • Jason L. Mitchell, Michael J. Tatro and Ian Bullard, "Multitexturing in DirectX 6",Game Developer, pp. 33-37, September 1998.
Links
IBM Human Computer Interaction Texture Library
 
 
 


 



©2009 Advanced Micro Devices, Inc.  |  Contact AMD  |  Careers  |  RSS Feeds  |  Terms and Conditions  |  Privacy  |  Trademark information  |  Site Map