3.6 Rays
A ray is a semi-infinite line specified by its origin and direction ; see Figure 3.8. pbrt represents Rays using a Point3f for the origin and a Vector3f for the direction; there is no need for non-Float-based rays in pbrt. See the files ray.h and ray.cpp in the pbrt source code distribution for the implementation of the Ray class.
Because we will be referring to these variables often throughout the code, the origin and direction members of a Ray are succinctly named o and d. Note that we again make the data publicly available for convenience.
The parametric form of a ray expresses it as a function of a scalar value , giving the set of points that the ray passes through:
The Ray class overloads the function application operator for rays in order to match the notation in Equation (3.4).
Given this method, when we need to find the point at a particular position along a ray, we can write code like:
Each ray also has a time value associated with it. In scenes with animated objects, the rendering system constructs a representation of the scene at the appropriate time for each ray.
Each ray also records the medium at its origin. The Medium class, which will be introduced in Section 11.4, encapsulates the (potentially spatially varying) properties of participating media such as a foggy atmosphere, smoke, or scattering liquids like milk. Associating this information with rays makes it possible for other parts of the system to account correctly for the effect of rays passing from one medium to another.
Constructing Rays is straightforward. The default constructor relies on the Point3f and Vector3f constructors to set the origin and direction to . Alternately, a particular point and direction can be provided. If an origin and direction are provided, the constructor allows values to be given for the ray’s time and medium.
3.6.1 Ray Differentials
To be able to perform better antialiasing with the texture functions defined in Chapter 10, pbrt makes use of the RayDifferential class, which is a subclass of Ray that contains additional information about two auxiliary rays. These extra rays represent camera rays offset by one sample in the and direction from the main ray on the film plane. By determining the area that these three rays project to on an object being shaded, a Texture can estimate an area to average over for proper antialiasing (Section 10.1).
Because RayDifferential inherits from Ray, geometric interfaces in the system can be written to take const Ray & parameters, so that either a Ray or RayDifferential can be passed to them. Only the routines that need to account for antialiasing and texturing require RayDifferential parameters.
The RayDifferential constructor mirrors the Ray’s.
In some cases, differential rays may not be available. Routines that take RayDifferential parameters should check the hasDifferentials member variable before accessing the differential rays’ origins or directions.
There is also a constructor to create a RayDifferential from a Ray. As with the previous constructor, the default false value of the hasDifferentials member variable is left as is.
Camera implementations in pbrt compute differentials for rays leaving the camera under the assumption that camera rays are spaced one pixel apart. Integrators usually generate multiple camera rays per pixel, in which case the actual distance between samples is lower and the differentials should be updated accordingly; if this factor is not accounted for, then textures in images will generally be too blurry. The ScaleDifferentials() method below takes care of this, given an estimated sample spacing of s. It is called, for example, by the fragment <<Generate camera ray for current sample>> in Chapter 1.