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.

Q. Are digital versions of the figures of the book available for use in course lectures?

A. Unfortunately, no. Feel free to use images from the gallery if they're helpful.

Using the system

Q. I'm seeing the error "/usr/bin/ld: ../../../zlib/libz.a(deflate.o): relocation R_X86_64_PC32 against symbol `_length_code' can not be used when making a shared object; recompile with -fPIC" when building on Ubuntu. How do I fix it?

A. Make sure that you have the "zlib1g-dev" package installed.

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 SamplerIntegrator::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 SurfaceInteraction 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, for example due to an occasional division by zero.

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.

How do I disable multi-threading in pbrt?

When debugging intermittent bugs that may be caused by race conditions, it can be useful to run without multi-threading; supply the command-line argument --nthreads=1 when running pbrt to do so.

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 second and third editions of the book?

A. Most existing pbrt scene files will work unmodified with the third version of the system. Please see the User's Guide for further details.

Also see the file 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 should 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 p. 487 of the third edition of Physically Based Rendering.)

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 1131 of the third 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.