3.6 Rays

A ray normal r is a semi-infinite line specified by its origin normal o and direction bold d ; 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.

Figure 3.8: A ray is a semi-infinite line defined by its origin normal o and its direction vector bold d .

<<Ray Definition>>= 
class Ray { public: <<Ray Public Methods>> 
PBRT_CPU_GPU bool HasNaN() const { return (o.HasNaN() || d.HasNaN()); } std::string ToString() const; Point3f operator()(Float t) const { return o + d * t; } Ray(Point3f o, Vector3f d, Float time = 0.f, Medium medium = nullptr) : o(o), d(d), time(time), medium(medium) {}
<<Ray Public Members>> 
Point3f o; Vector3f d; Float time = 0; Medium medium = nullptr;
};

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.

<<Ray Public Members>>= 

The parametric form of a ray expresses it as a function of a scalar value t , giving the set of points that the ray passes through:

normal r left-parenthesis t right-parenthesis equals normal o plus t bold d Baseline 0 less-than-or-equal-to t less-than normal infinity period
(3.4)

The Ray class overloads the function application operator for rays in order to match the normal r left-parenthesis t right-parenthesis notation in Equation (3.4).

<<Ray Public Methods>>= 
Point3f operator()(Float t) const { return o + d * t; }

Given this method, when we need to find the point at a particular position along a ray, we can write code like:

Ray r(Point3f(0, 0, 0), Vector3f(1, 2, 3)); Point3f p = r(1.7);

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.

<<Ray Public Members>>+=  
Float time = 0;

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.

<<Ray Public Members>>+= 
Medium medium = nullptr;

Constructing Rays is straightforward. The default constructor relies on the Point3f and Vector3f constructors to set the origin and direction to left-parenthesis 0 comma 0 comma 0 right-parenthesis . 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.

<<Ray Public Methods>>+= 
Ray(Point3f o, Vector3f d, Float time = 0.f, Medium medium = nullptr) : o(o), d(d), time(time), medium(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 x and y 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.

<<RayDifferential Definition>>= 
class RayDifferential : public Ray { public: <<RayDifferential Public Methods>> 
RayDifferential(Point3f o, Vector3f d, Float time = 0.f, Medium medium = nullptr) : Ray(o, d, time, medium) {} explicit RayDifferential(const Ray &ray) : Ray(ray) {} void ScaleDifferentials(Float s) { rxOrigin = o + (rxOrigin - o) * s; ryOrigin = o + (ryOrigin - o) * s; rxDirection = d + (rxDirection - d) * s; ryDirection = d + (ryDirection - d) * s; } PBRT_CPU_GPU bool HasNaN() const { return Ray::HasNaN() || (hasDifferentials && (rxOrigin.HasNaN() || ryOrigin.HasNaN() || rxDirection.HasNaN() || ryDirection.HasNaN())); } std::string ToString() const;
<<RayDifferential Public Members>> 
bool hasDifferentials = false; Point3f rxOrigin, ryOrigin; Vector3f rxDirection, ryDirection;
};

The RayDifferential constructor mirrors the Ray’s.

<<RayDifferential Public Methods>>= 
RayDifferential(Point3f o, Vector3f d, Float time = 0.f, Medium medium = nullptr) : Ray(o, d, time, medium) {}

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.

<<RayDifferential Public Members>>= 
bool hasDifferentials = false; Point3f rxOrigin, ryOrigin; Vector3f rxDirection, ryDirection;

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.

<<RayDifferential Public Methods>>+=  
explicit RayDifferential(const Ray &ray) : Ray(ray) {}

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.

<<RayDifferential Public Methods>>+= 
void ScaleDifferentials(Float s) { rxOrigin = o + (rxOrigin - o) * s; ryOrigin = o + (ryOrigin - o) * s; rxDirection = d + (rxDirection - d) * s; ryDirection = d + (ryDirection - d) * s; }