CHAPTER 7 - Implementation 2

Objectives <next>


Polygon Clipping (can use C- S or L- B for polygons on an edge by edge basis)


Tessellation and Convexity


Clipping as a Black Box


Pipeline Clipping of Line Segments (Sutherland and Hodgeman Algorithm)


Pipeline Clipping of Polygons


Bounding Boxes


Bounding boxes


Clipping and Visibility


Hidden Surface Removal


Painters Algorithm

• The painter's algorithm requires no additional buffer space, just the frame buffer.
• All objects are sorted in z from back to front => there are some tricky tests required if polygons overlap.
• Objects are then written into the frame buffer from back to front, i.e. the way a painter paints objects on a canvas.
• A simple, easy to implement algorithm but terribly inefficient since all front facing polygons are processed whether visible or not.

Depth Sort

Easy Cases

Hard Cases


Image Space Approach


Z-Buffer Algorithm Catmull 1974(compatible with object space or image space approaches - used by OpenGL)

The z-buffer or depth buffer algorithm is probably the most popularly implemented hidden surface elimination algorithm. It is intuitively simple to understand and quite easy to implement. The algorithm is an image precision algorithm developed for raster systems. OpenGL performs visibility testing using a depth buffer.

We know that color information for each pixel to be displayed is stored in a frame buffer. The z-buffer has the same number of entries as the frame buffer, but stores Z values for each pixel.

• The z-buffer entries are initialized with the z value of the back clipping plane of the specified view volume.
• The largest z value that can be stored in the buffer is the z value of the front clipping plane.
• The frame buffer entries are initialized to the screen background color.


Scan conversion of fragments proceeds (the 2D case is yet to be discussed)
• but now the z value of each point x, y is compared to the z value stored in the buffer for that point.
• If the fragment's z value is >= the buffer value then the buffer is updated with the fragment's z value and the frame buffer is updated with the fragment's color.
• Otherwise no updates take place.

To determine the relevant z values across a polygon span that lies along a scan line one can use linear interpolation (using the z values of the spans two endpoints). Linear interpolation can also be used over the set of scanlines intersected by the polygon's left and right edge.

The scan conversion algorithm sorts polygon spans using a y-bucket sort:
• All spans are sorted in y first.
• Each y-bucket(i) holds the set of spans that become active on scanline(i).
• The spans within each bucket are sorted in x.
• If we additionally perform a rough sort in z the algorithm is quite efficient.
• The z-sort is performed on objects in a front to back order so that front objects will be drawn into the frame buffer first.
• Thus the time consuming shading computation will never need to be performed for objects obscured by those closer to the observer.

The z-buffer algorithm is typically implemented in hardware.

The z-buffer can also be saved, along with the frame buffer, and then used to merge in other objects => no need to recompute the whole image for partial scene updates.


Efficiency (Linear Interpolation of depth values vs. using the polygon's plane equation)


Scan-Line Algorithm

The scan-line algorithms for lines and circles (Bresenham's midpoint algorithms) have been discussed previously. This section deals with polygons only.


Filling Rectangles

In general, to fill a rect, the algorithm must determine which scan lines intersect the rect and move thru the scanlines sequentially filling in spans of pixels within the rect from left to right.

Spans take advantage of several types of coherence that frequently occur within a scene to be displayed


• Spatial Coherence : pixels tend to remain the same within a span or from scanline to scanline.
• Span coherence : for a solidly colored polygon, all pixels on the span have the same color.
• Scanline coherence : for a solidly colored polygon, all scan lines that intersect the polygon are identical.
• Edge Coherence : for general polygons, the intersection of an edge with a scan line tends to change only incrementally in x, y.


Being able to write spans to the frame buffer is considerably more efficient than writing on a by pixel basis.
The general pseudo-code for rectangles is:

for (y from ymin to ymax of rectangle)

for( x from xmin to xmax)

WritePixel(x,y,color)

A problem arises in this solution if rects ( or polygons in general) share a boundary edge. In many cases it makes no difference if the edge is drawn twice (never efficient). But if XOR mode is used then pixels could disappear or be strangely colored. Thus for shared edges it becomes necessary to identify the polygon which owns an edge so it is only drawn once.

One rule that is frequently used for a boundary pixel : the pixel is not considered part of the primitive if the half plane defined by the edge and containing the primitive lies below or to the left of the edge.

Thus pixels on the left and bottom edge are drawn, while those on the right and top are not. A shared vertical edge belongs to the rightmost of two sharing polygons.


Still problems with this rule : bottom left pixel drawn twice (special rule), each span is missing rightmost pixel, each polygon is missing top row.


Filling Polygons

The general polygon scan-conversion algorithm handles convex, concave, self-intersecting polygons, or polygons with interior holes.

Span extrema of a polygon are calculated using an incremental algorithm. The incremental algorithm computes the intersection of a scan line and a polygonal edge based on the intersection (if any) of the polygon with the previous scan line. To compute the intersections analytically would be inefficient. Look at below figure to visualize spans and span extrema.

Once the spans on a scanline are identified, it is necessary to determine which spans are inside the polygon (to be drawn) and which are outside the polygon (not to be drawn). This is a three step process :

  1. Find the intersection of scanline with all polygon edges.
  2. Sort the intersections by increasing x coordinate.
  3. Fill in pixels between pairs of intersections that are inside the polygon. Use the odd-parity rule to do so. Parity is initially set to even for each scanline. When a span intersection is encountered invert the parity bit. Draw only when parity is odd. Look at figure and discuss odd-parity rule.

Steps 1) and 2) discussed later. Step 3 is elaborated below.

3.1 If the intersection is a fractional x value how do we determine which pixel on either side of the intersection is interior to the polygon? We can apply the odd-parity rule here as - if approaching the fractional intersection from the left and we are inside the polygon then round down, if we are outside the polygon then round up. This ensures that the resulting integer x coordinate is always interior to the polygon.

3.2 How do we deal with the special case of intersections at an integer pixel coordinate? Use the same rule as applied to shared edges of rectangles - if the coordinate is a left edge it is interior to the polygon, if it is a right edge then it is outside the polygon.

3.3 How do we deal with the special case in 3.2) of shared vertices? Use the rule that all edges and spans are treated as intervals that are closed at their minimum value and open at their maximum value. To visualize this look at the figure and imagine a scanline that intersects the polygon at A. We identify two intersections, one with line FA at its ymin value and one at AB at its ymax value. If we allow these two intersections to define a span (of one pixel) then our odd-parity rule would not work. Parity would be odd for the FA intersection (so draw pixel) but becomes even for the AB intersection and the remaining pixels along the span within the polygon are not drawn.

Thus, when encountering a shared vertex, count the ymin vertex as an intersection in the odd-parity rule. Do not count the ymax vertex as an intersection in the odd parity rule.

3.4 How do we deal with the special case in 3.2) of a horizontal edge? We use the same rule as for filling rectangles - bottom edges are draw, top edges are not. Look at simple example using a rectangle.

What happens when a scanline intersects only a vertex of the polygon? at a local minima? at a local maxima?


Horizontal Edges

As stated previously, when considering horizontal edges, we do not count their vertex intersection (for the odd-parity rule) with the scanline. Use figure to visualize.

Examine horizontal edges AB, CD whose spans start on lines JA, IJ and whose spans end on CB, and DE. The above rules work nicely for this example.

The algorithm is not perfect, it does omit pixels and does not totally avoid redrawing shared vertices.


Slivers

When displaying multiple polygons, it is possible that the polygons may be sufficiently close to create a polygonal sliver. Slivers are very small polygons whose interior area does not contain a distinct span for each scanline. Because of the size of the polygon and the left, bottom edge rule many scanlines may contain only a single pixel or none at all. This is an example of an aliasing artifact.


Edge Coherence for Scanline Algorithm that Fills Polygons (~openGL algorithm but defined only for convex polygons)

Step 1 requires calculating intersections of edges and the scanline. We don't want to do this analytically because of the inefficiency. Instead we can take advantage of edge coherence - the fact that once an edge intersects a scanline it probably intersects adjacent scanlines. Edge coherence occurs along an edge for as many scanlines as intersect the edge. We also want to identify only those edges that intersect a scanline...we don't want to test every edge in the polygon for intersection.

If we examine the endpoints of an edge it is possible to identify the ymax and ymin of the edge. This gives us the interval of scanlines which will intersect the edge. It also gives us the starting scanline and the ending scanline of the interval.

Assume we consider only left edges with m > 1. Right edges and other slopes are handled by similar more complicated arguments. Vertical edges are a special case while horizontal edges are handled by the span rules discussed earlier.

Thus x intersections along a scanline can be computed based on the old x intersection (as in the midpoint scan conversion algorithm) by using

i+1 = xi + 1/m where m is the slope of the line. As y is incremented thru the scanlines, the x intersection increases by 1/m.

Because x increases by 1/m, the new x intersection will result in an integer + fractional part. ie. if slope = 5/2 and start x at 2, then the sequence is 2, 2+2/5, 2+4/5, 2+6/5 = 3+1/5, ...

When the fractional part of x = 0 then we draw (x, y) which lies on the line. When the fractional part is non-zero we need to round up to produce an x value that lies inside the polygon. When the fractional part becomes > or = 1 then we need to increment x and subtract 1 from the fractional part. We also need to move x one pixel to the right by rounding for the > case, rounding doesn't affect the = case.

We can avoid the fractions by noting that the denominator of the fractional part = ymax - ymin (defined by the y endpoints of the edge). We can just keep track of the numerator thru successive iterations. When the numerator overflows the denominator then increment x and decrement the numerator by the denominator.

The algorithm needs a data structure to keep track of active edges, called the AET, or active edge table. Active edges in the table are sorted by their x intersection value (in increasing order) so that spans and span extrema are easily identified.

Lets say the algorithm is currently at scanline yi.

In order to identify new active edges, a global edge table (ET) is managed. Edges in the table are sorted by their ymin value. The ET is built using a y-bucket sort : number of entries in ET is equal to number of scanlines (the y buckets). Each y-bucket points to a list of edge entries. Thus bucket i represents scanline i and points to the list of edges that become active on scanline i. An edge entry should minimally contain the ymax, xmin, and slope of the edge (and a flag indicating a left or right edge). Actually more complicated for multiple polygons. Within the list, edge entries are sorted by xmin in increasing order. Why isn't the xmax coordinate stored?

The steps of the algorithm follow :

1. Set y = to smallest non-empty cell of ET.

2. Initialize AET to empty.

3. Repeat until ET and AET are empty.

3.1 Move entries from ET bucket y to AET (newly active edges).

3.2 Remove from AET all edge entries whose ymax < y (inactive edges). Sort AET on x (easy because ET is presorted, O(n)).

3.3 Fill pixels on scanline y using pairs of x coordinates from AET (by computing spans and using rules discussed previously).

3.4 Increment y.

3.5 For each non-vertical edge, update x for new y.

Note that for the general case of convex and concave polygons, scanline conversion is complicated by the fact that multiple spans may exist for one polygon. We can simplify scanline conversion if all polgons are triangles. Why?

A triangle will have only a single span on any scanline. Any polygon can be constructed from a mesh of triangles and simple convex polygons (easy for convex, harder for concave) through a process called tessellation. Many scanline algorithms require or apply a preprocessing tessellation step to remove many of the special cases we have seen in this algorithm.


Visibility Testing


Simple Example



BSP Tree


<next>