abrash-black-book/67-05.md
2013-12-30 20:26:41 +11:00

65 lines
3.9 KiB
Markdown

#### Implementation Notes {#Heading9}
Finally, a few notes on Listing 67.1. First, you'll notice that although
we clip all polygons to the view frustum in worldspace, we nonetheless
later clamp them to valid screen coordinates before adding them to the
edge list. This catches any cases where arithmetic imprecision results
in clipped polygon vertices that are a bit outside the frustum. I've
only found such imprecision to be significant at very small z distances,
so clamping would probably be unnecessary if there were a near clip
plane, and might not even be needed in Listing 67.1, because of the
slight nudge inward that we give the frustum planes, as described in
Chapter 65. However, my experience has consistently been that relying on
worldspace or viewspace clipping to produce valid screen coordinates 100
percent of the time leads to sporadic and hard-to-debug errors.
There is no separate routine to clear the background in Listing 67.1.
Instead, a special background surface at an effectively infinite
distance is added, so whenever no polygons are active the background
color is drawn. If desired, it's a simple matter to flag the background
surface and draw the background specially. For example, the background
could be drawn as a starfield or a cloudy sky.
The edge-processing code in Listing 67.1 is fully capable of handling
concave polygons as easily as convex polygons, and can handle an
arbitrary number of vertices per polygon, as well. One change is needed
for the latter case: Storage for the maximum number of vertices per
polygon must be allocated in the polygon structures. In a fully polished
implementation, vertices would be linked together or pointed to, and
would be dynamically allocated from a vertex pool, so each polygon
wouldn't have to contain enough space for the maximum possible number of
vertices.
Each surface has a field named **state**, which is incremented when a
leading edge for that surface is encountered, and decremented when a
trailing edge is reached. A surface is activated by a leading edge only
if **state** increments to 1, and is deactivated by a trailing edge only
if **state** decrements to 0. This is another guard against arithmetic
problems, in this case quantization during the conversion of vertex
coordinates from floating point to fixed point. Due to this conversion,
it is possible, although rare, for a polygon that is viewed nearly
edge-on to have a trailing edge that occurs slightly *before* the
corresponding leading edge, and the span-generation code will behave
badly if it tries to emit a span for a surface that hasn't yet started.
It would help performance if this sort of fix-up could be eliminated by
careful arithmetic, but I haven't yet found a way to do so for
1/z-sorted spans.
Lastly, as discussed in Chapter 66, Listing 67.1 uses the gradients for
1/z with respect to changes in screen x and y to calculate 1/z for
active surfaces each time a leading edge needs to be sorted into the
surface stack. The natural origin for gradient calculations is the
center of the screen, which is (x,y) coordinate (0,0) in viewspace.
However, when the gradients are calculated in **AddPolygonEdges()**, the
origin value is calculated at the upper-left corner of the screen. This
is done so that screen x and y coordinates can be used directly to
calculate 1/z, with no need to adjust the coordinates to be relative to
the center of the screen. Also, the screen gradients grow more extreme
as a polygon is viewed closer to edge-on. In order to keep the gradient
calculations from becoming meaningless or generating errors, a small
epsilon is applied to backface culling, so that polygons that are very
nearly edge-on are culled. This calculation would be more accurate if it
were based directly on the viewing angle, rather than on the dot product
of a viewing ray to the polygon with the polygon normal, but that would
require a square root, and in my experience the epsilon used in Listing
67.1 works fine.