Multiplying Quaternions

To understand quaternion multiplication we need to get familiar with imaginary numbers and the \(ijk\) notation of quaternions. Quaternions left multiply (post multiplication). If there are two quaternions, \(q\) which rotates 90 degrees on the x axis and \(p\) which rotates 30 degrees ont he y axis, then \(qp\) will rotate on the y axis first, then the x axis.

Distribute

We're going to start off with two quaternions, \(q\) with components \((q_0, q_1, q_2), q_3\) and \(p\) with components \((p_0, p_1, p_2), p_3\). These are in vector scalar notation with elements 0, 1, and 2 being the vector part and element 3 being the scalar part. Let's rewrite the quaternions in \(ijk\) notation. I'll color code the imaginary numbers.

$$ q = q_0\textcolor{red}{i} + q_1\textcolor{green}{j} + q_2\textcolor{blue}{k} + q_3 \\ p = p_0\textcolor{red}{i} + p_1\textcolor{green}{j} + p_2\textcolor{blue}{k} + p_3 $$

To multiply quaternions \(q\) and \(p\) together, distribute all of the components of \(p\) to the components of \(q\). For example the real component of \(p\), \(p_3\) distributes to \(q\) like so: \(p_3 q_0\textcolor{red}{i} + p_3 q_1\textcolor{green}{j} + p_3 q_2\textcolor{blue}{k} + p_3 q_3\). Distributing one of the components with an imaginary number is a little more complicated.

When distributing a component with an imaginary number, combine the real and imaginary componetns seperateley making sure to keep the order of the imaginary numbers. For example, \(p_0\textcolor{red}{i}\) would distribute to \(q\) like so: \( p_0 q_0 \textcolor{red}{i^2} + p_0 q_1 \textcolor{red}{i}\textcolor{green}{j} + p_0 q_2 \textcolor{red}{i}\textcolor{blue}{k} + p_0 q_3 \textcolor{red}{i} \). Notice how the real numbers combine first, then the imaginary ones. Because we're distributing \(p_0\textcolor{red}{i}\), the \(\textcolor{red}{i}\) imaginary number always comes first, and every term has an \(\textcolor{red}{i}\) component.

Doing the distribution for every component, \(qp\) looks like this:

$$ qp = \begin{alignedat}{6} &p_0 q_0 \textcolor{red}{i^2} & {+} &p_0 q_1 \textcolor{red}{i}\textcolor{green}{j} & {+} &p_0 q_2 \textcolor{red}{i}\textcolor{blue}{k} & {+} &p_0 q_3 \textcolor{red}{i} & {+} \\ &p_1 q_0 \textcolor{green}{j}\textcolor{red}{i}& {+} &p_1 q_1 \textcolor{green}{j^2} & {+} &p_1 q_2 \textcolor{green}{j}\textcolor{blue}{k} & {+} &p_1 q_3 \textcolor{green}{j}& {+} \\ &p_2 q_0 \textcolor{blue}{k}\textcolor{red}{i} & {+} &p_2 q_1 \textcolor{blue}{k}\textcolor{green}{j} & {+} &p_2 q_2 \textcolor{blue}{k^2} & {+} &p_2 q_3 \textcolor{blue}{k} & {+} \\ &p_3 q_0 \textcolor{red}{i} & {+} &p_3 q_1 \textcolor{green}{j} & {+} &p_3 q_2 \textcolor{blue}{k} & {+} &p_3 q_3 & \end{alignedat} $$

Let's try to get rid of those imaginary numbers. We know that \(\textcolor{red}{i^{2}} = -1\), \(\textcolor{green}{j^{2}} = -1\) and \(\textcolor{blue}{k^{2}} = -1\). These can be substituted right away:

$$ qp = \begin{alignedat}{6} &\mathbf{(p_0 q_0 * \textcolor{red}{-1})} & {+} & p_0 q_1 \textcolor{red}{i}\textcolor{green}{j} & {+} & p_0 q_2 \textcolor{red}{i}\textcolor{blue}{k} & {+} &p_0 q_3 \textcolor{red}{i} & {+} \\ & p_1 q_0 \textcolor{green}{j}\textcolor{red}{i}& {+} &\mathbf{(p_1 q_1 * \textcolor{green}{-1})} & {+} & p_1 q_2 \textcolor{green}{j}\textcolor{blue}{k} & {+} &p_1 q_3 \textcolor{green}{j}& {+} \\ & p_2 q_0 \textcolor{blue}{k}\textcolor{red}{i} & {+} & p_2 q_1 \textcolor{blue}{k}\textcolor{green}{j} & {+} &\mathbf{(p_2 q_2 \textcolor{blue}{-1})} & {+} &p_2 q_3 \textcolor{blue}{k} & {+} \\ & p_3 q_0 \textcolor{red}{i} & {+} & p_3 q_1 \textcolor{green}{j} & {+} & p_3 q_2 \textcolor{blue}{k} & {+} &p_3 q_3 & \end{alignedat} $$

But, what do we do with the imaginary numbers like \(\textcolor{red}{i}\textcolor{green}{j}\) or \(\textcolor{blue}{k}\textcolor{red}{i}\)? This is where the formula \(\textcolor{red}{i^{2}} = \textcolor{green}{j^{2}} = \textcolor{blue}{k^{2}} = \textcolor{red}{i}\textcolor{green}{j}\textcolor{blue}{k} = -1\) is needed.

ijk = -1

We can use the formula \(\textcolor{red}{i}\textcolor{green}{j}\textcolor{blue}{k} = -1\) to figure out the values for elements like \(\textcolor{green}{j}\textcolor{blue}{k}\) in the quaternion multiplication formula. Start by multiplying both sides of the equation by \(\textcolor{red}{i}\), like so: \(\textcolor{red}{i}(\textcolor{red}{i}\textcolor{green}{j}\textcolor{blue}{k}) = -\textcolor{red}{i}\). Distributing \(\textcolor{red}{i}\) leaves us with \(\textcolor{red}{i^2}\textcolor{green}{j}\textcolor{blue}{k} = -\textcolor{red}{i}\). Since \(\textcolor{red}{i^{2}} = -1\), we can substitute \(\textcolor{red}{i^{2}}\) to get: \(-\textcolor{green}{j}\textcolor{blue}{k} = -\textcolor{red}{i}\). If \(-\textcolor{green}{j}\textcolor{blue}{k} = -\textcolor{red}{i}\), then \(\textcolor{green}{j}\textcolor{blue}{k} = \textcolor{red}{i}\).

The values for \(\textcolor{blue}{k}\textcolor{red}{i}\) and \(\textcolor{red}{i}\textcolor{green}{j}\) can be found in a similar fashion to the vaue of \(\textcolor{green}{j}\textcolor{blue}{k}\):

These values can now be substituted into the multiplication formula from above:

$$ qp = \begin{alignedat}{6} &(\textcolor{red}{-}p_0 q_0) & {+} & \mathbf{p_0 q_1 \textcolor{blue}{k}} & {+} & p_0 q_2 \textcolor{red}{i}\textcolor{blue}{k} & {+} &p_0 q_3 \textcolor{red}{i} & {+} \\ & p_1 q_0 \textcolor{green}{j}\textcolor{red}{i}& {+} &(\textcolor{green}{-}p_1 q_1) & {+} & \mathbf{p_1 q_2 \textcolor{red}{i}} & {+} &p_1 q_3 \textcolor{green}{j}& {+} \\ & \mathbf{p_2 q_0 \textcolor{green}{j}} & {+} & p_2 q_1 \textcolor{blue}{k}\textcolor{green}{j} & {+} &(\textcolor{blue}{-}p_2 q_2) & {+} &p_2 q_3 \textcolor{blue}{k} & {+} \\ & p_3 q_0 \textcolor{red}{i} & {+} & p_3 q_1 \textcolor{green}{j} & {+} & p_3 q_2 \textcolor{blue}{k} & {+} &p_3 q_3 & \end{alignedat} $$

The goal is to get every component of the above equation to jave a either single imaginary number or no imaginary number. This leaves three unaccounted for imaginary numbers: \(\textcolor{red}{i}\textcolor{blue}{k}\), \(\textcolor{green}{j}\textcolor{red}{i}\) and \(\textcolor{blue}{k}\textcolor{green}{j}\). You may notice that these are in reverse order to the ones we found in the last step. When you flip the order of these numbers, just negate the result, like so:

The math for proving the values of \(\textcolor{red}{i}\textcolor{blue}{k}\), \(\textcolor{green}{j}\textcolor{red}{i}\) and \(\textcolor{blue}{k}\textcolor{green}{j}\) is skipped because it's similar to \(\textcolor{blue}{k}\textcolor{red}{i}\) and \(\textcolor{red}{i}\textcolor{green}{j}\) and \(\textcolor{green}{j}\textcolor{blue}{k}\). If the process of finding these values is confusing, this video does a great job of explaining how to find them. The linked video assumes quaternions to be in a \(w, x, y, z\) format instead of the \(x, y, z, w\) format used here.

We can finsh the multiplication formula by substituting these values:

$$ qp = \begin{alignedat}{6} & (\textcolor{red}{-}p_0 q_0) & {+} & p_0 q_1 \textcolor{blue}{k} & {+} &\mathbf{(\textcolor{green}{-}p_0 q_2 \textcolor{green}{j})} & {+} &p_0 q_3 \textcolor{red}{i} & {+} \\ &\mathbf{(\textcolor{blue}{-}p_1 q_0 \textcolor{blue}{k})} & {+} & (\textcolor{green}{-}p_1 q_1) & {+} & p_1 q_2 \textcolor{red}{i} & {+} &p_1 q_3 \textcolor{green}{j}& {+} \\ & p_2 q_0 \textcolor{green}{j} & {+} &\mathbf{(\textcolor{red}{-}p_2 q_1 \textcolor{red}{i})} & {+} & (\textcolor{blue}{-}p_2 q_2) & {+} &p_2 q_3 \textcolor{blue}{k} & {+} \\ & p_3 q_0 \textcolor{red}{i} & {+} & p_3 q_1 \textcolor{green}{j} & {+} & p_3 q_2 \textcolor{blue}{k} & {+} &p_3 q_3 & \end{alignedat} $$

Numbers with different imaginary components can't be added together. For example adding\(p_2 q_0 \textcolor{green}{j} \) and \(p_3 q_0 \textcolor{red}{i}\) is impossible. To fix this, grouping like terms together:

$$ qp = \begin{alignedat}{6} & \phantom{+} &p_0 q_3 \textcolor{red}{i} & {+} &p_1 q_2 \textcolor{red}{i} & {-} &p_2 q_1 \textcolor{red}{i} & {+} &p_3 q_0 \textcolor{red}{i} & {,} \\ & {-} &p_0 q_2 \textcolor{green}{j} & {+} &p_1 q_3 \textcolor{green}{j} & {+} &p_2 q_0 \textcolor{green}{j} & {+} &p_3 q_1 \textcolor{green}{j} & {,} \\ & \phantom{+} &p_0 q_1 \textcolor{blue}{k} & {-} &p_1 q_0 \textcolor{blue}{k} & {+} &p_2 q_3 \textcolor{blue}{k} & {+} &p_3 q_2 \textcolor{blue}{k} & {,} \\ & {-} &p_0 q_0 & {-} &p_1 q_1 & {-} &p_2 q_2 & {+} &p_3 q_3 \end{alignedat} $$

Changing from \(ijk\) notation back to vector scalar notation leaves us with the formula for quaternion multiplication. In the formula below elements 0, 1 and 2 are the vector part and element 3 is the scalar part of the resulting quaternion:

$$ qp_0 = \phantom{+}p_0 q_3 + p_1 q_2 - p_2 q_1 + p_3 q_0 \\ qp_1 = -p_0 q_2 + p_1 q_3 + p_2 q_0 + p_3 q_1 \\ qp_2 = \phantom{+}p_0 q_1 - p_1 q_0 + p_2 q_3 + p_3 q_2 \\ qp_3 = -p_0 q_0 - p_1 q_1 - p_2 q_2 + p_3 q_3 $$

When multiplying \(q\) and \(p\) together keep in mind that quaternions left multiply. This means when performing \(qp\), the rotation of \(p\) is applied first, then the rotation of \(q\)

Implementing the above formula for quaternion multiplication in code is trivial.

Quaternion Mul(Quaternion q, Quaternion p) {
    Quaternion result;

    result.x =   p.x * q.w  + p.y * q.z - p.z * q.y + p.w * q.x;
    result.y = -(p.x * q.z) + p.y * q.w + p.z * q.x + p.w * q.y;
    result.z =   p.x * q.y  - p.y * q.x + p.z * q.w + p.w * q.z;
    result.w = -(p.x * q.x) - p.y * q.y - p.z * q.z + p.w * q.w;  

    return result;
}

Dot and Cross Products

I'm going to re-order the equation from above so the vector part of the quaternion has it's negative term last, and the scalar part has its positive term first. I'm also going to switch from subscripting \(0, 1, 2, 3\) to \(x, y, z, w\) respectivley.

$$ qp_x = \textcolor{sienna}{p_x q_w} + \textcolor{teal}{p_w q_x} + \textcolor{purple}{p_y q_z - p_z q_y} \\ qp_y = \textcolor{sienna}{p_y q_w} + \textcolor{teal}{p_w q_y} + \textcolor{purple}{p_z q_x - p_x q_z} \\ qp_z = \textcolor{sienna}{p_z q_w} + \textcolor{teal}{p_w q_z} + \textcolor{purple}{p_x q_y - p_y q_x} \\ qp_w = p_w q_w \textcolor{orange}{- p_x q_x - p_y q_y - p_z q_z} $$

All i did here was re-arrange some numbers (and change the subscript notation), the value of each component remains unchanged by this re-arrangement. But, there are some interesting properties to notice in this re-arrangement.

First, look at the purple part of the equation, that's the vector cross product! Next, look at the brown part, it's the vector part of \(p\) scaled by \(q_w\). Similarly the teal (green-ish) part is the vector part of \(q\) scaled by \(p_w\).

Finally, look at the orange part of the equation. We can re-write it as: \(\textcolor{orange}{-1(p_x q_x + p_y q_y + p_z q_z)}\). What you might notice here is that's the vector dot product!

With these observations, the formula for quaternion multiplication can be re-written in terms of the dot and cross products.

$$ qp = (p_w\vec{q_v} + q_w\vec{p_v} + \vec{p_v} \times \vec{q_v}), p_w q_w - \vec{p_v} \cdot \vec{q_v} $$

In the above formula, \(\vec{q_v}\) and \(\vec{p_v}\) refer to the vector parts of \(q\) and \(p\) respectivley, \(q_w\) and \(p_w\) refer to the real parts. This notation might be a bit easier to remember. There is no speed gain for this notation, implementing it in code is trivial

Quaternion Mul_AlternateImplementation(Quaternion q, Quaternion p) {
    Vector3 q_v = Vector3(q.x, q.y, q.z);
    Vector3 p_v = Vector3(p.x, p.y, p.z);

    float q_r = q.w;
    float p_r = p.w;

    float scalar = q_r * p_r - Dot(q_v, p_v)
    // Assume + is vector addition here
    // vector = p_v * q_r + q_v * p_r + Cross(p_v, q_v);
    Vector3 vector = Scale(p_v, q_r) + Scale(q_v, p_r) + Cross(p_v, q_v);

    Quaternion result;
    result.w = scalar;
    result.x = vector.x;
    result.y = vector.y;
    result.z = vector.z;
    return result;
}