CS184 Lecture 39 summary

Visibility: Backface culling

The simplest way to eliminate some invisible surfaces is backface culling. Backfaces are those faces whose normals point away from the viewer, that is

(N . V) < 0

For a solid object, there is always material on the side of a face away from the normal. So when the dot product above is negative, it means that there is material between the face F and the viewer. In a normal situation, that means there should be another forward-face in front of F which obscures it from the viewer. So F should never be visible in this situation.

Exceptions are when the object is not actually solid (it is missing some faces) or when the viewer is inside the object. In the latter case, the viewer should really see total darkness, but in most systems, they will simply see through the object.

Z-Buffer rendering

The Z-buffer is a device that does scan conversion and arranges polygons in depth, displaying at each pixel the polygon closest to the viewer. The input is normally polygons (let's assume triangles for simplicity) which have x and y (screen) coordinates at each vertex and which have been clipped to the screen size. The vertices of a polygon each have a color (RGB value) and a Z (depth) value.

The buffer itself consists of two screen-resolution arrays: the intensity (color) array of say 1024x768 pixels, and the Z array with the same number of Z pixels. RGB values will have between 8 and 32 bits per pixel. The Z values are typically 24 to 48-bit integers.

Scan Conversion

To draw a polygon into the Z-buffer, it must be scan converted. To do this, we determine first the maximum and minimum y-values of the polygon. That determines the range of y-coordinates of all horizontal scan lines that intersect the polygon. For each scan line (starting from the top), you determine first the left and right intersection points of the polygon boundary with the scan line. Let these be P1 = (x1, y) and P2 = (x2, y). Then you linearly interpolate between the vertex colors of the edges containing P1 and P2 to get the colors C1 and C2 at these points. Finally, interpolate the Z coordinates of the neighboring vertices to get Z1 at P1 and Z2 at P2

Then, to fill in the scan line, you start at the pixel (x1, y), and compute its Z-value which is Z1. Compare this value with the Z-value currently in the Z-buffer at pixel (x1, y). If the new value is greater (the new polygon is closer to the viewer), you replace the Z-value at (x1, y) with Z1 and the color value of that pixel with C1.

To draw the next pixel you increment the Z-value by a per-pixel increment, which will be

DZ = (Z2 - Z1) / (x2 - x1)

Compare this Z-value with the z-value currently at pixel (x1+1, y). If the new value is greater, it replaces the old value. We also replace the color value at pixel (x1+1, y). The new color will be C1 + DC, where:

DC = (C2 - C1) / (x2 - x1)

We continue along the scan line, incrementing the Z-values by DZ, incrementing color values by DC, and replacing both whenever a new Z-value is greater than the Z-value currently in the buffer at that pixel.

The net effect of the Z-buffer is to draw the parts of a polygon that are in front of all other polygons. Thus it correctly renders polygons in depth, no matter what order they are inserted into the Z-buffer. The Z-buffer normally runs on a per-polygon basis. That is, it renders an entire polygon before starting on the next one.

Double-Buffering

It takes time to draw the polygons into a Z-buffer. To draw a complete frame, you have to start by clearing the buffer and setting all the color and Z-values to zero. That means every pixels starts black. You can now start drawing polygons, but it may take some time to get them all into the buffer, especially if you are operating near the limit of the buffer in polygons per second.

During the time you are drawing the polygons, the graphics card is sending the buffer contents out a line at a time to the monitor. If you happen to output a line before all the polygons have been drawn onto it (very likely for the top lines), they will contain wrong colors.

To prevent these kinds of problems, double-buffering is usually used. In double buffering, there are two copies of the color array (and one Z-value array). One of the color buffers is designated as the write buffer, while the other is designated as the read buffer. The write buffer is used to do the scan conversion and polygon drawing as described above. The read buffer is used to read the lines out to the monitor. Once an entire frame has been read, we simply switch the read and write buffers. The read buffer now contains a complete new frame, so there is never a problem of bad contents.

Note that in this scheme, the displayed image is actually delayed by one frame from the frame being written into the buffer. This is normally not a big deal, but can become important in delay-sensitive applications such as head-mounted VR systems.