Procedural Bump Mapping On Programmable Graphics Hardware

Eric Chan
September 12, 2002


Overview

This page describes an implementation of RenderMan's calculatenormal() function using programmable graphics hardware and some of the resulting problems.

Procedural Bump Mapping

I've been interested in procedural shading lately and was wondering if we could do true procedural bump mapping in the fragment programs (pixel shaders) of graphics hardware. In RenderMan, simple procedural bump mapping often takes the following form:

  point PP;
  float F = /* some bump function goes here */

  PP = P + F * normalize(N);
  Nf = calculatenormal(PP);

This code snippet computes a new position PP by displacing it in the direction of the normal by a certain amount F. The value of F represents the bump height and is determined by some mathematical function.

Recomputing the normal

The key here is that we need to compute the modified normal Nf at the new surface position. This is easily accomplished in RenderMan using the calculatenormal() function, which takes the cross product of the surface tangent vectors dP/du and dP/dv.

Implementing this function on programmable graphics hardware is tricky, since each vertex and fragment is processed independently, i.e. we do not have neighbor / connectivity information. However, we can make use of the screen-space partial derivative operators (DDX, DDY) in NVIDIA NV30 fragment programs. We first express dP/du and dP/dv in terms of screen-space x and y. Given this system of equations,

  dP/dx = dP/du * du/dx + dP/dv * dv/dx
  dP/dy = dP/du * du/dy + dP/dv * dv/dy

we solve for dP/du and dP/dv and obtain

  dP/du = (dP/dx * dv/dy - dP/dy * dv/dx) / k
  dP/dv = (dP/dy * du/dx - dP/dx * du/dy) / k

where k = du/dx * dv/dy - dv/dx * du/dy. We have expressed dP/du and dP/dv in terms of partial derivatives with respect to screen-space x and y. Each of these partial derivatives can be evaluated using the NV30 DDX or DDY fragment program instructions.

Finally, we take the cross product of dP/du and dP/dv and normalize the result. Note that while the value k above is needed to obtain correct values of dP/du and dP/dv, it is not needed to compute the normal!

Problems

While the mathematical formulation above is correct, there are two practical problems with this approach.

The first problem arises because of inaccurate partial derivative estimation. To obtain an accurate normal, we require accurate estimates of dP/du and dP/dv. This assumes that du and dv are small (infinitesimally small). To compute dP/du and dP/dv, we must first compute the partials of u and v with respect to x and y. However, a small change in x and y (pixel-size) may result in a large change in u and/or v. This leads to an inaccurate estimation of the partial derivatives of P with respect to u and/or v, which results in a "noisy" normal with popping artifacts.

The second problem is that we can compute only face normals. In other words, the resulting normals from our implementation are not smoothly interpolated across the primitive. The reason is that we are computing the normals by taking the cross product of two tangent vectors. These tangent vectors are not the true surface tangent vectors, but instead are obtained indirectly from P, u and v linearly interpolated across the face of a triangle. Hence dP/du and dP/dv lie in the plane of the triangle, so all normals computed for all points in a given triangle will be the same. Thus the result is a face normal.

Note that the second problem is a non-issue for the REYES architecture (used in Pixar's prman) because surfaces are finely diced until each micropolygon is less than a pixel on a side, so normals are effectively recalculated on a per-pixel basis, which leads to a smoother appearance.

Images

The images below show the object-space normals for spheres.

The normals for the spheres in the top row are computed using our calculatenormal() implementation. Notice that we can compute only face normals, and the overall appearance becomes smoother only if we dice the sphere more finely. In contrast, the normals for the spheres in the bottom row are obtained from the true vertex surface normals by linear interpolation during triangle rasterization.

Dicing: 1 Dicing: 5 Dicing: 10


Last updated: September 12, 2002