Bump Mapping
 
 
 

Bump mapping is a technique that enables a surface to appear wrinkled or dimpled without the need to model these depressions geometrically. Rather, the surface normal is perturbed according to information given in the 'bump map'. This results in variations to the smooth surface.

A 2D bump map is applied to the surface of an object. The space along the surface is called the UV space. The 2D bump map can be thought of as its own surface, where bright areas are hills and dark areas are valleys. On the surface then you can define a gradient, which can be thought of as the 'downhill' or 'uphill' directions. This gradient of values gives you a direction in UV space where you are going to perturb the surface normal. If you have a bump in a texture (a steep hill up for example) then you want to perturb the normal to make it look like there is a steep hill up there. Given the gradient in UV space, you need to figure out what direction in 3D (XYZ) space to perturb the normal that corresponds to that gradient in UV space. The bump basis vectors are used for this purpose. These are the vectors that represent the UVW axes of that texture in 3D. These are unit vectors that can be used to perturb the normal.

To understand how this is done, let's look at some sample code. The method responsible for returning the perturbed normal is Texmap::EvalNormalPerturb(). The code below happens to be from \MAXSDK\SAMPLES\MATERIALS\CHECKER.CPP but all the other 2D textures use a similar approach.

Point3 Checker::EvalNormalPerturb(ShadeContext& sc)
{
   Point3 dPdu, dPdv;
   if (!sc.doMaps) return Point3(0,0,0);
   if (gbufID) sc.SetGBufferID(gbufID);
 
   uvGen->GetBumpDP(sc,dPdu,dPdv);
   Point2 dM = uvGen->EvalDeriv(sc,&mysamp);
 
   returndM.x*dPdu+dM.y*dPdv;
}

The first significant line related to bump mapping is:

 uvGen->GetBumpDP(sc,dPdu,dPdv);

This method of UVGen gets the bump vectors. Developers can also get the bump vectors directly from the method ShadeContext::DPdUVW() although these would not be affected by the UVGen transformations. In our case, since all the coordinates are coming through UVGen, we must use GetBumpDP() since the UVGen has rotated things around -- it has transformed the bump vectors to a new position. Basically, the UVGen has rotated the UV space into another position locally. So again, this method gets the bump basis vectors, which are really the U and V axes in 3D space (unit vectors in the U direction and the V direction, but in 3D space).

The next line computes dM. This is the derivative of the function across the pixel. This is the rate of change of the function in the U direction (dM.x) and the V direction (dM.y). So for example, if this is a flat function, these will both be zero. If the function is increasing in U but is constant in V then the value in the U direction will correspond to how fast it is changing while V will still be zero. Thus, dM can be thought of as the gradient -- how fast things are changing up and down.

 Point2 dM = uvGen->EvalDeriv(sc,&mysamp);

Next, we need to compute the perturbation to the normal. This can be thought of as a small vector that will be added to the end of the existing normal that will move it over a little bit. There are several ways to do this. The common textbook algorithm (Blinn's algorithm for bump mapping) is not used by the 3ds Max textures. Rather, the calculation shown below was found to be simpler and faster with no visual difference. To compute the perturbation to apply to the normal the following code is used:

 returndM.x*dPdu+dM.y*dPdv;

This takes the sum of the U component (dM.x) multiplied by the U basis vector (dPdu) and the V component (dM.y) multiplied by the basis vector in the V direction. This gives the change (perturbation) to the normal as a unit vector.

The result of EvalNormalPerturb(), the perturbation to apply to the surface normal, is used by 3ds Max as follows: Outside the procedural texture, for example in the Standard material, the value returned is added on to the surface normal. Then the normal is re-normalized (made a unit vector again). This altered normal results in the surface appearing 'bumped' when rendered.