CS 351: Assignment #8

Title image Spring 2017

Z-buffer Rendering

Due 13 April 2017

For this assignment you'll implement z-buffer rendering to do hidden surface removal.


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.

  1. 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.
  2. 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.
  3. 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
  4. 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.
  5. 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.

  6. 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).

  7. 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) )

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



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


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