② Shadow mapping is a technique for rendering shadows from
point and distant light sources based on rendering an image from the light
source’s perspective that records depth in each pixel of the image and then
projecting points onto the shadow map and comparing their depth to the
depth of the first visible object as seen from the light in that direction.
This method was first described by Williams (1978), and
Reeves, Salesin, and Cook (1987) developed a number of key improvements.
Modify pbrt to be able to render depth map images into a file and then
use them for shadow testing for lights in place of tracing shadow rays.
How much faster can this be? Discuss the advantages and disadvantages of
the two approaches.
① Through algebraic manipulation and precomputation of one
more value in the constructor, the SpotLight::Falloff() method can be
rewritten to compute the exact same result (modulo floating-point
differences) while using no square root computations and no divides (recall
that the Vector3::Normalize() method performs both a square root and a
divide). Derive and implement this optimization. How much is running time
improved on a spotlight-heavy scene?
① The functionality of the SpotLight could be
replicated by using a suitable image in conjunction with the
ProjectionLight. Discuss the advantages and disadvantages of
providing this specific functionality separately with the SpotLight
class.
③ The current light source implementations don’t support
animated transformations. Modify pbrt to include this functionality, and
render images showing off the effect of animating light positions.
② Modify the ProjectionLight to also support orthographic
projections. This variant is particularly useful even without an image
map, since it gives a directional light source with a beam of user-defined
extent.
② Write an AreaLight implementation that improves on
the DiffuseAreaLight by supporting spatially and directionally
varying emitted radiance, specified via either image maps or
Textures. Use it to render images with effects like a television
illuminating a dark room or a stained-glass window lit from behind.
② Many of the Light::Power() method implementations
only compute approximations to the actual emitted power for their lights.
In particular, all of the lights that use images (ProjectionLight,
GonioPhotometricLight, and InfiniteAreaLight) all neglect the fact
that for each of them, different pixels subtend different solid angles and
therefore contribute differently to the emitted power. Derive accurate
models for the emitted power of these light sources, and implement them in
pbrt. How much error do the current implementations have when used in
some of the pbrt example scenes? Can you construct contrived scenes to
show the maximum error introduced by the current implementation?
② Read some of the papers in the “Further Reading”
section that discuss the shadow cache, and add this optimization to pbrt.
Measure how much it speeds up the system for a variety of scenes. What
techniques can you come up with that make it work better in the presence of
multiple levels of reflection?
③ Modify pbrt to support the shaft culling algorithm
(Haines and Wallace 1994). Measure the performance difference for scenes with area
light sources. Make sure that your implementation still performs well even
with very large light sources (like a hemispherical skylight).
③ Read the paper by Velázquez-Armendáriz et al. (2015),
and implement their method for efficiently rendering scenes with complex
light sources. Create or find models of a few complex lights, including
many shapes that exhibit specular reflection and/or transmission. Compare
results using your implementation to renderings using one or more of the
bidirectional integrators from Chapter 16 (which are best suited to
handling this challenge). Note that you may need to set very long maximum
integrator path lengths for the current implementation of pbrt to be able to render
these scenes at all.
How much more efficiently does your implementation render images of scenes
lit by these lights than the built-in integrators? Do results from the
two approaches match?