Vertex Skinning and Morph Targets Order in glTF

In glTF shaders, morph target calculations must always be applied before vertex skinning. Morph targets (also known as blend shapes) deform the mesh within its local object space, while vertex skinning transforms the local geometry into the skeleton's posed coordinate space. Applying these calculations in any other order leads to distorted geometry, misaligned animations, and incorrect surface lighting.

The Standard Pipeline Order

When writing or evaluating a glTF vertex shader, vertex transformations must follow this explicit sequence:

  1. Base Attributes: Read the initial vertex position, normal, and tangent attributes.
  2. Morph Target Blending: Apply the weighted morph target deltas to the base attributes in local mesh space.
  3. Vertex Skinning: Apply the skinning matrix palette to the morphed attributes to pose the mesh.
  4. Scene Transformations: Apply model, view, and projection matrices to transition from local/world space to clip space.

Step 1: Morph Target Calculations

glTF defines morph targets as deltas relative to the base mesh in local coordinate space (bind pose). Each active morph target provides displacement vectors for positions, normals, and tangents, which are multiplied by their respective target weights and summed with the base attribute.

The mathematical formulation for position morphing is:

\[\mathbf{P}_{\text{morphed}} = \mathbf{P}_{\text{base}} + \sum_{i} (w_i \cdot \Delta \mathbf{P}_i)\]

Where:

  • \(\mathbf{P}_{\text{base}}\) is the original vertex position attribute (POSITION).
  • \(w_i\) is the scalar weight of morph target \(i\).
  • \(\Delta \mathbf{P}_i\) is the position delta vector for target \(i\).

The identical additive calculation applies to normals and tangents using their respective deltas before any skeletal deformation occurs.

Step 2: Vertex Skinning Calculations

Vertex skinning takes the morphed local vertex and transforms it using the joint matrices that represent the skeleton's current pose. Skinning assumes the incoming vertex data is in the mesh's bind space.

The skinned position is calculated as:

\[\mathbf{P}_{\text{skinned}} = \sum_{j=0}^{3} \left( \text{weight}_j \cdot \mathbf{M}_j \right) \cdot \mathbf{P}_{\text{morphed}}\]

Where:

  • \(\mathbf{M}_j\) is the joint matrix for joint index \(j\), which converts bind space coordinates into posed skeletal coordinates.
  • \(\text{weight}_j\) is the scalar skin weight for joint \(j\).
  • \(\mathbf{P}_{\text{morphed}}\) is the vertex position derived from the morph step.

Why the Order Cannot Be Reversed

If skinning is evaluated before morphing, the vertex is moved out of its local bind pose into joint space before the morph displacements are applied. Because morph target deltas are authored relative to local model space, applying them to an already skinned vertex causes displacements along the wrong directional axes. This breaks facial expressions, muscle flexing, and corrective blend shapes whenever the underlying skeleton rotates or translates away from the default rest pose.

Handling Normals and Tangents

Surface vectors must follow the exact same evaluation sequence:

  1. Add the weighted morph deltas to the base normal and tangent vectors.
  2. Transform the resulting vectors by the skinning normal matrices.
  3. Normalize the final vectors after skinning to prevent distortion caused by matrix scaling or linear blend interpolation.