glTF Cubic Spline Interpolation and Tangents

In glTF 2.0, the CUBICSPLINE interpolation mode uses cubic Hermite splines to produce smooth transitions between animation keyframes. Unlike standard linear interpolation, cubic spline interpolation relies on explicit incoming and outgoing tangent vectors provided alongside each keyframe value in the animation data. This article explains how glTF structures this keyframe data, how incoming and outgoing tangents are computed from DCC source handles or curves, and how the runtime animation engine evaluates these tangents across normalized time intervals.

The glTF Keyframe Data Layout

For cubic spline tracks, the glTF binary buffer stores three distinct components for every keyframe timestamp \(k\):

  1. In-Tangent (\(a_k\)): The incoming tangent defining the slope of the curve as it arrives at the current keyframe.
  2. Value (\(v_k\)): The actual property value (e.g., translation, scale, or rotation) at the keyframe time \(t_k\).
  3. Out-Tangent (\(b_k\)): The outgoing tangent defining the slope of the curve as it leaves the current keyframe.

Because of this arrangement, the animation sampler's output accessor contains three times as many elements as the input time accessor. The first keyframe's in-tangent and the final keyframe's out-tangent are typically unused during standard playback, though they remain present in the data stream.

Converting Tangents from Authoring Tools

glTF does not calculate tangents at runtime; it expects precomputed tangents stored in the asset. Authoring tools (like Blender, Maya, or 3ds Max) often construct animation curves using cubic Bézier splines rather than Hermite splines. Exporters convert Bézier control points into glTF-compatible Hermite tangents using the relationship between Bézier handles and derivative vectors:

For a Bézier segment with start point \(P_0\), end point \(P_3\), and control handles \(P_1\) and \(P_2\) over a time interval \(\Delta t = t_{k+1} - t_k\):

  • Out-Tangent (\(b_k\)): Derived from the right handle of the starting point: \[b_k = \frac{3 \cdot (P_1 - P_0)}{\Delta t}\]
  • In-Tangent (\(a_{k+1}\)): Derived from the left handle of the ending point: \[a_{k+1} = \frac{3 \cdot (P_3 - P_2)}{\Delta t}\]

When tangents are generated mathematically without existing Bézier handles (such as in automated Catmull-Rom splines), the tangents can be approximated using central differences across neighboring keyframes:

\[m_k = \frac{v_{k+1} - v_{k-1}}{t_{k+1} - t_{k-1}}\]

Here, \(a_k = b_k = m_k\), enforcing \(C^1\) continuity across the keyframe.

Evaluating the Cubic Hermite Spline at Runtime

To calculate the interpolated value \(p(t)\) at a given time \(t\) between keyframes \(k\) and \(k+1\), runtime engines apply the cubic Hermite basis functions:

  1. Calculate Interval Duration and Normalized Factor: \[\Delta t = t_{k+1} - t_k\] \[s = \frac{t - t_k}{\Delta t} \quad \text{where } s \in [0, 1]\]

  2. Calculate the Hermite Basis Functions: \[h_{00}(s) = 2s^3 - 3s^2 + 1\] \[h_{10}(s) = s^3 - 2s^2 + s\] \[h_{01}(s) = -2s^3 + 3s^2\] \[h_{11}(s) = s^3 - s^2\]

  3. Compute the Interpolated Point: glTF stores tangents in units of [value] / [second]. Because the basis functions operate on normalized time \(s \in [0, 1]\), the engine scales the tangents by the segment's total duration \(\Delta t\): \[p(s) = h_{00}(s)v_k + \Delta t \cdot h_{10}(s)b_k + h_{01}(s)v_{k+1} + \Delta t \cdot h_{11}(s)a_{k+1}\]

Special Considerations for Quaternions

When interpolating rotation channels represented by quaternions, the cubic Hermite formulation is applied component-wise across the \(x, y, z,\) and \(w\) elements using the standard vector equation. Because cubic blending does not inherently preserve unit length, the engine must normalize the resulting quaternion after evaluation to ensure a valid 3D orientation:

\[q_{\text{final}} = \frac{p(s)}{\|p(s)\|}\]