Monday, August 27, 2018

Rotation maths

My previous post was designed to give you some intuition when working with quaternions. If you already have a library that does multiplications and inverses for you, you should be fine. If you'd like to learn more details or don't have a quaternion library, this blog post is for you.

How do we rotate something by some axis $V=(v_x, v_y, v_z)$ and some angle $\theta_V$?

To answer that, lets start with something simpler first: we ball with a stick in it, the stick is pointing in direction $D=(d_x, d_y, d_z)$. We want to rotate the ball via $V$ to get a new stick direction $R=(r_x, r_y, r_z)$.

We can do this by using Rodrigues' rotation formula.

$R = cos(\theta) D + sin(\theta) (V \times D) + (1-cos(\theta))(V \cdot D) V$

where $\times$ is the cross product (returns a vector)

$(V \times D)_x = (v_y d_z - v_z d_y)$
$(V \times D)_y = (v_z d_x - v_x d_z)$
$(V \times D)_z = (v_x d_y - v_y d_x)$

and $\cdot$ is the dot product (returns a single value)

$(V \cdot D) = v_xd_x + v_yd_y + v_zd_z$

This formula comes from splitting D into two pieces: $D_{\Vert}$, the part that is parallel to V, and $D_{\perp}$, the part that is perpendicular to V. Assuming $V$ and $D$ are normalized,  $D_{\Vert}=(V \cdot D)V$ is the vector projection of $D$ onto $V$, and then $D_{\perp}=D-D_{\Vert}$ (also known as the vector rejection of $D$ onto $V$).

Then we can find $R_{\Vert}$ and $R_{\perp}$ and get $R=R_{\Vert}+R_{\perp}$. (this isn't quite right, there are weighting terms on $R_{\Vert}$ and $R_{\perp}$ based on $\theta_V$, but this is a good intuition)

$D_{\Vert}$ is unaffected by the rotation, so $R_{\Vert}=D_{\Vert}$. 

$D_{\perp}$ is rotated $\theta_V$ degrees around $V$. If $\theta_V=90$, $R_{perp}$ will be perpendicular to $D_{\perp}$ and $V$. The cross product of any two vectors is known to return a vector that is perpendicular to them both, so if $\theta_V=90$ degrees, $R_{\perp} = V \times D_{\perp}$. Likewise, if $\theta_V=-90$ degrees, $R_{\perp}= V \times D_{\perp}$, and if $\theta_V=180$ degrees then $R_{\perp}=-D_{\perp}$.

The math for getting $R_{\perp}$ is then identical to what happens when you rotate the 2D vector $(1,0)$ by $\theta_V$ degrees around the origin, just replace $(1,0)$ with $D_{\perp}$ and $(0,1)$ with $V \times D_{\perp}$. See the Rodrigues' Rotation Formula wiki page for the full details.


If I have axis angle $V=(v_x, v_y, v_z), v_{\theta}$ and another axis angle $U=(u_x, u_y, u_z), u_{\theta}$, intuitively how we combine them is easy: we apply V to an object, then apply U to an object. But can we make a new axis angle $W=(w_x, w_y, w_z), w_{\theta}$ so that when we apply $W$, it is the same as applying $V$ and then applying $U$?

Quaternions are just a weird encoding of a rotation around a vector

Whenever the topic of Quaternions comes up, programmers I talk to are always annoyed by them. Quaternions seem useful, but tutorials that give nice intuitive pictures are very hard to find. Just look at the Wikipedia page and you'll see what I mean.

Let's fix that :)

The takeaway here is this: a single Quaternion is equivalent to an axis (3D unit vector) and a rotation θ degrees around that axis. Quaternions are just a weird encoding of these 4 values that make some computations nice.

How do we get the angle axis representation? Given a quaternion $Q=(q_x,q_y,q_z,q_w)$, our axis of rotation $V=(v_x, v_y, v_z)$ is:

$v_x = q_x / \sqrt{1-q_w*q_w}$
$v_y = q_y / \sqrt{1-q_w*q_w}$
$v_z = q_z / \sqrt{1-q_w*q_w}$

and our rotation angle $\theta_V$

$\theta_V = 2 * acos(q_w)$

You can just call this $\theta$, I just use this notation to make it clear that this is the axis $V$'s angle and not some other axis $W=(w_x, w_y, w_z)$'s angle (which would be $\theta_W$).

Cool so that's nice. Intuitively, rotating objects with these is nice: we stick the axis $V$ pointing out from the center of that object, then rotate the object $\theta_V$ around it. But how do we represent this in code? If you have a library (such as Unity) that has quaternions, just use their code. If not, see my follow up blog post.

The important takeaway here is that every time you use a quaternion, just imagine it as an angle axis in your head.

For example, given two quaternions Q_V and Q_U that by using the formula above map to angle axes $V=(v_x, v_y, v_z), \theta_V$ and $U=(u_x, u_y, u_z), \theta_U$, if you do $Q_V*Q_U$ that is equivalent to a new angle axis that rotates by V and U.

What order do these happen in? Well, rotations are weird in that they are associative (you can swap parenthesis, so $(Q_V*Q_U)Q_W=Q_V*(Q_U*Q_W)$), but they aren't communicative so $Q_V*Q_W$ isn't always equal to $Q_W*Q_V$. So if want to rotate some $M$,


means: rotate M by V, and then by U



means: rotate M by U, and then by V.

The main reason we use quaternions (as far as I know) is that to rotate positions and vectors using angle-axis, you end up using some sins, cos, dot products, etc. With quaternions you can rotate positions and vectors via linear functions that you can just represent as a 4x4 matrix.