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

119 lines
6.4 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.

And then yet another crop of sorting errors popped up.
We could have fixed those errors too; we'll take a quick look at how to
deal with such cases shortly. However, like the sixth rocket stage, the
fixes would have made Quake *slower* than it had been with BSP sorting.
So we gave up and went back to BSP order, and now the code is simpler
and sorting works reliably. It's too bad our experiment didn't work out,
but it wasn't wasted time because in trying what we did we learned quite
a bit. In particular, we learned that the information provided by a
simple, reliable world ordering mechanism, such as a BSP tree, can do
more good than is immediately apparent, in terms of both performance and
solid code.
Nonetheless, sorting on 1/z can be a valuable tool, used in the right
context; drawing a Quake world just doesn't happen to be such a case. In
fact, sorting on 1/z is how we're now handling the sorting of multiple
BSP models that lie within the same world leaf in Quake. In this case,
we don't have the option of using BSP order (because we're drawing
multiple independent trees), so we've set restrictions on the BSP models
to avoid running into the types of 1/z sorting errors we encountered
drawing the Quake world. Next, we'll look at another application in
which sorting on 1/z is quite useful, one where objects move freely
through space. As is so often the case in 3-D, there is no one "right"
technique, but rather a great many different techniques, each one handy
in the right situations. Often, a combination of techniques is
beneficial; for example, the combination in Quake of BSP sorting for the
world and 1/z sorting for BSP models in the same world leaf.
For the remainder of this chapter, I'm going to look at the three main
types of 1/z span sorting, then discuss a sample 3-D app built around
1/z span sorting.
### Types of 1/z Span Sorting {#Heading4}
As a quick refresher: With 1/z span sorting, all the polygons in a scene
are treated as sets of screenspace pixel spans, and 1/z (where z is
distance from the viewpoint in viewspace, as measured along the
viewplane normal) is used to sort the spans so that the nearest span
overlapping each pixel is drawn. As I discussed in Chapter 66, in the
sample program we're actually going to do all our sorting with polygon
edges, which represent spans in an implicit form.
There are three types of 1/z span sorting, each requiring a different
implementation. In order of increasing speed and decreasing complexity,
they are: intersecting, abutting, and independent. (These are names of
my own devising; I haven't come across any standard nomenclature in the
literature.)
#### Intersecting Span Sorting {#Heading5}
Intersecting span sorting occurs when polygons can interpenetrate. Thus,
two spans may cross such that part of each span is visible, in which
case the spans have to be split and drawn appropriately, as shown in
Figure 67.1.
![](images/67-01.jpg)\
**Figure 67.1**  *Intersecting span sorting.*
Intersecting is the slowest and most complicated type of span sorting,
because it is necessary to compare 1/z values at two points in order to
detect interpenetration, and additional work must be done to split the
spans as necessary. Thus, although intersecting span sorting certainly
works, it's not the first choice for performance.
#### Abutting Span Sorting {#Heading6}
Abutting span sorting occurs when polygons that are not part of a
continuous surface can butt up against one another, but don't
interpenetrate, as shown in Figure 67.2. This is the sorting used in
Quake, where objects like doors often abut walls and floors, and turns
out to be more complicated than you might think. The problem is that
when an abutting polygon starts on a given scan line, as with polygon B
in Figure 67.2, it starts at exactly the same 1/z value as the polygon
it abuts, in this case, polygon A, so additional sorting is needed when
these ties happen. Of course, the two-point sorting used for
intersecting polygons would work, but we'd like to find something
faster.
As it turns out, the additional sorting for abutting polygons is
actually quite simple; whichever polygon has a greater 1/z gradient with
respect to screen x (that is, whichever polygon is heading fastest
toward the viewer along the scan line) is the front one. The hard part
is identifying *when* ties—that is, abutting polygons—occur; due to
floating-point imprecision, as well as fixed-point edge-stepping
imprecision that can move an edge slightly on the screen, calculations
of 1/z from the combination of screen coordinates and 1/z gradients (as
discussed last time) can be slightly off, so most tie cases will show up
as near matches, not exact matches. This imprecision makes it necessary
to perform two comparisons, one with an adjust-up by a small epsilon and
one with an adjust-down, creating a range in which near-matches are
considered matches. Fine-tuning this epsilon to catch all ties, without
falsely reporting close-but-not-abutting edges as ties, proved to be
troublesome in Quake, and the epsilon calculations and extra comparisons
slowed things down.
![](images/67-02.jpg)\
**Figure 67.2**  *Abutting span sorting.*
I do think that abutting 1/z span sorting could have been made reliable
enough for production use in Quake, were it not that we share edges
between adjacent polygons in Quake, so that the world is a large polygon
mesh. When a polygon ends and is followed by an adjacent polygon that
shares the edge that just ended, we simply assume that the adjacent
polygon sorts relative to other active polygons in the same place as the
one that ended (because the mesh is continuous and there's no
interpenetration), rather than doing a 1/z sort from scratch. This
speeds things up by saving a lot of sorting, but it means that if there
is a sorting error, a whole string of adjacent polygons can be sorted
incorrectly, pulled in by the one missorted polygon. Missorting is a
very real hazard when a polygon is very nearly perpendicular to the
screen, so that the 1/z calculations push the limits of numeric
precision, especially in single-precision floating point.
Many caching schemes are possible with abutting span sorting, because
any given pair of polygons, being noninterpenetrating, will sort in the
same order throughout a scene. However, in Quake at least, the benefits
of caching sort results were outweighed by the additional overhead of
maintaining the caching information, and every caching variant we tried
actually slowed Quake down.