Courses

Q. How many classes have used the book previously? At which universities?

A. Thus far, the book has been used as the primary textbook in over seventy different graphics courses at over twenty different universities. (It has been used as a secondary textbook in a number of other courses.) Most of these courses have been advanced undergraduate or graduate courses focusing on rendering. The courses page has links to the homepages of many of these courses.

Q. Are solutions available to the exercises in the book?

A. Unfortunately, no. Many of the exercises are substantial programming projects, or even open research questions.

Using the system

Q. I am getting a run-time warning about infinite or not-a-number (NaN) values returned for image samples. What's going on?

A. The Scene::Render() method checks the radiance values returned by the integrators to see if they look like they may indicate errors elsewhere in the system. If such a value is found, it prints a warning message. The source of such errors is worth tracking down, as they are indicative of a bug in the system.

There are a few common scenarios for how these values can occur. Most commonly, a Shape returns infinite or NaN values in the DifferentialGeometry at the ray intersection point. These values then propagate through the computations by the integrators and back into the returned values.

Another common case is the Monte Carlo sampling routines. While we believe that the current implementation should never compute bogus values like these, if you're adding a new light source or BRDF, it's possible that errors in their sampling routines can lead to these values.

Another candidate can be the camera rays (most likely if you're adding a new camera plugin). Make sure that the origin, direction and ray weight are all reasonable.

One good way to debug these sorts of errors is to see what value the cameraRaysTraced variable in Scene::Render() has for the ray that take on an infinite or NaN value. Then, set a conditional breakpoint at the top of the main while loop of that method that hits only when cameraRaysTraced has that value. Then, carefully step through all of the computations for that ray until the source of the error is found.

Q. Does pbrt ensure that the BRDFs used in a scene are energy conserving?

A. No. It is quite possible to create a scene where surfaces (incorrectly) reflect more light than arrives at them. As a user, you must set material properties carefully to ensure energy conservation. (As a general principle, if you're using materials that take reflection coefficients as parameters, you should ensure that the sum of their values is less than or equal to one.)

While pbrt could (and possibly should) enforce energy conservation in simple cases, it's hard to do in the general case; consider a material given by a mix of a measured spatially-varying BRDF and a diffuse BRDF with spatially-varying reflectance properties computed, where the mixture coefficients are computed procedurally. Without complete insight into all of the the procedural mixing functions, it's impossible to know with certainty whether all combinations of them at specific points in the scene will be energy conserving at that point or not.

Q. What changes have been made to the file format between the first and second editions of the book?

A. Most existing pbrt scene files will work unmodified with the second version of the system. The most significant user-visible change is that we have taken this opportunity to fix the long-standing bug where LookAt inadvertently flipped the handedness of the coordinate system. You may find that scene files that use LookAt now render flipped images or otherwise have problems with the camera positioning. Add Scale -1 1 1 to the top of any scene description files with this problem to return to the previous camera position.

See the file README.txt in the pbrt source distribution for more details on changes to the system's functionality (and corresponding file format changes).

System implementation and internals

Q. How can a Camera implementation indicate that no ray should be traced for the given Sample value passed to its GenerateRay() method?

A. For some Cameras, not all rays will make it through the lens system. (For example, for a camera that models a realistic lens system, some rays will be blocked by elements inside the lens system.) In order to prevent rays from being traced for these samples, the camera just needs to return a value of zero for the ray's weight. The main rendering loop skips rays with a weight of zero.

Q. How come the range of pixel values that the Sampler needs to compute sample values for is more than the range from (0,0) to (xResolution, yResolution)?

A. There are two factors that affect the range of pixels for which Samplers need to generate samples: the crop window, if any, and the pixel reconstruction filter's extent. The first of these can reduce the number of pixels that need samples generated for them, and the second can increase it. In particular, it is often necessary to generate samples for pixels with negative coordinates and pixels with coordinates slightly greater than x/yResolution. (See discussion on pp. 376-377.)

Extending the system

Q. How do I add a new Shape (or Camera, or Material, or ...) to the system?

A. There is some discussion of this on page 1073 of the second edition of the book.

In general, you need to write code that has an implementation of the corresponding abstract base class (Shape, Camera, etc.), and modify the build rules to compile and link your code into the system.

Then, you need to modify the corresponding object creation function in core/api.cpp (for example, MakeShape() for Shapes), so that when the corresponding shape name is provided in a Shape statement in a scene description file, the provided parameters are passed along to your object creation function.

Q. How do I add a new BRDF to the system?

A. Recall that instances of the Material class create BRDFs and add them to the BSDFs they return. Therefore, you should implement a new Material class and include the definition of the new BRDF inside the Material's file. Then create a new instance of the BRDF in the Material::ComptueScatteringFunctions() method, add it to the BSDF, and you're done.