Programming assignment #2 - Polygon scan converter

CS 248 - Introduction to Computer Graphics
Autumn Quarter, 1999
Marc Levoy
Handout #7


Due Tuesday, October 26 by 5:00pm


Your assignment is to write a scan converter for rasterizing triangles, specified either interactively or from a text file. You will also implement a postprocess that redraws a previously specified set of triangles using supersampling and averaging down to reduce aliasing artifacts.

Required functionality

  1. Triangle scan conversion. Allow the user to interactively specify a triangle whose vertices lie at integer pixel coordinates on the canvas. Provide visual feedback of each vertex as it is placed. Once the third vertex has been placed, erase the feedback and scan convert the triangle. Draw it filled, not as an outline. Allow any number of triangles to be specified and drawn in this way.

  2. Clipping. Your scan converter should discard a polygon if any of its vertices lie outside the boundaries of the canvas. You do not need to implement full polygon clipping as covered in section 3.14 of the textbook.

  3. Discarding degenerate triangles. Your scan converter should test for and discard degenerate triangles (two coincident vertices or three colinear vertices). Print a message on the terminal for each triangle you discard so that we know when you detect one.

  4. Rapid redrawing. Your interface should contain a button labeled "Redraw." When pushed, you should erase the canvas and scan convert all previously specified triangles as fast as possible, updating the canvas only once, after all triangles have been drawn. You will be graded on speed. Note that scan conversion of triangles should be "from scratch" each time the Redraw button is pushed; no fair caching spans or other information from previous scan conversions.

  5. Meshing. The triangles you draw should mesh perfectly without holes or overlaps. Satisfying this condition will require some care. In order to verify that your algorithm behaves correctly in this regard, provide the user with the option to draw all triangles in a single color using XOR (exclusive or). The effect of drawing with XOR is that each time a pixel in the canvas is touched during scan conversion, its color is bitwise-inverted. If presented with a set of triangles that should mesh, drawing them with XOR will present obvious visible feedback of any meshing errors.

  6. Supersampling and averaging down. Your menu should contain a button labeled "Antialias." When pushed, you should erase the canvas, step through a pattern of subpixel positions, shift the vertices of all triangles as appropriate for that subpixel position, scan convert them at the shifted position, and combine the results into the canvas using the progressive refinement variant of the accumulation buffer algorithm, as summarized in Appendix A of this handout.

    Use any pattern of subpixel positions and any corresponding weights (summing to 1) that you like. In your README file you should describe your solution and characterize (in informal terms) the quality of antialiasing that it produces. Some patterns work well, and some do not, as we shall discuss in class. Allow slider control over the number of samples per pixel (variable s in appendix A). In order to verify that your algorithm behaves correctly, your user interface should provide an option that, when selected, automatically assigns a unique 24-bit color to each triangle (random colors are fine). When this option is selected, you should not draw triangles with XOR. You can use this option yourself to make sure that pixels along polygon edges and at polygon edge crossings (in case of overlapping triangles) contain reasonable antialiasing.

  7. Reading triangle coordinates from a file. In addition to an interactive interface, your program should be capable of reading triangle coordinates from a text file, so that we can test it with "grader data." This file will contain one text line per triangle. Each line will consist of three pairs of floating point numbers that represent the X,Y-coordinates of the three vertices of each triangle, followed by three integers that represent the color of the triangle. For example, the file
    10 20 15 25 8.5 16.5 200 150 100
    tells your program to draw one triangle with vertices (10,20), (15,25), and (8.5,16.5), and with RGB color (200,150,100). Treat your canvas as occupying the all-positive quadrant in X and Y, and treat color components as being between 0 and 255. These colors should be used during drawing only if neither the XOR nor the random coloring options (described earlier) are enabled. To be sure you can read our format, we have placed some sample files in /usr/class/cs248/data/triangles.

    We intend to measure the speed of your scan converter by running it with a large file of triangles and timing your "Redraw" function using a stopwatch. Your program should handle up to 100,000 triangles. We will measure your redraw time with and without antialiasing enabled. Since your rendering time with antialiasing enabled will include a component due to updating the accumulation buffer, and all your redraw times will include a component due to displaying the result, you should provide some means to control your active drawing area within the canvas. This control can be either interactive or at program startup. In any case, it must be capable of specifying a drawing area of 400 x 400 pixels. We will use this size for our speed measurements.

A few hints

Submission requirements

Unlike the first assignment, this one will not be graded face-to-face. Instead, you will submit the program to us online, and we will run it ourselves later. Specifically, by 5:00pm on Tuesday, October 26, you should submit an on-line executable program, a commented copy of your source code, and a README describing the functionality implemented. Your README file should describe how to run your program, including any command line arguments required. It should also describe your supersampling solution, as outlined earlier. As in the previous assignment, one screenful is enough, and if you did something especially clever, tell us about it. To submit your program, source code, and README, simply change the current directory to your assignment #2 directory and run the submit script, as you did for assignment #1.

The assignment will be graded on correctness (50 points), efficiency (30 points), programming style (10 points), and the elegance of your user interface (10 points). Remember - your executable must run on the Sweet Hall SGIs. If we can't get your program to run, or if it crashes, or if some of its functionality seems missing, your grade will suffer. Make sure your program runs reliably!

Extra credit

If you have completed the assignment you can earn extra credit by adding bells and whistles. Here are some suggestions. Feel free to make up your own.

  1. Draw rubber-band lines during interactive specification of each triangle. There are many reasonable designs for such an interface. Experiment a bit.

  2. Implement pattern fill (section 3.8 of the textbook). Provide an interface that allows the user to select a rectangle from an image for use as the generator for a repeating pattern.

  3. Implement polygon scan conversion for interactively specified concave polygons having an arbitrary number of sides. Decomposing complicated polygons into triangles is an acceptable strategy. For more fun (and credit), make your algorithm work correctly for polygons whose edges touch or cross (creating bow ties, holes, disjoint areas, etc.) as enumerated in class. You needn't support polygons with holes when those holes are defined by a separate set of edges.

  4. Allow interactive specification of an arbitrarily shaped ellipse and implement a scan converter for it. Draw it filled. We suggest using button-down to specify one corner of the bounding box for the ellipse and button-up (after dragging) to specify the diagonally opposite corner. Consider rubber-band-style drawing of the ellipse during dragging for enhanced user feedback.

  5. Allow the user to interactively move triangle vertices. Also allow the user to move the triangle itself by pointing to its interior. Use gravity to facilitate selection of triangles and vertices. Provide reasonable visual feedback during selection and moving. Allow interactive scaling and rotation of triangles around an interactively selected point in the canvas.

  6. Implement an interface that allows input of a curve as a sequence of connected line segments. We suggest using button-down to initiate a new curve and button-up to terminate it. Line segments can be defined to connect successive mouse positions or successive mouse positions once their distance apart exceeds a specified threshold. If the threshold distance is large, you might want to provide rubber-banding of the final segment for enhanced user feedback. Experiment a bit. Segments should be drawn using one or more polygons. Allow slider control over curve thickness. Pay careful attention to the endpoints of each segment. See page 963 of the textbook for ideas. Avoid drawing pixels more than once. See page 949 for solutions to this problem.

  7. Implement unweighted area sampling as described in section 3.17.2 of the textbook as a user-selectable alternative to accumulation buffer antialiasing. For more fun, implement weighted area sampling as described in section 3.17.3. You may assume (for these items only) that your triangles do not overlap.

  8. Allow a large number of input file names on the command line, where each input file is one frame of an animation. Play the animation by rasterizing the files on-the-fly. Provide VCR-like controls: play, pause, step forward, step backward, loop, etc. Find or algorithmically create an animation sequence. Document it in your README file so we can enjoy it too!

Appendix A: the accumulation buffer algorithm

Let "canvas" and "temp" be 24-bit pixel arrays. Let "triangle color" be a 24-bit color unique to each triangle.

1   clear canvas to black
2   for (i=1; i<=s; i++)  (for s subpixel positions)
3      clear temp to black
4      for each triangle
5         translate triangle vertices for this subpixel position
6         for each pixel in triangle (using your scan converter)
7            temp color <-- triangle color
8      for each pixel in canvas
9         canvas color <-- ((i-1)/i)*canvas color + 1/i*temp color
10     display changed portion of canvas

Line 7 causes later triangles to occlude earlier triangles in temp. This "painter's algorithm" occlusion ordering is in lieu of a hidden-surface algorithm, which you will learn about later in the course.

Line 9 implements a box filter, i.e. all subpixel positions are given equal weight. By modifying this interpolation formula to include a weight that varies with subpixel position relative to the center of the filter kernel, you can implement other reconstruction filters.

Line 10 causes the screen to be updated after all triangles have been rendered at each subpixel position, thus implementing a kind of progressive refinement. This is the required functionality. If you want to see your scan converter run faster, provide a check box that allows the user to disable display until the end of the scan conversion process.

If your progressive refinement is working correctly, what you should see is: after the first supersample location -> a canvas with all triangles at full intensity but with jaggies, after the second -> an average of two full-intensity images that are slightly spatially offset from one another, producing a bit of blurring, and after all s positions -> a nicely antialiased image. I'll show you some examples of antialiased images. Meanwhile, look at figure 3.56 in the text. The method they use to produce that figure is different (that is prefiltering, not supersampling and averaging down), but the visual effects are similar.

One final hint. For large numbers of samples (i.e. large values of s), line 9 will yield significant quantization artifacts. In these situations, you may need to use canvas arrays of 16-bit or 32-bit integers (one each for R,G, and B), sum your temp images into this array without first scaling each contribution (i.e. don't scale by 1/i), then normalize into a third array of 8-bit per color component pixels for display on the SGI. I don't require that you implement this, but if you do, you will have the pleasure of being able to progressively refine your images even for large s.


[email protected]
Copyright © 1999 Marc Levoy
Last update: October 15, 1999 07:22:33 PM