Required Image:
(Image scaled down. Click on images for fullsize view.)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.