Binary Space Partition Trees (or BSP trees for short) where introduced
by Fuchs, Kedem, and
Naylor around 1980. This graphics trio produced two
papers: "Predeterming Visibility Priority in 3-D Scenes" and "On
Visible Surface Generation by A Priori Tree Structures" which outlined
the usefullness of BSP trees and how to implement them. Fuchs, Kedem,
and Naylor primarly focused on enhancing rendering of static scenes.
Later authors built on the above papers to incorporate shadow
generation and handling of dynamic scenes.

Simply, a BSP tree is a heirarchical subdivisions of n dimensional
space into convex subspaces. Each node has a front
and back leaf. Starting off with the root node, all
subsequent insertions are partitioned by the hyperplane of the current
node. In 2 dimensional space, a hyperplane is a
line. In 3 dimensional space, a hyperplane is a
plane. The end goal of a BSP tree if for the
hyperplanes of the leaf nodes to be trivially "behind" or "infront" of
the parent hyperplane.

BSP trees are very useful for real time interaction with displays
of static images. Before the static scene is rendered, the BSP tree
is calculated. BSP trees can be traversed very quickly (linear time)
for hidden surface removal and shadow casting. With some work, BSP
trees can be modified to handle dynamic events in a scene.

First, select a partition hyperplane. For this dicussion, we will
use a 2 dimensional world and our root node will be a line.

Partition all polygons in the world with the initial partition
hyperplane, storing them in either the front or back polygon list.

recurse or iterate through the front and back polygon list,
creating a new tree node and attaching it the the left or right leaf
of the parent node.

As pointed out in by Fuchs in [3], the choice of a root node will
directly influence the size of the tree. If a root node cuts across
many different polygons, it will probably result in a large tree.
Since calculating a BSP tree is usually done before rendering, the
easiest method for finding an optimal root node is test a small number of
candidates. The node that results in the smallest number of nodes in the BSP tree should be chosen.

There are four cases to deal with when a polygon or line is partitioned
by the hyperplane:

polygon is infront of hyperplane

polygon is behind hyperlane

polygon is coincident with the hyperplane

polygon spans the hyperplane

For the first two cases, the line or polygon is simply added to
the appropriate node of the tree. If the line or polygon is
coincident, it can be handled by storing multiple polygons in the node
of the BSP tree. If the polygon spans the hyperplane, the polygon
splitting algorithm must find the intersection point of the polygon
and plane. This can be done with ray plane intersection approach for
convex polygons.

Finally, the BSP tree algorithm can perform either recursively or
iteratively. The recursive BSP tree is very simple to understand
because it simply performs a partition based on the current hyperplane
and then recurses the the front and back leaf nodes. The following sample code illustrates this:

However, for performance reasons, recursion is not always attractive.
In most situtations, BSP tree calculations are done prior to
interactive rendering of a scene. However, if dynamic objects are
inserted into the tree, BSP tree calculations may need to be done in
real time. According to [4], recursion can be modelled by a
stack. As you want to traverse down the BSP tree, you push the
pointer of the node onto the stack.

In order to really understand how a BSP tree works, it helps to see a
graphical demonstration of tree creation. The following set of images
show how in a 2 dimensional example lines would be added to the bsp
tree.

Improving rendering performance is one of the reasons BSP trees are
used. BSP trees essentially peform extensive precalculation for a
back to front painters algorithm or a front to back
scanline algorithm. The tree can be traversed in
linear time from an arbitrary viewpoint.

Since a painters algorithm works by drawing polygons farthest from
the eye first, the following code recurses to the bottom of the tree
and draws the polygons. As the recursion unwinds, polygons closer to
the eye are drawn over far polygons. Because the BSP tree allready
splits polygons into trivial pieces, the hardest part of the painters
algorithm is allready solved.

According to [4],
BSP trees can also be used to render a scene with a front to back
traversal of the tree using a scanline fill algorithm. A write mask
can be used to prevent a pixel from being written to more than once.
In a scene with many light sources, the painters algorithm would
evaluate the same pixel more than once.

In order to traverse the tree from front to back, simply change the order of the recursion in the above code.

While BSP trees are useful for fast hidden surface removal in static
scenes, they can also be used to compute shadows with one or more
fixed light source. Conventional methods for shadow generation form a
shadow polygon from two vectors emmanating from the point light source
and the vertices on an object's edges [1]. The shadow volume
is clipped against the view volume (the screen, a floor, a wall, etc)
to create a finite volume. Any polygon inside a shadow polygon is in
shadow. All visible points on a scene polygon are tested against the
relative number of shadow polygons between it and the eyepoint to
determine if the point is in shadow [1]

The above approach takes two passes: one to construct shadow
volumes and another to test points against shadow polygons. The scene
also must be transformed by the light source's perspective
transformation. Most shadow volume approaches require clipping
against polygons instead of planes as in the BSP approach.

The Shadow Volume BSP Tree algorithm proposed by Chin and Feiner
does not require clipping against a view volume or any kind of
tranformation of the scene. Basically, the SVBSP approach creates a
merged shadow volume during the process of inserting polygons into the
BSP tree. The SVBSP can be precalculated as long as the scene and
light source(s) are static.

The SVBSP tree algorithm has a shadow plane at each internal node.
The shadow plane is defined by the a point light source and an edge of
the polygon. The direction of the plane's normal determines if an
object is in or outside of shadow.

There are two main steps to the SVBSP algorithm:

Determining shadows: the polygon is filtered down the SVBSP
tree to determine if it is lit or shadowed. if the polygon is both in
shadow and light, it is split into two polygons. Note that if a
polygon is in front of the eye and its shadow volume is allready
defined by a polygon closer to the light source, the farther polygon
can be discarded. This can be seen in the example when line ef are
added into the SVBSP tree.

Enlarging the SVBSP tree: Once
a polygon has been filtered through the SVBSP tree, its shadow volume
is computed and inserted into the tree. In the case of segment eg in
the example below, the shadow volume is allready inside another shadow
volume and does not need to be included.

Multiple Light Sources
SVBSP trees can be easily extended to multiple light sources.
Basically, construct a SVBSP tree with the first light source. Store
all polygon fragments at the appropriate node of the BSP tree which
defines the scene. Once all polygons have been partitioned, throw
away the current SVBSP tree. Next, create a SVBSP tree from the next
light source. Filter the polygon fragments into this new tree.
Repeat until there are no more light sources and there is a final
SVBSP ready for shadow generation.

Chin and Feiner warn that multiple light sources could result in a
very large tree with many split polygons. They recommend that light
sources be processed in order that will lead to the least
fragmentation. One method is to process the light sources in
increasing order with the number of polygons facing them.
Alternatively, process static light sources first and dynamic sources
last.

all source code is from Near Real-Time shadow Generation Using BSP
Trees by Chin and Feiner. The shadowGenerator creates
shadow volumes by applying the recursive function
determineShadow to all scene polygons. determineShadow
filters the polygons down the SVBSP tree splitting them when necessary
and adding shadow volumes of lit fragments to the SVBSP tree.

proecedure shadowGenerator(PLS,BSPtree)
;Initialize the SVBSP tree to an OUT cell
SVBSPtree := OUT
;Process all polygons facing light source PLS in
;front-to-back order by BSP tree traversal in 0(n)
for each scene polygon p, in front-to-back order
relative to PLS
if p is facing PLS
;Determine areas of p that are shadowed.
;BSPnode is p's node in BSPtree
SVBSPtree := determineShadow(p,SVBSPtree,
PLS,BSPnode)
else
;p is not facing PLS or PLS is in p's plane
mark p as shadowed
endif
endfor
endproc

As shown in the above sections, BSP trees
are excellent algorithms for hidden surface removal in rendering and
shadow generation. However, since many applications want to have
dynamic interaction with the world that is created, some method needs
to be applied to handling dynamic objects.

One method proposed by Fuchs et. al. was to specify a bounding
polygon around the area that of the scene that would be changing.
Since the standard BSP algorithm does not interact with the insides of
polygons, the programmer can manipulate the environment inside the
polygon. One example wold be to place a long rectangular polygon over
a road in a static world scene. The render could be specially
modified to tree the bounding polygon as a special case and would know
how to update the contents inside the polygon. Another approach would
be to have a seperate hidden surface removal algorithm for dynamic
objects. For instance, in a doom type world, the level could be in a
BSP tree while all missles and bullets could be rendered using a
z-buffer.

in [2], Y. Chrysanthou et al. describe three criteria necessary for to support dynamic changes in 3D scenes:

Ability to change the camera view

ability to add an object into the scene

ability to delete an object from the scene

Both 1 and 2 can be handled by BSP trees; changing the camera requires
traversing the tree in a different order and adding an object just
means recursively partitioning the polygon into the BSP tree. The
third criteria is much more difficult; deleting a polygon from a scene
is hard in a BSP tree because a polygon may be split into the front
and back leafs of the node.

fortunately, there is a solution for deleting nodes from a BSP tree. There are four cases for deleting

on a leaf node. Simply delete this polygon because it does not
have any children who partition other polygons nor is it partitioned
itself.

on a node with other coincidental faces (on the same plane). This
is also easy since the partition hyperplane is defined by the other
polygons stored in that node.

a node with exatly one non-empty child. This is easy because the
hyperplane of this node split the subspace into a empty and non-empty
region. Simply, the child of the deleted node becomes the child of
the parent of the deleted node.

a node with two non empty children. This is the most challenging
problem because a polygon could have been split by the hyperplane of
this node. Fortunately, the polygons can be reconstructed by
filtering the smaller sub tree into the larger sub tree. The larger
sub tree takes the place of the deleted node. The
FilterIntoTree performs this processes.

performance issues
Polygons who are near the leaf-nodes of the BSP tree can be deleted in
constant time. Consequently, objects that are transient should be
inserted into the BSP tree last. The FilterIntoTree function may also
be fairly slow and costly when the smaller trees it is merging may be
transient. Y. Chrysanthou et al. mention that an alternative startegy
could be employed that would adopt criteria to determine when the
filtering operation should be carried out. One approach is to just
mark a node as deleted but not actually remove it from the tree.
ANother approach is to only filter when the sub tree is less than a
maximum size.

function TidyTree(tree)
{
if Empty(tree) then return EMPTY_TREE endif;
if tree.root is not market as "deleted" then
tree.front = TidyTree(tree.front);
tree.back = TidyTree(tree.back);
return(tree);
else
if any child of the tree is empty then
return(TidyTree(other child of tree))
else return(FilterIntoTree(polygons of smaller sub-tree,
TidyTree(largest subtree)))
endif
enif
}

modified tidytree algorithm that only runs if the sub-tree size gets too large:

if tree.root is not market as "deleted" or the smaller sub-tree is too large then
tree.root = TidyTree(tree.front);
tree.back = TidyTree(tree.back);
return(tree);
else ...