CS 351: Assignment #8

Z-buffer Rendering

This week you need to implement a set of the z-buffer functions we've
skipped so far. They are interspersed throughout the The graphics system document, so scan
through it to see the various places that need to change.

The major steps are outlined below.

Make sure you are initializing the z-values of your FPixel data
structure to 1.0 when you create or initialize an
image. Double-check your Image function image_reset. While 0.0 is
1/inf, 1.0 is 1/B, where B is the backplane depth. Anything with a
1/z value less than 1.0 should not be drawn.

Make sure your homogeneous normalization functions for various
primitives do not touch the z-value. They should divide only the x
and y values by the homogeneous coordinate, not the z value.

Make sure your Line, and Polyline structures include a z-buffer
flag, and implement the functions that let a programmer set the
flag. Then implement drawing lines using the z-buffer, if the flag is
set (non-zero). The process should be as follows:

Calculate the delta-1/z value for the direction in which the line
will be drawn. (1/z1 - 1/z0)/dx for lines in the 1st and 4th
octants, (1/z1 - 1/z0)/dy for lines in the 2nd and 3rd octants.
Using 1/z instead of z accounts for perspective projection
effects.

Calculate the initial 1/z-value for the line. If the line is being
clipped to the image, adjust the initial 1/z-value
accordingly. (Adjusting the initial 1/z value is a similar process
to adjusting the xIntersect in makeEdgeRec if an edge starts above
the screen.)

In the code section that draws the line color into the image, test
to see if the line's 1/z-value is greater than the current z-buffer
value for that pixel in the image. If the new 1/z value is greater,
draw the pixel and update the image's z-buffer at that pixel.

Make sure to update the current 1/z value each time through the
loop, whether or not you actually draw the pixel into the image.

if the current 1/z value > the current z-buffer value at (row, col)
update the current z-buffer value to the current 1/z value
draw the current color into the image at (row, col)
update the current 1/z value

The above modification to your line code should handle lines,
polylines and framing polygons. You may also want to create a
function for drawing points with a z-buffer test.

Modify your polygon scanline-fill algorithm to make use of a
z-buffer.

In the makeEdgeRec function, use the z values at each end of an
edge to calculate a zIntersect and a dzPerScanline value. Just like
the xIntersect value in the scanline-fill, the zIntersect value
needs to be incremented by dzPerScanline each time you move up a row
(in the updateActiveList function). Inside of the fillScan function,
you will need to compute a dzPerColumn value to increment the
z-value across a scanline. Use 1/z in all of your calculations for
computing zIntersect and dzPerScanline.

We use 1/z for interpolation because, under perspective projection,
z values do not interpolate linearly in x and y. However, 1/z does
interpolate linearly in x and y. So you will want to make your
zIntersect and dzPerScanline values reflect the inverse z values.
Note that you never have to execute an inversion to get the true z
value in order to do hidden surface removal. Instead of testing
whether the z value of a polygon is less than the current z value at
a pixel, just test whether the 1/z value of a polygon is greater
than the current 1/z value at a pixel (so store 1/z values in your
depth map and initialize your depth map to either 0 (infinite
distance) or 1/B, which is 1.0 in canonical view coordinates).

Within the fillScan() routine, for each edge pair calculate a curZ
and dzPerColumn value from the zIntersect field of p1 and the
zIntersect field of p2. Adjust the curZ value as necessary if the
edge starts outside of the image bounds. Then linearly interpolate
these values across the scanline inside your innermost for loop by
incrementing curZ by dzPerColumn at each Pixel.

Within the innermost loop of the scanline-fill algorithm, you will
need to add the Z-buffer logic that compares the current depth value
of a polygon pixel with the value in the Z-buffer for that pixel. If
the polygon's depth is closer (1/z is greater), write the pixel to
the image and update the Z-buffer. If the new value is further away
(1/z smaller), don't modify the image or the Z-buffer.

You will also need to update the innermost loop so it does different
things based on the DrawState shade value. It should use a single
color for ShadeConstant: the color in the DrawState color field. It
should draw a value proportional to the depth value (the actual
z-value should be between 0 and 1) for ShadeDepth.

Note that you will need to pass around a pointer to the DrawState
structure in functions like makeEdgeRec and fillScan in order to
test how to shade the polygon.

Update the processEdgeList function so that it increments the
zIntersect value by dzPerScanline, just like it currently increments
the xIntersect value by dxPerScanline.

Run the simple test program test8a.c. It
should produce an image of a cube. If you shade according to depth,
you'll get something like below. In this case, I inverted the 1/z
depth value and colored the image as below.

(r, g, b) = (1-z, 1-z, 1-z).

Run the example program cubism.c. For the
image sequence shown below I modified the colors of the polygons
based on depth in order to show the zbuffer was working. Given a
DrawState color field of (red, green, blue), you can shade according
to the following.

(r, g, b) = (red * (1-z), green * (1-z), blue * (1-z) )

Create another 3D image demonstrating your Z-buffer is working.

Extensions

Make an example that would be a useful demonstration of the z-buffer concept.

Build a full scanline rendering system that executes all polygons in one pass.

Build an A-buffer system instead of a z-buffer system so you can
do transparency (and shadows).

Make interesting models, like planets and spheres.

Make cool pictures and animation sequences.

Handle aliasing issues at boundaries by using the centroid value
of polygons to determine which of two polygons of similar depth
is really in front.

Writeup

Make a wiki page and give it the label cs351s17project8. Put up your
required and portfolio images along with brief descriptions and
relevant information.

Handin

Put your code on the handin server in a project 8 directory in your Private folder.