A ray is a semi-infinite line specified by its origin and direction. pbrt represents a Ray with a Point3f for the origin and a Vector3f for the direction. We only need rays with floating-point origins and directions, so Ray isn’t a template class parameterized by an arbitrary type, as points, vectors, and normals were. A ray is denoted by ; it has origin and direction , as shown in Figure 2.7.
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 also includes a member variable that limits the ray to a segment along its infinite extent. This field, tMax, allows us to restrict the ray to a segment of points .
Notice that this field is declared as mutable, meaning that it can be changed even if the Ray that contains it is const—thus, when a ray is passed to a method that takes a const Ray &, that method is not allowed to modify its origin or direction but can modify its extent. This convention fits one of the most common uses of rays in the system, as parameters to ray–object intersection testing routines, which will record the offsets to the closest intersection in tMax.
Each ray 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.
Finally, each ray records the medium containing its origin. The Medium class, introduced in Section 11.3, encapsulates the (potentially spatially varying) properties of media such as a foggy atmosphere, smoke, or scattering liquids like milk or shampoo. 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 a value to be given for tMax, the ray’s time and medium.
Because position along a ray can be thought of as a function of a single parameter , the Ray class overloads the function application operator for rays. This way, when we need to find the point at a particular position along a ray, we can write code like:
2.5.1 Ray Differentials
In order to be able to perform better antialiasing with the texture functions defined in Chapter 10, pbrt can keep track of some additional information with rays. In Section 10.1, this information will be used to compute values that are used by the Texture class to estimate the projected area on the image plane of a small part of the scene. From this, the Texture can compute the texture’s average value over that area, leading to a higher-quality final image.
RayDifferential 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, the Texture can estimate an area to average over for proper antialiasing.
Because the RayDifferential class 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 constructors mirror the Ray’s constructors.
There is a constructor to create RayDifferentials from Rays. The constructor sets hasDifferentials to false initially because the neighboring rays, if any, are not known.
Camera implementations in pbrt compute differentials for rays leaving the camera under the assumption that camera rays are spaced one pixel apart. Integrators such as the SamplerIntegrator can generate multiple camera rays per pixel, in which case the actual distance between samples is lower. The fragment <<Generate camera ray for current sample>> encountered in Chapter 1 called the ScaleDifferentials() method defined below to update differential rays for an estimated sample spacing of s.