Due midnight 10 November 2011
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.
- Make sure you are initializing the z-values of your FPixel data structure to 1.0 when you create or initialize an image. Add the new Image function Image_reset.
- Modify your homogeneous normalization functions so they 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 them using the z-buffer, if the flag is
set. The process should be as follows:
- Calculate the delta-1/z value for the direction in which the line will be drawn. (1/dz)/dx for lines in the 1st and 4th octants, (1/dz)/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, 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 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.
Modify your polygon scanline-fill algorithm to make use of a
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 draw a single color for ShadeConstant. 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 your UpdateEdgeList function so that it increments the zIntersect value by dzPerScanline, just like it currently increments the dxPerScanline.
Run the simple test program test7a.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), shade according to the
(r, g, b) = (red * (1-z), green * (1-z), blue * (1-z) )
- Create another 3D image demonstrating your Z-buffer is working. Each person in a group should create their own image.
- 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.
Make a child wiki page from your main CS 351 wiki page. Give it the label cs351f11project7. Please follow the format below.
- Abstract: 200 word description (at most) of what you did and a picture to go along with it.
- Description of the task, in your own words. Be brief, but write it as though explaining it to a fellow student not in the course.
- Description of how you solved the task, including any key equations or algorithms. You should also include algorithms or descriptions of what you did for any extensions. Include pictures here.
- More pictures. Please put a caption on each picture explaining something about it. If you have nothing else, give it a name and indicate whose picture it is.
- Summary of what you learned.
Put your code files in your private handin folder as a single zip or tar file. Put your writeup on the wiki. Give it the label cs351f11project7.