③ One approach for reducing renderer startup time is to
support a binary representation of internal data structures that can be
written to disk. For example, for complex
scenes, creating the ray acceleration aggregates may take more time than
the initial parsing of the scene file. An alternative is to modify
the system to have the capability of dumping out a representation of the
acceleration structure and all of the primitives inside it after it is
first created. The resulting file could then be subsequently read back
into memory much more quickly than rebuilding the data structure from
scratch. However, because C++ doesn’t have native support for saving
arbitrary objects to disk and then reading them back during a subsequent
execution of the program (a capability known as serialization or
pickling
in other languages), adding this feature effectively requires extending
many of the objects in pbrt to support this capability on their own.
One additional advantage of this approach is that substantial amounts of
computation can be invested in creating high-quality acceleration
structures, with the knowledge that this cost doesn’t need to be paid each
time the scene is loaded into memory. Implement support for serializing
the scene representation and then reusing it across multiple renderings of
the scene. How is pbrt’s start-up time (up until when rendering begins)
affected? What about overall rendering time?
② The material assigned to object instances in pbrt is the
current material when the instance was defined inside the
pbrtObjectBegin()/pbrtObjectEnd() block. This can be
inconvenient if the user wants to use the same geometry multiple times in a
scene, giving it a different material. Fix the API and the implementation
of the
TransformedPrimitive class so that the material assigned to instances is
the current material when the instance is instantiated, or, if the user
hasn’t set a material, the current material when the instance was created.
③ Generalize pbrt’s API for specifying animation; the current
implementation only allows the user to provide two transformation matrices,
only at the start and end of a fixed time range. For specifying more
complex motion, a more flexible approach may be useful. One improvement is
to allow the user to specify an arbitrary number of keyframe
transformations, each associated with an arbitrary time.
More generally, the system could be extended to support transformations
that are explicit functions of time. For example, a rotation could be
described with an expression of the form Rotate (time * 2 + 1) 0 0 1
to describe a time-varying rotation about the axis. Extend pbrt to
support a more general matrix animation scheme, and render images showing
results that aren’t possible with the current implementation. What is the
performance impact of your changes for scenes with animated objects that
don’t need the generality of your improvements?
③ Extend pbrt’s API to have some retained mode semantics so that
animated sequences of images can be rendered without needing to respecify
the entire scene for each frame. Make sure that it is possible to remove
some objects from the scene, add others, modify objects’ materials and
transformations from frame to frame, and so on.
② In the current implementation, a
unique TransformedPrimitive is created for each Shape with an
animated transformation. If many shapes have exactly the same animated
transformation, this turns out to be a poor choice. Consider the
difference between a million-triangle mesh with an animated transformation
versus a million independent triangles, all of which happen to have the
same animated transformation.
In the first case, all of the triangles in the mesh are stored in a
single instance of a TransformedPrimitive with an animated transformation. If a
ray intersects the conservative bounding box that encompasses all of the
object’s motion over the frame time, then it is transformed to the mesh’s
object space according to the interpolated transformation at the ray’s
time. At this point, the intersection computation is no different from the
intersection test with a static primitive; the only overhead due to the
animation is from the larger bounding box and rays that hit the bounding
box but not the animated primitive and the extra computation for matrix
interpolation and transforming each ray once, according to its time.
In the second case, each triangle is stored in its
own TransformedPrimitive, all of which happen to have the
same AnimatedTransform. Each instance of TransformedPrimitive will
have a large bounding box to encompass each triangle’s motion, giving the
acceleration structure a difficult set of inputs to deal with: many
primitives with substantially overlapping bounding boxes. The impact on
ray–primitive intersection efficiency will be high: the ray will be
redundantly transformed many times by what happens to be the same
recomputed interpolated transformation, and many intersection tests will be
performed due to the large bounding boxes. Overall performance will be
much worse than the first case.
To address this case, modify the code that implements the pbrt API calls
so that if independent shapes are provided with the same animated
transformation, they’re all collected into a single acceleration structure
with a single animated transformation. What is the performance improvement
for the worst case outlined above? Is there an impact for more typical
scenes with animated primitives?