Project 6

Acceleration Structures: Bounding Volume Hierarchy

Required Image:

(Image scaled down. Click on images for fullsize view.)

This is a chocolate bunny made up of 69451 chocolate triangles, that was rendered
with the help of a BVH. Performance data is here.

An MPM data set, rendered from 0.2 units away (the particles in the image are
significantly smaller than that distance). There are 809533 particles in the data set.

Same data set, rendered from closer. The red, green, and blue threads represent 1 unit of
the x-, y-, and z-axes, respectively.

A closeup of the corner present in the data set. Here the particles are much more
clearly visible.

Design choices:
I decided to implement a BVH because it made more sense to me than the other options (both in terms of how it generally works, as well as implementation details such as what data structures would be necessary). The BVH has the structure of a restricted binary tree. It is restricted in the sense that the only nodes that are allowed not to have exactly two children are the leaf nodes, and the direct parents of leaf nodes. The tree's nodes are of type BVH_node, which is a simple data structure holding pointers to a bounding box, and two objects (as well as other bookkeeping details that do not matter to this discussion).

The tree is stored in an array. The restricted structure allows this as a convenience, since every node, when specified by its depth from the root and its left-to-right position within that layer, is easily converted to an array index and accessed.

The BVH is built after all objects have been added to scene, by way of an explicit call to Scene::build_BVH. The build is done recursively, starting at the root of the tree. The root contains information about a super-bounding-box that contains the entire scene. The vector containing the scene's objects is sorted by the x-coordinate of the centers of the bounding boxes of each of its objects. This gives a partial ordering of the objects that will lead heuristically to a balanced distribution of objects to the tree. The bounding box of all the objects is computed, and this is stored in the root node. The object pointers in the root node are both set to NULL--this is the implicit signal that the root node is in fact not a leaf node. The object vector is then split roughly in half, and each of the root node's children will repeat the actions of the root node, but on their respective halves of the list. The only difference is that instead of sorting the objects along the x-axis, it will be done on the y-axis for this iteration (and on the z-axis on the next, and then the x- once again, ad infinitum).

If ever this process occurs on a portion of the vector with two or fewer items, then recursion does not take place. This serves as a sufficient stopping condition for the recursive algorithm, since by repeatedly halving the list, this situation will come about eventually.

At this point, the BVH is built. If the program is going to use the BVH to accomplish acceleration, the user must enable it with a call to Scene::enable_BVH. If for whatever reason the acceleration needs to be turned off later, there is the Scene::disable_BVH method to take care of that.

If BVH acceleration is enabled, then the render phase will not simply iterate through the list of objects as in previous projects. Instead, each generated ray makes a series of intersections with various bounding boxes, starting with the one referenced by the root node of the tree. If the ray misses the box, then certainly it will not hit the boxes inside this box, and this ray will be known not to hit any object in the scene. Otherwise, the process must be repeated for the children boxes of the first bounding box. This process continues recursively, until the ray possibly reaches a leaf node of the tree. At this point, the ray will attempt to intersect with the objects referenced by the leaf. At this point it will be known whether there is an intersection and at what t value, and processing can continue as in previous projects.