122 lines
6.9 KiB
Markdown
122 lines
6.9 KiB
Markdown
------------------------ --------------------------------- --------------------
|
||
[Previous](66-04.html) [Table of Contents](index.html) [Next](67-02.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
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.
|
||
|
||
------------------------ --------------------------------- --------------------
|
||
[Previous](66-04.html) [Table of Contents](index.html) [Next](67-02.html)
|
||
------------------------ --------------------------------- --------------------
|
||
|
||
* * * * *
|
||
|
||
Graphics Programming Black Book © 2001 Michael Abrash
|