GLSL Quaternion Multiplication Using vec4

This article explains how to implement quaternion multiplication in the OpenGL Shading Language (GLSL) using native four-component vectors (vec4). It covers the mathematical formulation of the Hamilton product, breaks down the vector and scalar components, and provides an optimized GLSL function utilizing built-in vector operations like dot and cross.

Quaternion Representation in GLSL

In 3D graphics, a quaternion \(q\) is typically represented as a combination of a 3D vector part \(\mathbf{v} = (x, y, z)\) and a scalar real part \(w\):

\[q = (\mathbf{v}, w) = (x, y, z, w)\]

In GLSL, a vec4 natively maps to this representation, where the .xyz swizzle corresponds to the vector part \(\mathbf{v}\), and the .w component corresponds to the scalar \(w\).

Mathematical Formulation

The product of two quaternions \(q_1 = (\mathbf{v}_1, w_1)\) and \(q_2 = (\mathbf{v}_2, w_2)\) is defined by the Hamilton product:

\[\mathbf{v}_{\text{result}} = w_1 \mathbf{v}_2 + w_2 \mathbf{v}_1 + (\mathbf{v}_1 \times \mathbf{v}_2)\] \[w_{\text{result}} = w_1 w_2 - (\mathbf{v}_1 \cdot \mathbf{v}_2)\]

This formulation is ideal for GLSL because it maps directly to built-in vector instructions: scalar multiplication, addition, the cross product (cross), and the dot product (dot).

GLSL Implementation

Below is the standard, vector-optimized GLSL function to multiply two quaternions stored in vec4:

vec4 quatMult(vec4 q1, vec4 q2) {
    return vec4(
        q1.w * q2.xyz + q2.w * q1.xyz + cross(q1.xyz, q2.xyz),
        q1.w * q2.w - dot(q1.xyz, q2.xyz)
    );
}

How the Code Works

  1. Vector Component Calculation (.xyz):
    • q1.w * q2.xyz: Scales the vector part of \(q_2\) by the scalar part of \(q_1\).
    • q2.w * q1.xyz: Scales the vector part of \(q_1\) by the scalar part of \(q_2\).
    • cross(q1.xyz, q2.xyz): Computes the 3D cross product of both vector parts, handling the non-commutative rotational interaction.
    • Adding these three together yields the directional vector component of the resulting quaternion.
  2. Scalar Component Calculation (.w):
    • q1.w * q2.w: Multiplies the two scalar components.
    • dot(q1.xyz, q2.xyz): Computes the Euclidean dot product of the vector parts.
    • Subtracting the dot product from the scalar product yields the final scalar real component.

Component-Wise Alternative

If you need to avoid the cross function or want to inspect individual algebra steps, the fully expanded component-wise formulation can be used:

vec4 quatMultExpanded(vec4 q1, vec4 q2) {
    return vec4(
        q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,
        q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x,
        q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w,
        q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
    );
}

The vector-based version using cross() and dot() is generally preferred for readability and leverages dedicated hardware vector units efficiently on modern GPUs.