## 3.10 Applying Transformations

We can now define routines that perform the appropriate matrix multiplications to transform points and vectors. We will overload the function application operator to describe these transformations; this lets us write code like:

### 3.10.1 Points

The point transformation routine takes a point and implicitly
represents it as the homogeneous column vector It
then transforms the point by premultiplying this vector with the
transformation matrix. Finally, it divides by to convert back to a
nonhomogeneous point representation. For efficiency, this method skips
the division by the homogeneous weight, , when , which is common for
most of the transformations that will be used in `pbrt`—only the
projective transformations defined in Chapter 5 will
require this division.

The `Transform` class also provides a corresponding
`ApplyInverse()` method for each type it transforms. The one
for `Point3` applies its inverse transformation to the given point.
Calling this method is more succinct and generally more efficient than
calling `Transform::Inverse()` and then calling its `operator()`.

All subsequent types that can be transformed also have an
`ApplyInverse()` method, though we will not include them in the book text.

### 3.10.2 Vectors

The transformations of vectors can be computed in a similar fashion. However, the multiplication of the matrix and the column vector is simplified since the implicit homogeneous coordinate is zero.

### 3.10.3 Normals

Normals do not transform in the same way that vectors do, as shown in Figure 3.29. Although tangent vectors at a surface transform in the straightforward way, normals require special treatment. Because the normal vector and any tangent vector on the surface are orthogonal by construction, we know that

When we transform a point on the surface by some matrix , the new tangent vector at the transformed point is . The transformed normal should be equal to for some 44 matrix . To maintain the orthogonality requirement, we must have

This condition holds if , the
identity matrix. Therefore, , and
so
and we see that normals must be
transformed by the inverse transpose of the transformation matrix. This
detail is
one of the reasons why `Transform`s maintain their inverses.

Note that this method does not explicitly compute the transpose of the
inverse when transforming normals. It just indexes into the inverse matrix
in a different order (compare to the code for transforming `Vector3f`s).

### 3.10.4 Rays

Transforming rays is conceptually straightforward: it is a matter
of transforming the constituent origin and direction and copying the other
data members. (`pbrt` also provides a similar method for transforming
`RayDifferential`s.)

The approach used in `pbrt` to manage floating-point round-off error
introduces some subtleties that require a small adjustment to the
transformed ray origin. The <<Offset ray origin to edge of error
bounds and compute `tMax`>> fragment handles these details; it is defined in
Section 6.8.6, where round-off error and `pbrt`’s
mechanisms for dealing with it are discussed.

`tMax`>>

### 3.10.5 Bounding Boxes

The easiest way to transform an axis-aligned bounding box is to transform all eight of its corner vertices and then compute a new bounding box that encompasses those points. The implementation of this approach is shown below; one of the exercises for this chapter is to implement a technique to do this computation more efficiently.

### 3.10.6 Composition of Transformations

Having defined how the matrices representing individual types of transformations are constructed, we can now consider an aggregate transformation resulting from a series of individual transformations. We will finally see the real value of representing transformations with matrices.

Consider a series of transformations . We would
like to compute a new transformation such that applying gives
the same result as applying each of , , and in
reverse order; that is, . Such a transformation can be computed by
multiplying the matrices of the transformations , , and
together. In `pbrt`, we can write:

Then we can apply `T` to `Point3f`s `p` as
usual, `Point3f pp = T(p)`,
instead of applying each transformation in turn: `Point3f pp = A(B(C(p)))`.

We overload the C++ `*` operator in the `Transform` class to
compute the new transformation that results
from postmultiplying a transformation with another transformation
`t2`. In matrix multiplication, the th element of
the resulting matrix is the inner product of the th row of the first
matrix with the th column of the second.

The inverse of the resulting transformation is equal to the product of
`t2.mInv * mInv`. This is a result of the matrix
identity

### 3.10.7 Transformations and Coordinate System Handedness

Certain types of transformations change a left-handed coordinate system into a right-handed one, or vice versa. Some routines will need to know if the handedness of the source coordinate system is different from that of the destination. In particular, routines that want to ensure that a surface normal always points “outside” of a surface might need to flip the normal’s direction after transformation if the handedness changes.

Fortunately, it is easy to tell if handedness is changed by a transformation: it happens only when the determinant of the transformation’s upper-left 33 submatrix is negative.

### 3.10.8 Vector Frames

It is sometimes useful to define a rotation that aligns three
orthonormal vectors in a coordinate system with the , , and
axes. Applying such a transformation to direction vectors in that coordinate
system can simplify subsequent computations. For example, in `pbrt`, BSDF
evaluation is performed in a coordinate system where the surface normal is
aligned with the axis. Among other things, this makes it possible
to efficiently evaluate trigonometric functions using functions like the `CosTheta()`
function that was introduced in Section 3.8.3.

The `Frame` class efficiently represents and performs such
transformations, avoiding the full generality (and hence, complexity) of
the `Transform` class. It only needs to store a matrix,
and storing the inverse is unnecessary since it is just the matrix’s
transpose, given orthonormal basis vectors.

Given three orthonormal vectors , , and , the matrix that transforms vectors into their space is

The `Frame` stores this matrix using three `Vector3f`s.

The three basis vectors can be specified explicitly; in debug builds,
`DCHECK()`s in the constructor ensure that the provided vectors
are orthonormal.

`Frame` also provides convenience methods that construct a frame from
just two of the basis vectors, using the cross product to compute the
third.

Only the axis vector can be provided as well, in which case the others are set arbitrarily.

A variety of other functions, not included here, allow specifying a frame using a normal vector and specifying it via just the or basis vector.

Transforming a vector into the frame’s coordinate space is done using the
matrix. Because `Vector3f`s were used to store its rows, the
matrix-vector product can be expressed as three dot products.

A `ToLocal()` method is also provided for normal vectors.
In this case, we do not need to compute the inverse transpose of
for the transformation normals (recall the discussion of transforming normals in
Section 3.10.3). Because is an orthonormal
matrix (its rows and columns are mutually orthogonal and unit length),
its inverse is equal to its transpose, so it is its own inverse transpose
already.

The method that transforms vectors out of the frame’s local space transposes to find its inverse before multiplying by the vector. In this case, the resulting computation can be expressed as the sum of three scaled versions of the matrix columns. As before, surface normals transform as regular vectors. (That method is not included here.)

For convenience, there is a `Transform` constructor that takes a
`Frame`. Its simple implementation is not included here.