Rotating vectors using quaternions
To rotate a vector by a quaternion, turn the vector into a pure quaternion. This can be done by creating a quaternion with no scalar part and treating the (normalized) vector as it's vector part. For example, converting a vector \(\vec{v}\) to a quaternion \(v'\) would look like this:
$$ \vec{v} = (v_x, v_y, v_z) \\ v' = (\hat{v_x}, \hat{v_y}, \hat{v_z}), 0 $$
Next, left multiply this by the desired quaternion, and right multiply it by the inverse (conjugate if normalized) of the quaternion. Rotating \(v'\) by some quaternion \(q\) would look like this:
$$ r = q \cdot v' \cdot q^{-1} $$
The new, rotated vector is stored in the vector part of the result. The real part of the resulting quaternion should be 0. This type of rotation is easy to implement, but it's not very optimal. For most games doing two quaternion multiplications is fairly quick, but we can write something omre optimal and easier to understand.
Since this isn't the final multiplication formula, I won't go into detail on why the inverse post multiply is needed. This video does a great job of explaining how the formula works
Optimizing
Consider the formula: \(r = q \cdot v' \cdot q^{-1}\). We already know that hte real part of the resulting quaternion is going to be zero, but how is the vector part calculated?
- Start with the formula: \(r = q \cdot v' \cdot q^{*}\)
- Write it in vector-scalar notation: \((\vec{q_v}, q_s)(\vec{v}, 0)(-\vec{q_v}, q_s)\)
- \(\vec{q_v}\) is the vector part of \(q\), \(\vec{q_v} = (q_x, q_y, q_z)\)
- \(q_s\) is the scalar part of \(q\), \(q_s = q_w\)
- \(v\) is the vector being rotated
- Do the left side multiplication: \((q_s \vec{v} + 0 \vec{q_v} + \vec{q_v} \times \vec{v}, 0 * p_s - \vec{q_v} \cdot \vec{v})(-\vec{q_v}, q_s)\)
- Remove the terms that evaluate to zero: \((q_s \vec{v} + \vec{q_v} \times \vec{v}, -\vec{q_v} \cdot \vec{v})(-\vec{q_v}, q_s)\)
- Do the right side multiplication: \(((-\vec{q_v} \cdot \vec{v})(-\vec{q_v}) + q_s(q_s \vec{v} + \vec{q_v} \times \vec{v}) + (s \vec{v} + \vec{q_v} \times \vec{v}) \times -{q_v}), 0\)
- To save space simplified the scalar part to 0, it's going to be 0
- Simplify: \( ((\vec{q_v} \cdot \vec{v})\vec{q_v} + q_{s}^{2}\vec{v} + q_s(\vec{q_v} \times \vec{v}) + q_s\vec{v} \times (-\vec{q_v}) + (\vec{q_v} \times \vec{v}) \times (-\vec{q_v}), 0) \)
The real part is not really interesting, it evaluates to 0. But the vector part, that can be simplified!
$$ \begin{aligned} \vec{v'} &= (\vec{q_v} \cdot \vec{v})\vec{q_v} + q_{s}^{2}\vec{v} + q_s(\vec{q_v} \times \vec{v}) + q_s\vec{v} \times (-\vec{q_v}) + (\vec{q_v} \times \vec{v}) \times (-\vec{q_v}) \\ &= (\vec{q_v} \cdot \vec{v})\vec{q_v} + q_s^{2}\vec{v} + q_s(\vec{q_v} \times \vec{v}) + q_s(\vec{q_v} \times \vec{v}) + \vec{q_v} \times (\vec{q_v} \times \vec{v}) \\ &= (\vec{q_v} \cdot \vec{v})\vec{q_v} + q_s^{2}\vec{v} + 2q_s(\vec{q_v} \times \vec{v}) + (\vec{q_v} \cdot \vec{v})\vec{q_v} - (\vec{q_v} \cdot \vec{q_v})\vec{v} \\ &= 2(\vec{q_v} \cdot \vec{v})\vec{q_v} + (q_s^{2} - \vec{q_v} \cdot \vec{q_v})\vec{v} + 2q_s(\vec{q_v} \times \vec{v}) \end{aligned} $$
This simplifie equation \(2(\vec{q_v} \cdot \vec{v})\vec{q_v} + (q_s^{2} - \vec{q_v} \cdot \vec{q_v})\vec{v} + 2q_s(\vec{q_v} \times \vec{v})\) is both easier to implement and uses less operations, making it more efficient.
// Could also be called Rotate(vector, quaternion) or something similar Quaternion Mul(Vector3 v, Quaternion q) { Vector3 u = Vector3(q.x, q.y, q.z); float s = q.w; return 2.0 * Dot(u, v) * u + (s * s - Dot(u, u)) * v + 2.0 * s * cross(u, v); /* Implemented with add / scale functions Add( Add( Scale(u, 2 * Dot(u, v)), Scale(v, s * s - Dot(u, u)) ), Scale(Cross(u, v), 2 * s) )*/ }