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

118 lines
6.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

For each scan line, a z-sorted active polygon list (APL) is maintained.
The x-sorted AEL is stepped through in order. As each new edge is
encountered (that is, as each polygon starts or ends as we move left to
right), the associated polygon is activated and sorted into the APL, as
shown in Figure 66.3, or deactivated and removed from the APL, as shown
in Figure 66.4, for a leading or trailing edge, respectively. If the
nearest polygon has changed (that is, if the new polygon is nearest, or
if the nearest polygon just ended), a span is emitted for the polygon
that just stopped being the nearest, starting at the point where the
polygon first because nearest and ending at the x coordinate of the
current edge, and the current x coordinate is recorded in the polygon
that is now the nearest. This saved coordinate later serves as the start
of the span emitted when the new nearest polygon ceases to be in front.
Don't worry if you didn't follow all of that; the above is just a quick
overview of edge-sorting to help make the rest of this chapter a little
clearer. My thorough discussion of the topic will be in Chapter 67.
The spans that are generated with edge-sorting are exactly the same
spans that ultimately emerge from span-sorting; the difference lies in
the intermediate data structures that are used to sort the spans in the
scene. With edge-sorting, the spans are kept implicit in the edges until
the final set of visible spans is generated, so the sorting, clipping,
and span emission is done as each edge adds or removes a polygon, based
on the span state implied by the edge and the set of active polygons.
With span-sorting, spans are immediately made explicit when each polygon
is rasterized, and those intermediate spans are then sorted and clipped
against other the spans on the scan line to generate the final spans, so
the states of the spans are explicit at all times, and all work is done
directly with spans.
Both span-sorting and edge-sorting work well, and both have been
employed successfully in commercial projects. We've chosen to use
edge-sorting in Quake partly because it seems inherently more efficient,
with excellent horizontal coherence that makes for minimal time spent
sorting, in contrast with the potentially costly sorting into linked
lists that span-sorting can involve. A more important reason, though, is
that with edge-sorting we're able to share edges between adjacent
polygons, and that cuts the work involved in sorting, clipping, and
rasterizing edges nearly in half, while also shrinking the world
database quite a bit due to the sharing.
![](images/66-03.jpg)\
**Figure 66.3**  *Activating a polygon when a leading edge is
encountered in the AEL.*
One final advantage of edge-sorting is that it makes no distinction
between convex and concave polygons. That's not an important
consideration for most graphics engines, but in Quake, edge clipping,
transformation, projection, and sorting have become a major bottleneck,
so we're doing everything we can to get the polygon and edge counts
down, and concave polygons help a lot in that regard. While it's
possible to handle concave polygons with span-sorting, that can involve
significant performance penalties.
![](images/66-04.jpg)\
**Figure 66.4**  *Deactivating a polygon when a trailing edge is
encountered in the AEL.*
Nonetheless, there's no cut-and-dried answer as to which approach is
better. In the end, span-sorting and edge-sorting amount to the same
functionality, and the choice between them is a matter of whatever you
feel most comfortable with. In Chapter 67, I'll go into considerable
detail about edge-sorting, complete with a full implementation. I'm
going the spend the rest of this chapter laying the foundation for
Chapter 67 by discussing sorting keys and 1/z calculation. In the
process, I'm going to have to make a few forward references to aspects
of edge-sorting that I haven't yet covered in detail; my apologies, but
it's unavoidable, and all should become clear by the end of Chapter 67.
### Edge-Sorting Keys {#Heading9}
Now that we know we're going to sort edges, using them to emit spans for
the polygons nearest the viewer, the question becomes: How can we tell
which polygons are nearest? Ideally, we'd just store a sorting key in
each polygon, and whenever a new edge came along, we'd compare its
surface's key to the keys of other currently active polygons, and could
easily tell which polygon was nearest.
That sounds too good to be true, but it is possible. If, for example,
your world database is stored as a BSP tree, with all polygons clipped
into the BSP leaves, then BSP walk order is a valid drawing order. So,
for example, if you walk the BSP back-to-front, assigning each polygon
an incrementally higher key as you reach it, polygons with higher keys
are guaranteed to be in front of polygons with lower keys. This is the
approach Quake used for a while, although a different approach is now
being used, for reasons I'll explain shortly.
If you don't happen to have a BSP or similar data structure handy, or if
you have lots of moving polygons (BSPs don't handle moving polygons very
efficiently), another way to accomplish your objectives would be to sort
all the polygons against one another before drawing the scene, assigning
appropriate keys based on their spatial relationships in viewspace.
Unfortunately, this is generally an extremely slow task, because every
polygon must be compared to every other polygon. There are techniques to
improve the performance of polygon sorts, but I don't know of anyone
who's doing general polygon sorts of complex scenes in realtime on a PC.
An alternative is to sort by z distance from the viewer in screenspace,
an approach that dovetails nicely with the excellent spatial coherence
of edge-sorting. As each new edge is encountered on a scan line, the
corresponding polygon's z distance can be calculated and compared to the
other polygons' distances, and the polygon can be sorted into the APL
accordingly.
Getting z distances can be tricky, however. Remember that we need to be
able to calculate z at any arbitrary point on a polygon, because an edge
may occur and cause its polygon to be sorted into the APL at any point
on the screen. We could calculate z directly from the screen x and y
coordinates and the polygon's plane equation, but unfortunately this
can't be done very quickly, because the z for a plane doesn't vary
linearly in screenspace; however, 1/z *does* vary linearly, so we'll use
that instead. (See Chris Hecker's 1995 series of columns on texture
mapping in *Game Developer* magazine for a discussion of screenspace
linearity and gradients for 1/z.) Another advantage of using 1/z is that
its resolution increases with decreasing distance, meaning that by using
1/z, we'll have better depth resolution for nearby features, where it
matters most.