92 lines
4.3 KiB
Markdown
92 lines
4.3 KiB
Markdown
The obvious way to get a 1/z value at any arbitrary point on a polygon
|
||
is to calculate 1/z at the vertices, interpolate it down both edges of
|
||
the polygon, and interpolate between the edges to get the value at the
|
||
point of interest. Unfortunately, that requires doing a lot of work
|
||
along each edge, and worse, requires division to calculate the 1/z step
|
||
per pixel across each span.
|
||
|
||
A better solution is to calculate 1/z directly from the plane equation
|
||
and the screen x and y of the pixel of interest. The equation is
|
||
|
||
1/z = (a/d)x' - (b/d)y' + c/d
|
||
|
||
where z is the viewspace z coordinate of the point on the plane that
|
||
projects to screen coordinate (x',y') (the origin for this calculation
|
||
is the center of projection, the point on the screen straight ahead of
|
||
the viewpoint), [a b c] is the plane normal in viewspace, and d is the
|
||
distance from the viewspace origin to the plane along the normal.
|
||
Division is done only once per plane, because a, b, c, and d are
|
||
per-plane constants.
|
||
|
||
The full 1/z calculation requires two multiplies and two adds, all of
|
||
which should be floating-point to avoid range errors. That much
|
||
floating-point math sounds expensive but really isn't, especially on a
|
||
Pentium, where a plane's 1/z value at any point can be calculated in as
|
||
little as six cycles in assembly language.
|
||
|
||
#### Where That 1/Z Equation Comes From {#Heading10}
|
||
|
||
For those who are interested, here's a quick derivation of the 1/z
|
||
equation. The plane equation for a plane is
|
||
|
||
ax + by + cz - d = 0
|
||
|
||
where x and y are viewspace coordinates, and a, b, c, d, and z are
|
||
defined above. If we substitute x=x'z and y=-y'z (from the definition of
|
||
the perspective projection, with y inverted because y increases upward
|
||
in viewspace but downward in screenspace), and do some rearrangement, we
|
||
get:
|
||
|
||
z = d / (ax' - by' + c)
|
||
|
||
Inverting and distributing yields:
|
||
|
||
= ax'/d - by'/d + c/d
|
||
|
||
We'll see 1/z sorting in action in Chapter 67.
|
||
|
||
#### Quake and Z-Sorting {#Heading11}
|
||
|
||
I mentioned earlier that Quake no longer uses BSP order as the sorting
|
||
key; in fact, it uses 1/z as the key now. Elegant as the gradients are,
|
||
calculating 1/z from them is clearly slower than just doing a compare on
|
||
a BSP-ordered key, so why have we switched Quake to 1/z?
|
||
|
||
The primary reason is to reduce the number of polygons. Drawing in BSP
|
||
order means following certain rules, including the rule that polygons
|
||
must be split if they cross BSP planes. This splitting increases the
|
||
numbers of polygons and edges considerably. By sorting on 1/z, we're
|
||
able to leave polygons unsplit but still get correct drawing order, so
|
||
we have far fewer edges to process and faster drawing overall, despite
|
||
the added cost of 1/z sorting.
|
||
|
||
Another advantage of 1/z sorting is that it solves the sorting issues I
|
||
mentioned at the start involving moving models that are themselves small
|
||
BSP trees. Sorting in world BSP order wouldn't work here, because these
|
||
models are separate BSPs, and there's no easy way to work them into the
|
||
world BSP's sequence order. We don't want to use z-buffering for these
|
||
models because they're often large objects such as doors, and we don't
|
||
want to lose the overdraw-reduction benefits that closed doors provide
|
||
when drawn through the edge list. With sorted spans, the edges of moving
|
||
BSP models are simply placed in the edge list (first clipping polygons
|
||
so they don't cross any solid world surfaces, to avoid complications
|
||
associated with interpenetration), along with all the world edges, and
|
||
1/z sorting takes care of the rest.
|
||
|
||
### Decisions Deferred {#Heading12}
|
||
|
||
There is, without a doubt, an awful lot of information in the preceding
|
||
pages, and it may not all connect together yet in your mind. The code
|
||
and accompanying explanation in the next chapter should help; if you
|
||
want to peek ahead, the code is available on the CD-ROM as DDJZSORT.ZIP
|
||
in the directory for Chapter 67. You may also want to take a look at
|
||
Foley and van Dam's *Computer Graphics* or Rogers' *Procedural Elements
|
||
for Computer Graphics*.
|
||
|
||
As I write this, it's unclear whether Quake will end up sorting edges by
|
||
BSP order or 1/z. Actually, there's no guarantee that sorted spans in
|
||
any form will be the final design. Sometimes it seems like we change
|
||
graphics engines as often as they play Elvis on the ‘50s oldies stations
|
||
(but, one would hope, with more aesthetically pleasing results!) and no
|
||
doubt we'll be considering the alternatives right up until the day we
|
||
ship.
|