Quaternions

How to 3D

Chapter 4: Transformation Machinery

Quaternions

Imagine working on a game in which players can fast-travel. Once they select their destination on a spinnable globe interface, an animation shows them teleporting between their start and end positions. How do we know the avatar's location at each frame of the animation? We could linearly interpolate between the two positions, but that might not produce the intended effect.

Click on the big globe in this renderer to make the small sphere travel. What happens if the start and end positions are far apart?

Linear interpolation follows the shortest and straightest path between two positions. If the surface between the positions is curved, the interpolated positions will not be on the surface. We need a more sophisticated interpolant. For a spherical surface, we need spherical linear interpolation, or slerp. Switch the renderer to use slerp and see how the small sphere glides along the globe's surface.

We slerp by interpolating a special data structure called a quaternion rather than a vector of Cartesian components. A quaternion is a vector of 4-elements that represents a rotation. The xyz-components can be thought of as a vector related to the axis of rotation, and the w-component as a scalar related to the angle of rotation. We won't dig deeper than that into their mathematical significance. A superficial understanding is enough is implement and use a Quaternion class. Like vectors, quaternions can be combined or operated on to achieve certain effects. Later on, we'll need such a class in order to render animated models because the glTF format stores rotations as quaternions instead of matrices.

Create file lib/quaternion.ts and paste in your code from the following exercises. Some of the code will be very similar to code you wrote for Vector3.

We'll test this class next chapter when we assemble animated scenes.

Summary

We have achieved a unified interface for scaling, rotation, and translation by reframing them as dot products between vectors of coefficients and a vertex position. This reframing requires some contriving: many components of the position must be canceled with coefficients of 0, and translation requires a homogeneous coordinate of 1 to be tacked on to the position. The payoff is speed and composability. Once we see these transformations as dot products, the next step is to stack the coefficient vectors as the rows of a matrix. We evaluate the dot products—we apply the transformation—by multiplying the matrix and a vertex position. Graphics cards are engineered to perform such multiplications quickly. A series of transformations is expressed as a chain of multiplications. Because matrix multiplication is associative, the matrices may be premultiplied. Premultiplication happens just once on the CPU instead of repeatedly in the vertex or fragment shaders. Rotation is sometimes expressed as a quaternion rather than a matrix. Quaternions are more compact than matrices. We use them to interpolate between rotations.

← Matrix4