Rotate

How to 3D

Chapter 2: Triangles and Transformations

Rotate

To rotate a model, you apply some trigonometric operations to each of its positions. In two dimensions, the amount of rotation is determined by an angle, which is a scalar rather than a vector. By mathematical convention, a positive angle rotates the model counterclockwise.

Let's figure out what operations need to be applied to rotate a point. First we recognize that a point \(\mathbf{p}\) can be expressed either in Cartesian coordinates or in polar coordinates as a radius and angle:

$$\begin{align*} \mathbf{p} &= \begin{bmatrix}x & y\end{bmatrix} &\leftarrow \text{Cartesian}\\ &= (r, a) &\leftarrow \text{polar} \end{align*}$$

Suppose you know the Cartesian coordinates of \(\mathbf{p}\). These can be converted to polar coordinates. Step through the following sequence to see how Cartesian coordinates and polar coordinates relate:

The trigonometric identities you learned earlier in life tell you that the Cartesian coordinates and polar coordinates have this relationship:

$$\begin{aligned} x &= r \times \cos a \\ y &= r \times \sin a \end{aligned}$$

Hold onto these equations. We'll recall them in a moment.

Here then is the problem of rotation. You have an existing point \(\mathbf{p}\), and you want to rotate it \(b\) radians to compute the rotated point \(\mathbf{p}'\). In polar coordinates, \(\mathbf{p}'\) is a lot like \(\mathbf{p}\). It's just rotated a bit more:

$$\begin{aligned} \mathbf{p} &= (r, a) \\ \mathbf{p}' &= (r, a + b) \\ \end{aligned}$$

Now we apply the polar-to-Cartesian formulas from above to find the rotated point's Cartesian coordinates:

$$\begin{aligned} x' &= r \times \cos~(a + b) \\ y' &= r \times \sin~(a + b) \\ \end{aligned}$$

Those cosine and sine of sums may be expanded with help from these trigonometric sum identities that you may not remember:

$$\begin{aligned} \cos~(a + b) &= \cos a \times \cos b - \sin a \times \sin b \\ \sin~(a + b) &= \sin a \times \cos b + \cos a \times \sin b \end{aligned}$$

After expanding the sums, we arrive at these seemingly unhelpful equations:

$$\begin{aligned} x' &= r \times (\cos a \times \cos b - \sin a \times \sin b) \\ y' &= r \times (\sin a \times \sin b + \cos a \times \cos b) \\ \end{aligned}$$

But wait. We can simplify a bit. First we distribute \(r\):

$$\begin{aligned} x' &= r \times \cos a \times \cos b - r \times \sin a \times \sin b \\ y' &= r \times \sin a \times \sin b + r \times \cos a \times \cos b \\ \end{aligned}$$

Do you see that \(r \times \cos a\)? You know what that is? That's \(x\). You saw it earlier as the Cartesian x-coordinate of unrotated \(\mathbf{p}\). And \(r \times \sin a\) is \(y\). We substitute these in:

$$\begin{aligned} x' &= x \times \cos b - y \times \sin b \\ y' &= x \times \sin b + y \times \cos b \end{aligned}$$

And those are our equations for rotating. We combine the x- and y-coordinates of the unrotated position \(\mathbf{p}\) together with some trigonometric functions of the angle of rotation.

This vertex shader rotates the incoming position property by the uniform radians:

uniform float radians;
in vec3 position;

void main() {
  gl_Position = vec4(
    position.x * cos(radians) - position.y * sin(radians),
    position.x * sin(radians) + position.y * cos(radians),
    position.z,
    1.0
  );
}
uniform float radians;
in vec3 position;

void main() {
  gl_Position = vec4(
    position.x * cos(radians) - position.y * sin(radians),
    position.x * sin(radians) + position.y * cos(radians),
    position.z,
    1.0
  );
}

On the CPU side, we supply a value for radians:

shader.setUniform1f('radians', 0.1);
shader.setUniform1f('radians', 0.1);

There's no vectorized operation for rotation like there is for translation and scaling—at least not a simple one. In the next chapter we'll learn how all three transformations can be expressed as matrix operations, which will make them fast to compute and easier to combine.

← ScaleLecture: Triangles →