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.
This difference is discussed in Section 2.8.
=
template <typename T> inline Normal3<T>
Normalize(const Normal3<T> &n) {
return n / n.Length();
}
#ifndef NDEBUG
Normal3<T>(const Normal3<T> &n) {
Assert(!n.HasNaNs());
x = n.x; y = n.y; z = n.z;
}
Normal3<T> &operator=(const Normal3<T> &n) {
Assert(!n.HasNaNs());
x = n.x; y = n.y; z = n.z;
return *this;
}
#endif // !NDEBUG
friend std::ostream& operator<<(std::ostream& os, const Normal3<T> &v) {
os << "[" << v.x << ", " << v.y << ", " << v.z << "]";
return os;
}
explicit Normal3<T>(const Vector3<T> &v) : x(v.x), y(v.y), z(v.z) {
Assert(!v.HasNaNs());
}
bool operator==(const Normal3<T> &n) const {
return x == n.x && y == n.y && z == n.z;
}
bool operator!=(const Normal3<T> &n) const {
return x != n.x || y != n.y || z != n.z;
}
T operator[](int i) const {
Assert(i >= 0 && i <= 2);
if (i == 0) return x;
if (i == 1) return y;
return z;
}
T &operator[](int i) {
Assert(i >= 0 && i <= 2);
if (i == 0) return x;
if (i == 1) return y;
return z;
}
<<Normal3 Public Data>>
T x, y, z;
};
<<Normal Declarations>>+=
typedef Normal3<Float> Normal3f;
The implementations of Normal3s and Vector3s are very similar. Like
vectors, normals are represented by three components x, y,
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.
Normal3 provides an extra constructor that initializes a
Normal3 from a Vector3. Because Normal3s and Vector3s
are different in subtle ways, we want to make sure that this conversion
doesn’t happen when we don’t intend it to, so the C++ explicit keyword
is again used here. Vector3 also provides a constructor that
converts the other way.
Thus, given the declarations Vector3f v; and Normal3f n;, then the assignment
n = v is illegal, so it is necessary to explicitly convert the vector, as in
n = Normal3f(v).
The Dot() and AbsDot() functions are also overloaded to compute
dot products between the various possible combinations of normals and
vectors. This code won’t be included in the text here. We also won’t include
implementations of all of the various other Normal3 methods here,
since they are similar to those for vectors.
One new operation to implement comes from the fact it’s often necessary to
flip a surface normal so that it lies in the same hemisphere as a given
vector—for example, the surface normal that lies in the same hemisphere
as an outgoing ray 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.