③ An advantage of the way that pbrt separates parsing,
graphics state management, and the creation of scene objects is that
it is easier to replace or extend those components of the system than it
might be if all those responsibilities were in a single class.
Investigate pbrt’s parsing performance with scenes that have multi-gigabyte
*.pbrt scene description files (Disney’s Moana Island scene
(Walt Disney Animation Studios 2018) is a good choice) and develop a scene description
format for pbrt that is more efficient to parse. You might, for example,
consider a compact binary format.
Take advantage of the ParserTarget interface to write a converter
from pbrt’s current scene file format to your format and then implement
new parsing routines that call ParserTarget interface methods. Use a
profiler to measure how much time is spent in parsing before and after your
changes. What is the performance benefit from your representation? How
much smaller are file sizes?
③ Generalize pbrt’s mechanism for specifying animation; the current
implementation only allows the user to provide two transformation matrices,
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 are not possible with the current implementation. Is there a
performance cost due to your changes for scenes with animated objects that
do not need the generality of your improvements?
③ Extend pbrt 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. Measure the performance
benefit from your approach versus the current implementation. How is the
benefit affected by how fast rendering is?
② In pbrt’s current implementation, a
unique TransformedPrimitive is created for each Shape with an
animated transformation when the CPU is used for rendering. 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 the triangles in the mesh are stored in a
single instance of a TransformedPrimitive with an animated transformation. If a
ray intersects the bounding box that encompasses all 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. Performance will be
much worse than the first case.
To address this case, modify the code that creates primitives
so that if independent shapes are provided with the same animated
transformation, they are all collected into a single acceleration structure
with a single animated transformation. What is the performance improvement
for the worst case outlined above? Are there cases where the current
implementation is a better choice?