3.5 Normals

A surface normal (or just normal) is a vector that is perpendicular to a surface at a particular position. It can be defined as the cross product of any two nonparallel vectors that are tangent to the surface at a point. Although normals are superficially similar to vectors, it is important to distinguish between the two of them: because normals are defined in terms of their relationship to a particular surface, they behave differently than vectors in some situations, particularly when applying transformations. (That difference is discussed in Section 3.10.)

<<Normal3 Definition>>= 
template <typename T> class Normal3 : public Tuple3<Normal3, T> { public: <<Normal3 Public Methods>> 
using Tuple3<Normal3, T>::x; using Tuple3<Normal3, T>::y; using Tuple3<Normal3, T>::z; using Tuple3<Normal3, T>::HasNaN; using Tuple3<Normal3, T>::operator+; using Tuple3<Normal3, T>::operator*; using Tuple3<Normal3, T>::operator*=; Normal3() = default; PBRT_CPU_GPU Normal3(T x, T y, T z) : Tuple3<pbrt::Normal3, T>(x, y, z) {} template <typename U> PBRT_CPU_GPU explicit Normal3<T>(Normal3<U> v) : Tuple3<pbrt::Normal3, T>(T(v.x), T(v.y), T(v.z)) {} template <typename U> explicit Normal3<T>(Vector3<U> v) : Tuple3<pbrt::Normal3, T>(T(v.x), T(v.y), T(v.z)) {}

<<Normal3 Definition>>+= 
using Normal3f = Normal3<Float>;

The implementations of Normal3s and Vector3s are very similar. Like vectors, normals are represented by three components xy, and z; they can be added and subtracted to compute new normals; and they can be scaled and normalized. However, a normal cannot be added to a point, and one cannot take the cross product of two normals. Note that, in an unfortunate turn of terminology, normals are not necessarily normalized.

In addition to the usual constructors (not included here), Normal3 allows conversion from Vector3 values given an explicit typecast, similarly to the other Tuple2- and Tuple3-based classes.

<<Normal3 Public Methods>>= 
template <typename U> explicit Normal3<T>(Vector3<U> v) : Tuple3<pbrt::Normal3, T>(T(v.x), T(v.y), T(v.z)) {}

The Dot() and AbsDot() functions are also overloaded to compute dot products between the various possible combinations of normals and vectors. This code will not be included in the text here. We also will not include implementations of all the various other Normal3 methods here, since they are similar to those for vectors.

One new operation to implement comes from the fact that it is often necessary to flip a surface normal so it lies in the same hemisphere as a given vector—for example, the surface normal that lies in the same hemisphere as a ray leaving a surface is frequently needed. The FaceForward() utility function encapsulates this small computation. (pbrt also provides variants of this function for the other three combinations of Vector3s and Normal3s as parameters.) Be careful when using the other instances, though: when using the version that takes two Vector3s, for example, ensure that the first parameter is the one that should be returned (possibly flipped) and the second is the one to test against. Reversing the two parameters will give unexpected results.

<<Normal3 Inline Functions>>= 
template <typename T> Normal3<T> FaceForward(Normal3<T> n, Vector3<T> v) { return (Dot(n, v) < 0.f) ? -n : n; }