Review : Hmm, good ideas, but awful grammar.
--------------------------------------
Notes on the fast BeamTree algorithm
Charles Bloom
1-17-2000
--------------------------------------
The goal of the fast beamtree algorithm is for it to
be so lean that it can never ever possible slow down the
renderer. The consequence is that full occlusion is not
done; eg. the beamtree does not contain all polygons, and
does not occlude all polygons wich could be occluded.
Let's examine this. Once a beamtree has N polygons in it,
it has a cost of O( log(N) ) to test, the result is that
the bigger it gets, the more expensive it is to test a
polygon and see that it is visible (occlusion test that
report non-visibility can be O(1)). So, the main thing we
can do is only add a few polygons to the beamtree, resulting
in a total cost off O( N ) instead of O( N * log(N) ).
In most interesting cases, only a few large polygons are the
primary occluders (for example, when you stand right in front
of a wall), so that if we can add only these few large
polygons, we will get most occlusion, while only having a
few polygons in the tree.
Now, when a beamtree contains all polygons and those polygons
are sorted front to back strictly, then the rendering algorithm
goes as follows :
1. clip the polygon into the beamtree, cutting it against
all old polygons
2. for all of the new polygon pieces that land in an
empty part of the beamtree:
3. render the polygon piece
4. add it to the beamtree.
(note that if the four edges of the screen are added to the
beamtree first, this does the clipping for you too). This is
quite elegant and provides for full exact occlusion, but we
cannot use it, because we will only add a few polygons to the
screen, and our polygons will not be strictly front to back,
only roughly sorted front to back.
So, conceptually, our beamtree algorithm must be as follows :
For a given polygon, estimate if it is a good occluder. If so,
add it to the beam tree. Associate a "Z" value with this polygon
equal to the farthest Z of any part of the polygon (we treat the
polygon as 2d in screen space with just one Z value for the whole
polygon). Now, to test a polygon against the beamtree, we
simply find all the beamtree leaves that it lies in. We check the
closest Z depth of the polygon against the value in the leaf : if
the Z of the polygon is closer than that in the leaf, it is
visible. Note that if a polygon is visible, we can early out of
the beamtree tests quickly (once it is visible in any portion, we
render the whole thing), so that our beamtree is very fast when it
is failing to occlude anything.
We don't actually test all polygons to be rendered against the
beamtree, but rather test bounding boxes of groups of polygons
against the tree. The test against the tree is typically not
worth the compuation unless we might occlude a large polygon or
quite a few polygons. Note that if the front three faces of a
bounding cube are occluded, then everything inside is occluded.
(we actually just use the screenspace bounding polygon of the cube).
In practice, a 2d BSP is used to construct the beamtree, so that
adjacent polygons can abutt exactly (by sharing a node in the tree).
Also, polygons to be rendered are not actually checked against the
tree, but rather their bounding circle or bounding rectangle in
screen space is used for speed.
Let me say it again to try to make this clear:
imagine that you've already found the good occluders and added them to
the "occlusion structure" (OS) (the beamtree, or whatever). then,
descend the oct-tree:
take the AABB of the current oct-tree node and do the heirarchical
test against the frustum; stop if it's all-out
if it's partially inside, render the contents of the node
if it's full inside, test the AABB against the OS and
render the contents only if visible.
So, the only part that we need the front-3 faces for is this test
against the OS. To do that we just project the corners of the AABB
to screen space, then take the quads that make up the 3 front faces
and see if any part of them is visible (eg. lands in an empty part
of the beam-tree).
----------------------------------------------------------
Unsolved issues :
1. Faster bounding cube test? Currently requires transforming
the eight corners of the cube, more expensive than we'd like.
----------------------------------------------------------
(addendum Jun 6:)
The cool way to drive this "loose beamtree" is with a whole-scene
oct-tree. You start with a camera frustum, with which you can
easily (heirarchically) tell which cubes off the octtree are in
the frustum; this gives you your first level of visibility culling.
Next, you visit all the visible octtree cubes near the camera (this
is a simply heirarchical descent). In all those cubes, you tell
the objects that they may add beamtree occluders after they render.
(there are no beamtree tests).
For the rest of the visible octtree cubes, you test the three font
faces sides of the cube against the beamtree; if they are not all
occluded, then you render the objects contained in the cube.
You should use the "loose octtrees" of Thatcher Ulrich to make sure
that each object is wholy contained in one and only one octtree node.
(you can compute which three faces will be the front-facing ones
only once per frame, it's the same for every cube). See
http://www.tulrich.com/geekstuff/partitioning.html
How do objects add to the beamtree? Basically, they have a bunch
of OBB's (oriented bounding boxes) which are as large as possible
which being entirely enclosed by the polygons of the objects. They
then simply add the front faces of these OBB's to the beamtree.
Note that if we do this, we don't even need depth comparisons in
the beamtree (eg. no Z's). Instead, it's just a way of drawing 2d
regions that are a binary occlusion mask. It's equivalent to
creating a very elaborate frustum for culling out the objects in
octtree cubes away from the camera.
----------------------------------------------------------