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

122 lines
7.2 KiB
Markdown

### Entities {#Heading9}
So far, all we've drawn is the static, unchanging (apart from dynamic
lighting) world. That's an important foundation, but it's certainly not
a game; now we need to add moving objects. These objects fall into four
very different categories: BSP models, polygon models, sprites, and
particles.
#### BSP Models {#Heading10}
BSP models are just like the world, except that they can move. Examples
include doors, moving bridges, and health and ammo boxes. The way these
are rendered is by clipping their polygons into the world BSP tree, so
each polygon fragment is in only one leaf. Then these fragments are
added to the edge list, just like world polygons, and scanned out, along
with the rest of the world, when the edge list is processed. The only
trick here is front-to-back ordering. Each BSP model polygon fragment is
given the BSP sorting order of the leaf in which it resides, allowing it
to sort properly versus the world polygons. If two or more polygons from
different BSP models are in the same leaf, however, BSP ordering is no
longer useful, so we then sort those polygons by 1/z, calculated from
the polygons' plane equations.
Interesting note: We originally tried to sort all world polygons on 1/z
as well, the reason being that we could then avoid splitting polygons
except when they actually intersected, rather than having to split them
along the lines of parent nodes. This would result in fewer edges, and
faster edge list processing and rasterization. Unfortunately, we found
that precision errors and special cases such as seamlessly abutting
objects made it difficult to get global 1/z sorting to work completely
reliably, and the code that we had to add to work around these problems
slowed things up to the point where we were getting no extra performance
for all the extra code complexity. This is not to say that 1/z sorting
can't work (especially in something like a flight sim, where objects
never abut), but BSP sorting order can be a wonderful thing, partly
because it always works perfectly, and partly because it's simpler and
faster to sort on integer node and leaf orders than on floating-point
1/z values.
BSP models take some extra time because of the cost of clipping them
into the world BSP tree, but render just as fast as the rest of the
world, again with no overdraw, so closed doors, for example, block
drawing of whatever's on the other side (although it's still necessary
to transform, project, and add to the edge list the polygons the door
occludes, because they're still in the PVS—they're potentially visible
if the door opens). This makes BSP models most suitable for fairly
simple structures, such as boxes, which have relatively few polygons to
clip, and cause relatively few edges to be added to the edge list.
#### Polygon Models and Z-Buffering {#Heading11}
Polygon models, such as monsters, weapons, and projectiles, consist of a
triangle mesh with front and back skins stretched over the model. For
speed, the triangles are drawn with affine texture mapping; the
triangles are small enough, and the models are generally distant enough,
that affine distortion isn't visible. (However, it is visible on the
player's weapon; this caused a lot of extra work for the artists, and we
will probably implement a perspective-correct polygon-model rasterizer
in Quake 2 for this specific purpose.) The triangles are also Gouraud
shaded; interestingly, the light vector used to shade the models is
always from the same direction, and has no relation to any actual lights
in the world (although it does vary in intensity, along with the model's
ambient lighting, to match the brightness of the spot the player is
standing above in the world). Even this highly inaccurate lighting works
well, though; the Gouraud shading makes models look much more
three-dimensional, and varying the lighting in even so crude a way
allows hiding in shadows and illumination by explosions and muzzle
flashes.
One issue with polygon models was how to handle occlusion issues; that
is, what parts of models were visible, and what surfaces they were in
front of. We couldn't add models to the edge list, because the hundreds
of polygons per model would overwhelm the edge list. Our initial
occlusion solution was to sort polygon-model polygons into the world
BSP, drawing the portions in each leaf at the right points as we drew
the world in BSP order. That worked reasonably well with respect to the
world (not perfectly, though, because it would have been too expensive
to clip all the polygon-model polygons into the world, so there was some
occlusion error), but didn't handle the case of sorting polygon models
in the same leaf against each other, and also didn't help the polygons
in a given polygon model sort properly against each other.
The solution to this turned out to be z-buffering. After all the spans
in the world are drawn, the z-buffer is filled in for those spans. This
is a write-only operation, and involves no comparisons or overdraw
(remember, the spans cover every pixel on the screen exactly once), so
it's not that expensive—the performance cost is about 10%. Then polygon
models are drawn with z-buffering; this involves a z-compare at each
polygon-model pixel, but no complicated clipping or sorting—and
occlusion is exactly right in all respects. Polygon models tend to
occupy a small portion of the screen, so the cost of z-buffering is not
that high, anyway.
Opinions vary as to the desirability of z-buffers; some people who favor
more analytical approaches to hidden surface removal claim that John has
been seduced by the z-buffer. Maybe so, but there's a lot there to be
seduced by, and that will be all the more true as hardware rendering
becomes the norm. The addition of particles—thousands of tiny colored
rectangles—to Quake illustrated just how seductive the z-buffer can be;
it would have been very difficult to get all those rectangles to draw
properly using any other occlusion technique. Certainly z-buffering by
itself can't perform well enough to serve for all hidden surface
removal; that's why we have the PVS and the edge list (although for
hardware rendering the PVS would suffice), but z-buffering pretty much
means that if you can figure out how to draw an effect, you can readily
insert it into the world with proper occlusion, and that's a powerful
capability indeed.
Supporting scenes with a dozen or more models of 300 to 500 polygons
each was a major performance challenge in Quake, and the polygon-model
drawing code was being optimized right up until the last week before it
shipped. One help in allowing more models per scene was the PVS; we only
drew those models that were in the PVS, meaning that levels could have a
hundred or more models without requiring a lot of work to eliminate most
of those that were occluded. (Note that this is not unique to the PVS;
whatever high-level culling scheme we had ended up using for world
polygons would have provided the same benefit for polygon models.) Also,
model bounding boxes were used to trivially clip those that weren't in
the view pyramid, and to identify those that were unclipped, so they
could be sent through a special fast path. The biggest breakthrough,
though, was a very different sort of rasterizer that John came up with
for relatively distant models.