110 lines
6.3 KiB
Markdown
110 lines
6.3 KiB
Markdown
Chapter 67\
|
|
Sorted Spans in Action {#Heading1}
|
|
-----------------------
|
|
|
|
### Implementing Independent Span Sorting for Rendering without Overdraw {#Heading2}
|
|
|
|
In Chapter 66, we dove headlong into the intricacies of hidden surface
|
|
removal by way of z-sorted (actually, 1/z-sorted) spans. At the end of
|
|
that chapter, I noted that we were currently using 1/z-sorted spans in
|
|
Quake, but it was unclear whether we'd switch back to BSP order. Well,
|
|
some time after that writing, it's become clear: We're back to sorting
|
|
spans by BSP order.
|
|
|
|
In Robert A. Heinlein's wonderful story "The Man Who Sold the Moon," the
|
|
chief engineer of the Moon rocket project tries to figure out how to get
|
|
a payload of three astronauts to the Moon and back. He starts out with a
|
|
four-stage rocket design, but finds that it won't do the job, so he adds
|
|
a fifth stage. The fifth stage helps, but not quite enough, "Because,"
|
|
he explains, "I've had to add in too much dead weight, that's why." (The
|
|
dead weight is the control and safety equipment that goes with the fifth
|
|
stage.) He then tries adding yet another stage, only to find that the
|
|
sixth stage actually results in a net slowdown. In the end, he has to
|
|
give up on the three-person design and build a one-person spacecraft
|
|
instead.
|
|
|
|
1/z-sorted spans in Quake turned out pretty much the same way, as we'll
|
|
see in a moment. First, though, I'd like to note up front that this
|
|
chapter is very technical and builds heavily on material I covered
|
|
earlier in this section of the book; if you haven't already read
|
|
Chapters 59 through 66, you really should. Make no mistake about it,
|
|
this is commercial-quality stuff; in fact, the code in this chapter uses
|
|
the same sorting technique as the test version of Quake, QTEST1.ZIP,
|
|
that id Software placed on the Internet in early March 1996. This
|
|
material is the Real McCoy, true reports from the leading edge, and I
|
|
trust that you'll be patient if careful rereading and some occasional
|
|
catch-up reading of earlier chapters are required to absorb everything
|
|
contained herein. Besides, the ultimate reference for any design is
|
|
working code, which you'll find, in part, in Listing 67.1, and in its
|
|
entirety in the file DDJZSORT.ZIP on the CD-ROM.
|
|
|
|
### Quake and Sorted Spans {#Heading3}
|
|
|
|
As you'll recall from Chapter 66, Quake uses sorted spans to get zero
|
|
overdraw while rendering the world, thereby both improving overall
|
|
performance and leveling frame rates by speeding up scenes that would
|
|
otherwise experience heavy overdraw. Our original design used spans
|
|
sorted by BSP order; because we traverse the world BSP tree from
|
|
front-to-back relative to the viewpoint, the order in which BSP nodes
|
|
are visited is a guaranteed front-to-back sorting order. We simply gave
|
|
each node an increasing BSP sequence number as it was visited, set each
|
|
polygon's sort key to the BSP sequence number of the node (BSP splitting
|
|
plane) it lay on, and used those sort keys when generating spans.
|
|
|
|
(In a change from earlier designs, polygons now are stored on nodes,
|
|
rather than leaves, which are the convex subspaces carved out by the BSP
|
|
tree. Visits to potentially visible leaves are used only to mark that
|
|
the polygons that touch those leaves are visible and need to be drawn,
|
|
and each marked-visible polygon is then drawn after everything in front
|
|
of its node has been drawn. This results in less BSP splitting of
|
|
polygons, which is A Good Thing, as explained below.)
|
|
|
|
This worked flawlessly for the world, but had a couple of downsides.
|
|
First, it didn't address the issue of sorting small, moving BSP models
|
|
such as doors; those models could be clipped into the world BSP tree's
|
|
leaves and assigned sort keys corresponding to the leaves into which
|
|
they fell, but there was still the question of how to sort multiple BSP
|
|
models in the same world leaf against each other. Second, strict BSP
|
|
order requires that polygons be split so that every polygon falls
|
|
entirely within a single leaf. This can be stretched by putting polygons
|
|
on nodes, allowing for larger polygons on average, but even then,
|
|
polygons still need to be split so that every polygon falls within the
|
|
bounding volume for the node on which it lies. The end result, in either
|
|
case, is more and smaller polygons than if BSP order weren't used—and
|
|
that, in turn, means lower performance, because more polygons must be
|
|
clipped, transformed, and projected, more sorting must be done, and more
|
|
spans must be drawn.
|
|
|
|
We figured that if only we could avoid those BSP splits, Quake would get
|
|
a lot faster. Accordingly, we switched from sorting on BSP order to
|
|
sorting on 1/z, and left our polygons unsplit. Things did get faster at
|
|
first, but not as much as we had expected, for two reasons.
|
|
|
|
First, as the world BSP tree is descended, we clip each node's bounding
|
|
box in turn to see if it's inside or outside each plane of the view
|
|
frustum. The clipping results can be remembered, and often allow the
|
|
avoidance of some or all clipping for the node's polygons. For example,
|
|
all polygons in a node that has a trivially accepted bounding box are
|
|
likewise guaranteed to be unclipped and in the frustum, since they all
|
|
lie within the node's volume and need no further clipping. This
|
|
efficient clipping mechanism vanished as soon as we stepped out of BSP
|
|
order, because a polygon was no longer necessarily confined to its
|
|
node's volume.
|
|
|
|
Second, sorting on 1/z isn't as cheap as sorting on BSP order, because
|
|
floating-point calculations and comparisons are involved, rather than
|
|
integer compares. So Quake got faster but, like Heinlein's fifth rocket
|
|
stage, there was clear evidence of diminishing returns.
|
|
|
|
That wasn't the bad part; after all, even a small speed increase is A
|
|
Good Thing. The real problem was that our initial 1/z sorting proved to
|
|
be unreliable. We first ran into problems when two forward-facing
|
|
polygons started at a common edge, because it was hard to tell which one
|
|
was really in front (as discussed below), and we had to do additional
|
|
floating-point calculations to resolve these cases. This fixed the
|
|
problems for a while, but then odd cases started popping up where just
|
|
the right combination of polygon alignments caused new sorting errors.
|
|
We tinkered with those too, adding more code and incurring additional
|
|
slowdowns in the process. Finally, we had everything working smoothly
|
|
again, although by this point Quake was back to pretty much the same
|
|
speed it had been with BSP sorting.
|