118 lines
6.4 KiB
Markdown
118 lines
6.4 KiB
Markdown
Chapter 66\
|
|
Quake's Hidden-Surface Removal {#Heading1}
|
|
-------------------------------
|
|
|
|
### Struggling with Z-Order Solutions to the Hidden Surface Problem {#Heading2}
|
|
|
|
Okay, I admit it: I'm sick and tired of classic rock. Admittedly, it's
|
|
been a while, about 20 years, since I was last excited to hear anything
|
|
by the Cars or Boston, and I was never particularly excited in the first
|
|
place about Bob Seger or Queen, to say nothing of Elvis, so some things
|
|
haven't changed. But I knew something was up when I found myself
|
|
changing the station on the Allman Brothers and Steely Dan and Pink
|
|
Floyd and, God help me, the Beatles (just stuff like "Hello Goodbye" and
|
|
"I'll Cry Instead," though, not "Ticket to Ride" or "A Day in the Life";
|
|
I'm not *that* far gone). It didn't take long to figure out what the
|
|
problem was; I'd been hearing the same songs for a quarter-century, and
|
|
I was bored.
|
|
|
|
I tell you this by way of explaining why it was that when my daughter
|
|
and I drove back from dinner the other night, the radio in my car was
|
|
tuned, for the first time ever, to a station whose slogan is "There is
|
|
no alternative."
|
|
|
|
Now, we're talking here about a 10-year-old who worships the Beatles and
|
|
has been raised on a steady diet of oldies. She loves melodies, catchy
|
|
songs, and good singers, none of which you're likely to find on an
|
|
alternative rock station. So it's no surprise that when I turned on the
|
|
radio, the first word out of her mouth was "Yuck!"
|
|
|
|
What did surprise me was that after listening for a while, she said,
|
|
"You know, Dad, it's actually kind of interesting."
|
|
|
|
Apart from giving me a clue as to what sort of music I can expect to
|
|
hear blasting through our house when she's a teenager, her quick uptake
|
|
on alternative rock (versus my decades-long devotion to the music of my
|
|
youth) reminded me of something that it's easy to forget as we become
|
|
older and more set in our ways. It reminded me that it's essential to
|
|
keep an open mind, and to be willing, better yet, eager, to try new
|
|
things. Programmers tend to become attached to familiar approaches, and
|
|
are inclined to stick with whatever is currently doing the job
|
|
adequately well, but in programming there are always alternatives, and
|
|
I've found that they're often worth considering.
|
|
|
|
Not that I should have needed any reminding, considering the
|
|
ever-evolving nature of Quake.
|
|
|
|
### Creative Flux and Hidden Surfaces {#Heading3}
|
|
|
|
Back in Chapter 64, I described the creative flux that led to John
|
|
Carmack's decision to use a precalculated potentially visible set (PVS)
|
|
of polygons for each possible viewpoint in Quake, the game we're
|
|
developing here at id Software. The precalculated PVS meant that instead
|
|
of having to spend a lot of time searching through the world database to
|
|
find out which polygons were visible from the current viewpoint, we
|
|
could simply draw all the polygons in the PVS from back-to-front
|
|
(getting the ordering courtesy of the world BSP tree) and get the
|
|
correct scene drawn with no searching at all; letting the back-to-front
|
|
drawing perform the final stage of hidden-surface removal (HSR). This
|
|
was a terrific idea, but it was far from the end of the road for Quake's
|
|
design.
|
|
|
|
#### Drawing Moving Objects {#Heading4}
|
|
|
|
For one thing, there was still the question of how to sort and draw
|
|
moving objects properly; in fact, this is the single technical question
|
|
I've been asked most often in recent months, so I'll take a moment to
|
|
address it here. The primary problem is that a moving model can span
|
|
multiple BSP leaves, with the leaves that are touched varying as the
|
|
model moves; that, together with the possibility of multiple models in
|
|
one leaf, means there's no easy way to use BSP order to draw the models
|
|
in correctly sorted order. When I wrote Chapter 64, we were drawing
|
|
sprites (such as explosions), moveable BSP models (such as doors), and
|
|
polygon models (such as monsters) by clipping each into all the leaves
|
|
it touched, then drawing the appropriate parts as each BSP leaf was
|
|
reached in back-to-front traversal. However, this didn't solve the issue
|
|
of sorting multiple moving models in a single leaf against each other,
|
|
and also left some ugly sorting problems with complex polygon models.
|
|
|
|
John solved the sorting issue for sprites and polygon models in a
|
|
startlingly low-tech way: We now z-buffer them. (That is, before we draw
|
|
each pixel, we compare its distance, or z, value with the z value of the
|
|
pixel currently on the screen, drawing only if the new pixel is nearer
|
|
than the current one.) First, we draw the basic world, walls, ceilings,
|
|
and the like. No z-buffer *testing* is involved at this point (the world
|
|
visible surface determination is done in a different way, as we'll see
|
|
soon); however, we do *fill* the z-buffer with the z values (actually,
|
|
1/z values, as discussed below) for all the world pixels. Z-filling is a
|
|
much faster process than z-buffering the entire world would be, because
|
|
no reads or compares are involved, just writes of z values. Once the
|
|
drawing and z-filling of the world is done, we can simply draw the
|
|
sprites and polygon models with z-buffering and get perfect sorting all
|
|
around.
|
|
|
|
#### Performance Impact {#Heading5}
|
|
|
|
Whenever a z-buffer is involved, the questions inevitably are: What's
|
|
the memory footprint and what's the performance impact? Well, the memory
|
|
footprint at 320x200 is 128K, not trivial but not a big deal for a game
|
|
that requires 8 MB to run. The performance impact is about 10 percent
|
|
for z-filling the world, and roughly 20 percent (with lots of variation)
|
|
for drawing sprites and polygon models. In return, we get a perfectly
|
|
sorted world, and also the ability to do additional effects, such as
|
|
particle explosions and smoke, because the z-buffer lets us flawlessly
|
|
sort such effects into the world. All in all, the use of the z-buffer
|
|
vastly improved the visual quality and flexibility of the Quake engine,
|
|
and also simplified the code quite a bit, at an acceptable memory and
|
|
performance cost.
|
|
|
|
#### Leveling and Improving Performance {#Heading6}
|
|
|
|
As I said above, in the Quake architecture, the world itself is drawn
|
|
first, without z-buffer reads or compares, but filling the z-buffer with
|
|
the world polygons' z values, and then the moving objects are drawn atop
|
|
the world, using full z-buffering. Thus far, I've discussed how to draw
|
|
moving objects. For the rest of this chapter, I'm going to talk about
|
|
the other part of the drawing equation; that is, how to draw the world
|
|
itself, where the entire world is stored as a single BSP tree and never
|
|
moves.
|