139 lines
7.4 KiB
Markdown
139 lines
7.4 KiB
Markdown
### The Beam Tree {#Heading8}
|
||
|
||
John's original Quake design was to draw front-to-back, using a second
|
||
BSP tree to keep track of what parts of the screen were already drawn
|
||
and which were still empty and therefore drawable by the remaining
|
||
polygons. Logically, you can think of this BSP tree as being a 2-D
|
||
region describing solid and empty areas of the screen, as shown in
|
||
Figure 64.4, but in fact it is a 3-D tree, of the sort known as a *beam
|
||
tree.* A beam tree is a collection of 3-D wedges (beams), bounded by
|
||
planes, projecting out from some center point, in this case the
|
||
viewpoint, as shown in Figure 64.5.
|
||
|
||
In John's design, the beam tree started out consisting of a single beam
|
||
describing the frustum; everything outside that beam was marked solid
|
||
(so nothing would draw there), and the inside of the beam was marked
|
||
empty. As each new polygon was reached while walking the world BSP tree
|
||
front-to-back, that polygon was converted to a beam by running planes
|
||
from its edges through the viewpoint, and any part of the beam that
|
||
intersected empty beams in the beam tree was considered drawable and
|
||
added to the beam tree as a solid beam. This continued until either
|
||
there were no more polygons or the beam tree became entirely solid. Once
|
||
the beam tree was completed, the visible portions of the polygons that
|
||
had contributed to the beam tree were drawn.
|
||
|
||
\
|
||
**Figure 64.4** *Partitioning the screen into 2-D regions.*
|
||
|
||
\
|
||
**Figure 64.5** *Beams as wedges projecting from the viewpoint to
|
||
polygon edges.*
|
||
|
||
The advantage to working with a 3-D beam tree, rather than a 2-D region,
|
||
is that determining which side of a beam plane a polygon vertex is on
|
||
involves only checking the sign of the dot product of the ray to the
|
||
vertex and the plane normal, because all beam planes run through the
|
||
origin (the viewpoint). Also, because a beam plane is completely
|
||
described by a single normal, generating a beam from a polygon edge
|
||
requires only a cross-product of the edge and a ray from the edge to the
|
||
viewpoint. Finally, bounding spheres of BSP nodes can be used to do the
|
||
aforementioned bulk culling to the frustum.
|
||
|
||
The early-out feature of the beam tree—stopping when the beam tree
|
||
becomes solid—seems appealing, because it appears to cap worst-case
|
||
performance. Unfortunately, there are still scenes where it's possible
|
||
to see all the way to the sky or the back wall of the world, so in the
|
||
worst case, all polygons in the frustum will still have to be tested
|
||
against the beam tree. Similar problems can arise from tiny cracks due
|
||
to numeric precision limitations. Beam-tree clipping is fairly
|
||
time-consuming, and in scenes with long view distances, such as views
|
||
across the top of a level, the total cost of beam processing slowed
|
||
Quake's frame rate to a crawl. So, in the end, the beam-tree approach
|
||
proved to suffer from much the same malady as the painter's algorithm:
|
||
The worst case was much worse than the average case, and it didn't scale
|
||
well with increasing level complexity.
|
||
|
||
### 3-D Engine du Jour {#Heading9}
|
||
|
||
Once the beam tree was working, John relentlessly worked at speeding up
|
||
the 3-D engine, always trying to improve the design, rather than
|
||
tweaking the implementation. At least once a week, and often every day,
|
||
he would walk into my office and say "Last night I couldn't get to
|
||
sleep, so I was thinking..." and I'd know that I was about to get my
|
||
mind stretched yet again. John tried many ways to improve the beam tree,
|
||
with some success, but more interesting was the profusion of wildly
|
||
different approaches that he generated, some of which were merely
|
||
discussed, others of which were implemented in overnight or weekend-long
|
||
bursts of coding, in both cases ultimately discarded or further evolved
|
||
when they turned out not to meet the design criteria well enough. Here
|
||
are some of those approaches, presented in minimal detail in the hopes
|
||
that, like Tom Wilson with the Paradise FIFO, your imagination will be
|
||
sparked.
|
||
|
||
#### Subdividing Raycast {#Heading10}
|
||
|
||
Rays are cast in an 8x8 screen-pixel grid; this is a highly efficient
|
||
operation because the first intersection with a surface can be found by
|
||
simply clipping the ray into the BSP tree, starting at the viewpoint,
|
||
until a solid leaf is reached. If adjacent rays don't hit the same
|
||
surface, then a ray is cast halfway between, and so on until all
|
||
adjacent rays either hit the same surface or are on adjacent pixels;
|
||
then the block around each ray is drawn from the polygon that was hit.
|
||
This scales very well, being limited by the number of pixels, with no
|
||
overdraw. The problem is dropouts; it's quite possible for small
|
||
polygons to fall between rays and vanish.
|
||
|
||
#### Vertex-Free Surfaces {#Heading11}
|
||
|
||
The world is represented by a set of surface planes. The polygons are
|
||
implicit in the plane intersections, and are extracted from the planes
|
||
as a final step before drawing. This makes for fast clipping and a very
|
||
small data set (planes are far more compact than polygons), but it's
|
||
time-consuming to extract polygons from planes.
|
||
|
||
#### The Draw-Buffer {#Heading12}
|
||
|
||
Like a z-buffer, but with 1 bit per pixel, indicating whether the pixel
|
||
has been drawn yet. This eliminates overdraw, but at the cost of an
|
||
inner-loop buffer test, extra writes and cache misses, and, worst of
|
||
all, considerable complexity. Variations include testing the draw-buffer
|
||
a byte at a time and completely skipping fully-occluded bytes, or
|
||
branching off each draw-buffer byte to one of 256 unrolled inner loops
|
||
for drawing 0-8 pixels, in the process possibly taking advantage of the
|
||
ability of the x86 to do the perspective floating-point divide in
|
||
parallel while 8 pixels are processed.
|
||
|
||
#### Span-Based Drawing {#Heading13}
|
||
|
||
Polygons are rasterized into spans, which are added to a global span
|
||
list and clipped against that list so that only the nearest span at each
|
||
pixel remains. Little sorting is needed with front-to-back walking,
|
||
because if there's any overlap, the span already in the list is nearer.
|
||
This eliminates overdraw, but at the cost of a lot of span arithmetic;
|
||
also, every polygon still has to be turned into spans.
|
||
|
||
#### Portals {#Heading14}
|
||
|
||
The holes where polygons are missing on surfaces are tracked, because
|
||
it's only through such portals that line-of-sight can extend. Drawing
|
||
goes front-to-back, and when a portal is encountered, polygons and
|
||
portals behind it are clipped to its limits, until no polygons or
|
||
portals remain visible. Applied recursively, this allows drawing only
|
||
the visible portions of visible polygons, but at the cost of a
|
||
considerable amount of portal clipping.
|
||
|
||
### Breakthrough! {#Heading15}
|
||
|
||
In the end, John decided that the beam tree was a sort of second-order
|
||
structure, reflecting information already implicitly contained in the
|
||
world BSP tree, so he tackled the problem of extracting visibility
|
||
information directly from the world BSP tree. He spent a week on this,
|
||
as a byproduct devising a perfect DOOM (2-D) visibility architecture,
|
||
whereby a single, linear walk of a DOOM BSP tree produces zero-overdraw
|
||
2-D visibility. Doing the same in 3-D turned out to be a much more
|
||
complex problem, though, and by the end of the week John was frustrated
|
||
by the increasing complexity and persistent glitches in the visibility
|
||
code. Although the direct-BSP approach was getting closer to working, it
|
||
was taking more and more tweaking, and a simple, clean design didn't
|
||
seem to be falling out. When I left work one Friday, John was preparing
|
||
to try to get the direct-BSP approach working properly over the weekend.
|