4 Primitives and Intersection Acceleration

The classes described in the last chapter focus exclusively on representing geometric properties of 3D objects. Although the Shape class provides a convenient abstraction for geometric operations such as intersection and bounding, it doesn’t contain enough information to fully describe an object in a scene. For example, it is necessary to bind material properties to each shape in order to specify its appearance. To accomplish these goals, this chapter introduces the Primitive abstract base class and provides a number of implementations.

Shapes to be rendered directly are represented by the GeometricPrimitive class. This class combines a Shape with a description of its appearance properties. So that the geometric and shading portions of pbrt can be cleanly separated, these appearance properties are encapsulated in the Material class, which is described in Chapter 9.

The TransformedPrimitive class handles two more general uses of Shapes in the scene: shapes with animated transformation matrices and object instancing, which can greatly reduce the memory requirements for scenes that contain many instances of the same geometry at different locations (such as the one in Figure 4.1). Implementing each of these features essentially requires injecting an additional transformation matrix between the Shape’s notion of world space and the actual scene world space. Therefore, both are handled by a single class.

Figure 4.1: This outdoor scene makes heavy use of instancing as a mechanism for compressing the scene’s description. There are only 24 million unique triangles in the scene, although, thanks to object reuse through instancing, the total geometric complexity is 3.1 billion triangles. (Scene courtesy of Laubwerk.)

This chapter also introduces the Aggregate class, which represents a container that can hold many Primitives. pbrt uses this class as a base for acceleration structures—data structures that help reduce the otherwise upper O left-parenthesis n right-parenthesis complexity of testing a ray for intersection with all n objects in a scene. Most rays will intersect only a few primitives and miss the others by a large distance. If an intersection acceleration algorithm can reject whole groups of primitives at once, there will be a substantial performance improvement compared to simply testing each ray against each primitive in turn. One benefit from reusing the Primitive interface for these acceleration structures is that it makes it easy to support hybrid approaches where an accelerator of one type holds accelerators of other types.

This chapter describes the implementation of two accelerators, one, BVHAccel, based on building a hierarchy of bounding boxes around objects in the scene, and the second, KdTreeAccel, based on adaptive recursive spatial subdivision. While many other acceleration structures have been proposed, almost all ray tracers today use one of these two. The “Further Reading” section at the end of this chapter has extensive references to other possibilities.