Implementation Strategy and Results
Two strategies were employed in the implementation of an objected
oriented ray tracer. Both of them were aimed at reducing the number of
objects that need to be tested for intersection with a given ray.
Bounding Volumes
The first technique was to use bounding volumes for complex
objects. Because of the object oriented implementation this was a
rather simple matter. Every renderable object is subclassed for an
abstract class GraphicalObject. The GraphicalObject has a
virtual method intersect(Ray). In the case that the object is a
primitive (sphere, cylinder, circle, hyperboloid, ...) this method
will perform the intersection calculation. In a complex object which
is composed of other objects and contained in a bounding volume, the
function will first call the intersection method of the bounding
volume. If this fails intersection failure is returned and none of the
other objects need be tested. If it is found that the bounding volume
is intersected then the intersect method of all the objects that
compose the complex object must be called. Which ever object is found
to have the closest point of intersection is returned along with the
time of intersection. If no objects are found to intersect the ray
failure is returned.
The Above image of a pawn (traced at 500 x 500 pixels) is composed of
6 primitive objects, 2 cylinders, 3 hyperboloids, and 1
sphere. Without a bounding volume the image takes about 65 sec. (59
sec, 67 sec, 69 sec) to trace. If the pawn is treated as a complex
object with a cylinder as a bounding volume, the time to trace it is
reduced to about 35 sec. (34 sec, 34 sec, 38 sec) almost cutting the
rendering time if half.
Spatial Subdivision
The second technique used is a form of
non-uniform spatial subdivision. Instead of breaking the world into
voxels, 4 planes are defined using the observer point and the 4
corners of the view plane.
The objects in the world are then tested to determine if any portion
of them is in the viewing area defined by these planes. This is
accomplished with the InView(plane) method of the
GraphicalObject class. It is important to keep this method as simple
as possible. For example the implementation of this method for a
sphere involves displacing the center of the sphere(in world
coordinates) by its radius in the direction of the normal of the plane
and determining which side of the plane this point falls on. Only
those objects that are in view need to be considered for
intersection. If the number of objects is above some threshold amount
the view plane is subdivided and the process repeated on each of the
new "smaller" viewplanes.
This recursive non-uniform subdivision is handled by threads. Every
time the "viewplane" is subdivided 4 threads are created to process
each of the new viewplanes. Each thread if given a list of objects
that were in view of the larger viewplane as well as which quadrant of
that viewplane it is processing. The quadrant number is needed to keep
the thread from checking the objects against the planes that were part
of the previous viewplanes boundary.
The recursion bottoms out if either the number of objects in view is
below some threshold or if the area of the viewplane is below some
threshold. In this implementation the object threshold was set at 0
(if there are no objects in view set the pixel to the background
color) and the area cutoff was 500 pixels.
The above image (500 x 500 pixels) of 16 pawns with bounding volumes
takes about 195sec. (190sec & 199sec) to trace without spatial
subdivision. Using the above thresholds the time is reduced to
53sec. (58s, 60s, 55s, 41s) almost 1/4 the execution time.
It should be noted that these timings were performed on a
multiprocessor(2) alpha with kernel supported pthreads and 500M RAM.