Linear Blend Skinning vs Dual Quaternion Skinning in GLSL

Skeletal animation in real-time graphics relies primarily on two vertex skinning algorithms: Linear Blend Skinning (LBS) and Dual Quaternion Skinning (DQS). While LBS blends affine transformation matrices directly, DQS blends dual quaternions to represent combined rotation and translation. This article breaks down the mathematical and practical differences between implementing LBS and DQS calculations within OpenGL Shading Language (GLSL) vertex shaders, examining their performance trade-offs, code structures, and visual artifacts.

Mathematical Representation and Data Structures

In GLSL, the two techniques rely on completely different input structures passed to the vertex shader:

  • Linear Blend Skinning: Bone transformations are passed as an array of 4x4 matrices (mat4 boneMatrices[]) or encoded in a texture buffer. Each matrix represents the full rotation, scale, and translation for a joint.
  • Dual Quaternion Skinning: Bone transformations are passed as pairs of 4D vectors (vec4) representing the real part (rotation quaternion) and dual part (translation quaternion). A bone is typically represented as a struct containing two vec4 values or indexed from an array: vec4 boneDQs[MAX_BONES * 2].

GLSL Calculation: Linear Blend Skinning

LBS calculates the final vertex position via a weighted sum of bone matrices. The vertex attributes include bone indices (ivec4) and bone weights (vec4).

// Linear Blend Skinning in GLSL
mat4 skinMatrix = 
    boneMatrices[boneIndices.x] * boneWeights.x +
    boneMatrices[boneIndices.y] * boneWeights.y +
    boneMatrices[boneIndices.z] * boneWeights.z +
    boneMatrices[boneIndices.w] * boneWeights.w;

vec4 skinnedPosition = skinMatrix * vec4(inPosition, 1.0);
vec3 skinnedNormal   = mat3(skinMatrix) * inNormal;

Advantages and Disadvantages of LBS

  • ALU Cost: Very low. Modern GPUs have dedicated hardware for matrix-vector multiplication.
  • Artifacts: LBS suffers from the "candy-wrapper" artifact. Because matrices are linearly interpolated, rotational components do not interpolate along the shortest arc on a sphere. Twisting joints (like wrists or forearms) cause the mesh to collapse and lose volume.

GLSL Calculation: Dual Quaternion Skinning

DQS uses dual quaternion linear blending (DLB). It linearly blends the real and dual parts, normalizes the result to maintain a rigid transformation, and then transforms the position and normal vectors using quaternion algebra.

To prevent antipodal cancellation (where quaternions representing the same rotation cancel each other out), the shader must check the dot product of each quaternion with the base quaternion and flip signs if necessary.

// Dual Quaternion Skinning in GLSL
vec4 real = vec4(0.0);
vec4 dual = vec4(0.0);

vec4 dq0_real = boneDQs[boneIndices.x * 2];
vec4 dq0_dual = boneDQs[boneIndices.x * 2 + 1];

for (int i = 0; i < 4; ++i) {
    int idx = boneIndices[i] * 2;
    vec4 r = boneDQs[idx];
    vec4 d = boneDQs[idx + 1];

    // Ensure shortest path interpolation
    float weight = boneWeights[i];
    if (dot(r, dq0_real) < 0.0) {
        weight = -weight;
    }

    real += r * weight;
    dual += d * weight;
}

// Normalize the blended dual quaternion
float len = length(real);
real /= len;
dual /= len;

// Transform position: p' = p + 2 * cross(real.xyz, cross(real.xyz, p) + real.w * p) + 2 * (real.w * dual.xyz - dual.w * real.xyz + cross(real.xyz, dual.xyz))
vec3 t = 2.0 * (real.w * dual.xyz - dual.w * real.xyz + cross(real.xyz, dual.xyz));
vec3 skinnedPosition = inPosition + 2.0 * cross(real.xyz, cross(real.xyz, inPosition) + real.w * inPosition) + t;

// Transform normal (rotates by the real quaternion component only)
vec3 skinnedNormal = inNormal + 2.0 * cross(real.xyz, cross(real.xyz, inNormal) + real.w * inNormal);

Advantages and Disadvantages of DQS

  • ALU Cost: Higher than LBS. DQS requires dot products, conditional sign flipping, normalization (inversesqrt), and multiple vector cross products to extract the transformed point.
  • Memory Bandwidth: Lower than LBS. Storing two vec4 types takes 8 floats per bone, compared to 12 to 16 floats for a mat4 or mat4x3.
  • Artifacts: DQS completely eliminates the candy-wrapper artifact and preserves volume during extreme joint twists. However, it can produce unnatural "joint-bulging" around sharp bends (like elbows or knees) and does not natively support non-uniform scaling without complex adjustments.

Summary of Differences

Feature Linear Blend Skinning (LBS) Dual Quaternion Skinning (DQS)
Data Footprint per Bone 16 floats (mat4) or 12 floats (mat4x3) 8 floats (2x vec4)
Shader Complexity Simple matrix-vector arithmetic Quaternion blending, normalization, cross products
GPU ALU Overhead Low Moderate
Twisting Behavior Mesh collapses (candy-wrapper effect) Mesh volume is strictly preserved
Bending Behavior Tends to pinch Tends to bulge
Scaling Support Handles uniform and non-uniform scaling natively Requires matrix fallback or specialized scale handling