四元数插值 Interpolating Quaternions

整理 Nlerp、Slerp、四元数指数形式和双覆盖问题。

四元数插值 Interpolating Quaternions

对四元数进行 插值,本质上是在多个 旋转姿态平滑过渡权重混合,四元数插值有两种常见做法

方法全称主要特点典型用途
SlerpSpherical Linear Interpolation(球面线性插值)1. 在四维单位球面上走最短圆弧,角速度恒定
2. 产生的旋转序列“扭矩最小”(torque-minimal),不会出现忽快忽慢的转动。
3. 计算量稍大(需要三角函数)。
4. 不具备交换律:Slerp(q₀→q₁, t) ≠ Slerp(q₁→q₀, t)。
需要高物理正确性和匀速旋转的场景,如刚体动力学、摄像机跟随。
NlerpNormalized Linear Interpolation(归一化线性插值)1. 先对四元数做线性插值(Lerp),再把结果单位化;计算只用乘加、一次归一化,速度快、代码简单
2. 插值路径不是严格的球面大圆弧,角速度 不恒定(靠近端点时更慢)。
3. 具备交换律:Nlerp(q₀→q₁, t) = Nlerp(q₁→q₀, 1−t)。
4. 仍然是扭矩最小插值,但精度略逊于 Slerp。
角色动画、UI 过渡等对精度要求不高,追求性能的场合。

这里所说的 “扭矩最小”(torque-minimal 或 minimum-torque) 是从 “刚体动力学” 角度来评价一条旋转插值曲线的物理性质:在理想均匀刚体、忽略外力的前提下,这条插值轨迹所需的外加扭矩 τ\tau(或其二范数的时间积分)是所有可行路径里最小的

Nlerp 归一化线性插值

对任何 标量向量lerp 线性插值 都是

a+t(ba),t[0,1]\mathbf{a} + t(\mathbf{b} - \mathbf{a}), \quad t \in[0,1]

四元数也不例外,只是 a, b\mathbf{a}, \ \mathbf{b} 必须保持单位长度,而进行 lerp 之后,长度一般不再等于 11,所以我们可以再最后进行一次 归一化 Normalize,这就是 Nlerp 归一化线性插值

可能不是最短路径

因为 单位四元数 和它的 相反数 q-q三维空间 中表示 同一姿态,但却位于 四维超球面对立半球,当我们直接执行 lerp 时,如果起点和终点分属两侧,会沿半个大圆以外的 “长弧” 进行插值,角度将多转 180180^\circ,为了避免这种情况,我们可以用 点乘 判断是否同属一个半球,如果

ab<0\mathbf{a} \cdot \mathbf{b} < 0

那么说明它们分属 四维超球面 的两侧,我们需要将终点进行 取反

bb\mathbf{b} \leftarrow -\mathbf{b}

典型代码

Quaternion Nlerp(Quaternion start, Quaternion end, float t)
{
    if(Dot(start,end) < 0.0f)         // ① 取最短弧
        end = Negate(end);
    Quaternion res = start + (end - start) * t;   // ② 线性插值
    return Normalize(res);            // ③ 单位化
}

Nlerp 非常快速,只有加减乘+一次 normalize

带权混合 Mixing

动画系统常用另一种 等价形式 来表示 lerpNlerp,即用 权重混合 来把两个(或多个)Pose/骨骼姿态叠加,对于四元数,也就是按 权重 对姿态求平均

mix(a,b; w)=Normalize((1w)a+wb)\mathrm{mix}(\mathbf{a},\mathbf{b}; \ w) = \mathrm{Normalize}((1-w)\mathbf{a} + w\mathbf{b})
  • 去掉 Normalize\mathrm{Normalize} 就是普通的 lerp 带权混合形式

当然,我们仍然需要用 点乘 判断是否同属一个半球用以取反来保证插值按 最短弧 进行

Quaternion Nlerp(Quaternion start, Quaternion end, float t)
{
    if(Dot(start,end) < 0.0f)         // ① 取最短弧
        end = Negate(end);
    Quaternion res =  a*(1-t) + b*t;   // ② 线性插值
    return Normalize(res);            // ③ 单位化
}

这在 动画层叠 / additive blending 里特别常用:你可以给若干动作片段各分配 0.3、0.5、0.2…… 的权重,把它们 “加权平均” 后再归一化,得到最终骨骼姿态

Slerp 球面线性插值

我们先来看看 三维向量Slerp,其完全基于 大圆(测地线) 上做等速运动 的 几何约束

首先,若有 夹角θ\thetaq0, q1\vec{q}_0, \ \vec{q}_1 均是 三维单位球体 上的点,那么它们生成的 二维子空间 P=span{q0, q1}P = \mathrm{span}\{\vec{q}_0, \ \vec{q}_1 \}原点 相交于一个 单位圆,整条 最短弧 都落在这个平面里,也就是说我们将问题降维到一个 二维单位圆

我们首先需要构造 正交基,因为 q0, q1\vec{q}_0, \ \vec{q}_1 都是 三维单位球体 上的点,所以它们都是 单位向量,那么

e1=q0\mathbf{e}_1 = \vec{q}_0

而要构造 e2\mathbf{e}_2,我们先使用 Gram-Schmidt 正交化 q1(q1q0)q0\vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0,然后再对其执行归一化,也就是除以它的长度 q1(q1q0)q0\| \vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0 \|

e2=q1(q1q0)q0q1(q1q0)q0\mathbf{e}_2 = \frac{\vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0}{\| \vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0 \|}

我们知道 点乘 可以写为

q1q0=q1q0cosθ=cosθ\vec{q}_1 \cdot \vec{q}_0 = \|\vec{q}_1\| \cdot \|\vec{q}_0\| \cos{\theta} = \cos{\theta}
  • q0, q1\vec{q}_0, \ \vec{q}_1 都是 单位向量

所以 分子 可以写为

q1(q1q0)q0=q1cosθq0\vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0 = \vec{q}_1 - \cos{\theta}\vec{q}_0

分母 也可以进行修改

q1(q1q0)q02=q12+cos2θq022cosθ(q1q0)=1+cos2θ2cos2θq12=q02=1, q1q0=cosθ=1cos2θ=sin2θ q1(q1q0)q0=sinθ\begin{align*} \| \vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0 \|^2 &= \|\vec{q}_1 \|^2 + \cos^2{\theta}\|\vec{q}_0\|^2 - 2\cos{\theta}(\vec{q}_1 \cdot \vec{q}_0) \\ &= 1 + \cos^2{\theta} - 2\cos^2{\theta} \quad \because \|\vec{q}_1\|^2 = \|\vec{q}_0\|^2 = 1, \ \vec{q}_1 \cdot \vec{q}_0 = \cos{\theta} \\ &= 1 - \cos^2{\theta} \\ &= \sin^2{\theta} \\ \therefore \ \| \vec{q}_1 - (\vec{q}_1 \cdot \vec{q}_0)\vec{q}_0 \| &= \sin{\theta} \end{align*}

所以我们得到最终的 正交基

{e1=q0e2=q1cosθ q0sinθ    P=span{e1, e2}\begin{cases} \mathbf{e}_1 = \vec{q}_0 \\[6pt] \mathbf{e}_2 = \dfrac{\vec{q}_1 - \cos{\theta} \ \vec{q}_0}{\sin{\theta}} \end{cases} \implies P = \mathrm{span}\{\mathbf{e}_1,\ \mathbf{e}_2 \}

e1, e2\mathbf{e}_1,\ \mathbf{e}_2 在平面 PP正交归一,因此任何 插值点 都可写成

q(t)=x(t)e1+y(t)e2\vec{q}(t) = x(t)\mathbf{e}_1 + y(t)\mathbf{e}_2

p0p1\vec{p}_0 \to \vec{p}_1插值点 必须满足以下 约束条件

  1. 端点条件

    q(0)=q0=e1    x(0)=1, y(0)=0q(1)=q1=cosθe1+sinθe2    x(1)=cosθ, y(1)=sinθ\begin{align*} \vec{q}(0) &= \vec{q}_0 = \mathbf{e}_1 &\implies x(0) = 1, \ y(0) = 0 \quad \quad \quad \\ \vec{q}(1) &= \vec{q}_1 = \cos{\theta}\mathbf{e}_1 + \sin{\theta}\mathbf{e}_2 &\implies x(1) = \cos{\theta}, \ y(1) = \sin{\theta} \end{align*}
    • q1=cosθe1+sinθe2\vec{q}_1 = \cos{\theta}\mathbf{e}_1 + \sin{\theta}\mathbf{e}_2 是因为 q1e1=q1q0=cosθq1e2=q1q1cosθ q0sinθ=q1q1(q1q0)(q1q0)sinθ=1cos2θsinθ=sin2θsinθ=sinθ\begin{align*} \vec{q}_1 \cdot \mathbf{e}_1 &= \vec{q}_1 \cdot \vec{q}_0 = \cos{\theta} \\ \vec{q}_1 \cdot \mathbf{e}_2 &= \vec{q}_1 \cdot \frac{\vec{q}_1 - \cos{\theta} \ \vec{q}_0}{\sin{\theta}} \\ &= \frac{\vec{q}_1\cdot\vec{q}_1 - (\vec{q}_1\cdot \vec{q}_0)(\vec{q}_1\cdot \vec{q}_0)}{\sin{\theta}} \\ &= \frac{1 - \cos^2{\theta}}{\sin{\theta}} \\ &= \frac{\sin^2{\theta}}{\sin{\theta}} \\ &= \sin{\theta} \end{align*}
  2. 保持单位长度
    插值向量 的长度需要保持为 11

    q(t)2=x(t)2+y(t)2=1\|\vec{q}(t)\|^2 = x(t)^2 + y(t)^2 = 1
  3. 角度线性(匀速)
    我们知道 单位向量 uuvv点乘夹角 关系是

    uv=cos[(u, v)]\vec{u} \cdot \vec{v} = \cos{[\angle(\vec{u}, \ \vec{v})]}

    我们还知道 q0\vec{q}_0q1\vec{q}_1完整夹角θ\theta,而 插值向量 q(t)\vec{q}(t)初始向量 q0\vec{q}_0夹角(q0, q(t))\angle(\vec{q}_0,\ \vec{q}(t)),我们希望它随着插值的进行,一路 线性 的增长到 θ\theta,也就是

    (q0, q(t))=tθ,t[0,1]\angle(\vec{q}_0,\ \vec{q}(t)) = t\theta,\quad t\in [0,1]

    有了这个结论,再结合 单位向量点乘夹角 关系,可得

    q0q(t)=costθ\vec{q}_0 \cdot \vec{q}(t) = \cos{t\theta}

    我们之前还建立了 正交基 e1,e2\mathbf{e}_1,\mathbf{e}_2,并用它表示了 插值向量 q(t)\vec{q}(t),我们知道

    e1=q0q(t)=x(t)e1+y(t)e2\begin{align*} \mathbf{e}_1 &= \vec{q}_0 \\ \vec{q}(t) &= x(t)\mathbf{e}_1 + y(t)\mathbf{e}_2 \end{align*}

    所以有

    q0q(t)=e1[x(t)e1+y(t)e2]=x(t)e1e1+y(t)e2e1=x(t)1+y(t)0=x(t)\begin{align*} \vec{q}_0 \cdot \vec{q}(t) &= \mathbf{e}_1 \cdot [x(t)\mathbf{e}_1 + y(t)\mathbf{e}_2] \\ &= x(t)\mathbf{e}_1\cdot\mathbf{e}_1 + y(t)\mathbf{e}_2\cdot\mathbf{e}_1 \\ &= x(t)\cdot 1 + y(t)\cdot 0 \\ &= x(t) \end{align*}

    回代,最终得到

    x(t)=costθx(t) = \cos{t\theta}

角度线性 约束,我们已经得到了 x(t)x(t),接下来我们只需要结合 保持单位长度 约束就可以得到 y(t)y(t)

x(t)2+y(t)2=1    y(t)=±1cos2(tθ)=±sin(tθ)x(t)^2 + y(t)^2 = 1 \implies y(t) = \pm \sqrt{1 - \cos^2(t\theta)} = \pm\sin(t\theta)

沿 最短弧y(t)0y(t) \geq 0 (与 e2\mathbf{e}_2 同向),故取正号,得到

y(t)=sintθy(t) = \sin{t\theta}

所以我们得出

q(t)=costθ e1+sintθ e2\vec{q}(t) = \cos{t\theta} \ \mathbf{e}_1 + \sin{t\theta} \ \mathbf{e}_2

然后我们将 e1, e2\mathbf{e}_1, \ \mathbf{e}_2 展开写回为 q0, q1\vec{q}_0, \ \vec{q}_1

{e1=q0e2=q1cosθ q0sinθq(t)=costθ e1+sintθ e2=costθ q0+sintθ q1cosθ q0sinθ=costθ q0+sintθsinθq1cosθsintθsinθq0=[costθcosθsintθsinθ]q0+sintθsinθq1=sinθcostθcosθsintθsinθq0+sintθsinθq1\begin{align*} & \begin{cases} \mathbf{e}_1 = \vec{q}_0 \\[6pt] \mathbf{e}_2 = \dfrac{\vec{q}_1 - \cos{\theta} \ \vec{q}_0}{\sin{\theta}} \end{cases} \\ \\ \vec{q}(t) &= \cos{t\theta} \ \mathbf{e}_1 + \sin{t\theta} \ \mathbf{e}_2 \\ &= \cos{t\theta} \ \vec{q}_0 + \sin{t\theta} \ \frac{\vec{q}_1 - \cos{\theta} \ \vec{q}_0}{\sin{\theta}} \\ &= \cos{t\theta} \ \vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1 - \frac{\cos{\theta}\sin{t\theta}}{\sin{\theta}}\vec{q}_0 \\ &= \left[\cos{t\theta} - \frac{\cos{\theta}\sin{t\theta}}{\sin{\theta}} \right]\vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1\\ &= \frac{\sin{\theta}\cos{t\theta} - \cos{\theta}\sin{t\theta}}{\sin{\theta}}\vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1 \end{align*}

我们可以利用 三角恒等式

sin(AB)=sinAcosBcosAsinB\sin(A - B) = \sin{A}\cos{B} - \cos{A}\sin{B}

所以我们有

sin(θtθ)=sinθcostθcosθsintθ\sin{(\theta - t\theta)} = \sin{\theta}\cos{t\theta} - \cos{\theta}\sin{t\theta}

回代,得到

q(t)=sinθcostθcosθsintθsinθq0+sintθsinθq1=sin(θtθ)sinθq0+sintθsinθq1=sin[(1t)θ]sinθq0+sintθsinθq1\begin{align*} \vec{q}(t) &= \frac{\sin{\theta}\cos{t\theta} - \cos{\theta}\sin{t\theta}}{\sin{\theta}}\vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1 \\ &= \frac{\sin{(\theta - t\theta)}}{\sin{\theta}}\vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1 \\ &= \boxed{ \frac{\sin{[(1 - t)\theta]} }{\sin{\theta}}\vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1 } \end{align*}

这正是 正弦权重 版的 Slerp 公式,整条 插值曲线 因此可以视为在以 q0, q1\vec{q}_0, \ \vec{q}_1 “按弦长正弦比” 进行的匀速运动,当 θ0\theta\to0 时,sinθθ\sin{\theta} \approx \theta,此时公式滑退化为普通 lerp 线性插值 q0+(q1q0)t\vec{q}_0 + (\vec{q}_1 - \vec{q}_0)t

四元数 Slerp

要推导 四元数 q0q_0q1q_1Slerp,核心思想是在 四维单位球 S3S^3 上寻找经过 q0q1q_0 \to q_1 的最短大圆弧,并要求匀速(角速度恒定)地走这条弧线

单位四元数 对应 三维旋转,其集合构成 三维流形 S3S^3 (半径 11四维超球面),两个 四元数q0, q1S3q_0,\ q_1 \in S^3 间的测地线就是穿过两点的 大圆(Great Circle),设

Ω=arccos(q0q1),0<Ωπ\Omega = \mathrm{arccos}(q_0\cdot q_1), \quad 0 < \Omega \leq \pi

是两点在 S3S^3 上的 夹角 (用四元数点积即可求得),在大圆坐标系里,想要 匀速 行走意味着 插值点 必须满足

θ(t)=(1t)0+tΩ=tΩ\theta(t) = (1 - t) 0 + t\Omega = t\Omega

插值角度 θ(t)\theta(t) 与参数 tt线性 关系

根据我们前面的推导,在二维圆上我们有以下 slerp 插值公式

q(t)=sin[(1t)θ]sinθq0+sintθsinθq1\vec{q}(t) = \frac{\sin{[(1 - t)\theta]} }{\sin{\theta}}\vec{q}_0 + \frac{\sin{t\theta}}{\sin{\theta}}\vec{q}_1

我们将 p0\vec{p}_0p1\vec{p}_1 换成 四元数 q0, q1q_0,\ q_1 就得到

q(t)=sin[(1t)Ω]sinΩq0+sintΩsinΩq1q(t) = \boxed{ \frac{\sin{ [(1-t)\Omega] }}{\sin{\Omega}}q_0 + \frac{\sin{t\Omega}}{\sin{\Omega}}q_1 }

四元数 Slerp - 指数形式

我们知道,四元数的相乘表示旋转的叠加,并且我们已知

  • 起点姿态 q0q_0
  • 终点姿态 q1q_1

我们需要找到一个 “中间旋转” Δ\Delta 使得

q0Δ=q1q_0\Delta = q_1

可得

Δ=q01q1=q0q1\Delta = q_0^{-1}q_1 = q_0^\ast q_1

可以认为我们先用 q01q_0^{-1} 把当前姿态 “复原” 到基准坐标,再用 q1q_1 把它转到目标姿态,整个组合就等价于一次旋转 Δ\Delta

  • 注意,Δ\Delta“姿态到姿态”差旋转 输入/输出都是完整的 四元数,包含 朝向 + 自旋,而我们前面章节介绍的,输入 from 向量to 向量 来构造 from-to 四元数“方向到方向” 的最短旋转,输入仅仅是两个 3D 向量,输出一个把第一个向量转到第二支向量的 四元数不含轴向自旋
    • 旋转差 3 DoF 自由度:Δ=q01q1\Delta = q_0^{-1}q_1 完整 SO(3) 旋转;既包含 “把坐标轴指向哪儿”,也包含 “绕该方向自旋了多少”
    • from-to 2 DoF 自由度:只能把一根方向矢量对齐到另一根;无法决定 绕该方向自旋(roll)

由于 Δ\Delta 也是表示旋转的 单位四元数,所以我们可以将它写成 轴-角 表示以及等价的 指数表示

Δ=(cosΩ2, n^sinΩ2)=en^Ω2\Delta = (\cos{\frac{\Omega}{2}},\ \hat{n}\sin{\frac{\Omega}{2}} ) = e^{\hat{n}\frac{\Omega}{2}}

如此一来,我们执行插值将会变的容易,我们可以取 对数 以后对角度按 tt 进行 线性插值

logΔ=n^Ω2    tlogΔ=n^tΩ2\log{\Delta} = \hat{n}\frac{\Omega}{2} \implies t\log{\Delta} = \hat{n}\frac{t\Omega}{2}

如果我们再对左右取 指数,可得

etlogΔ=en^tΩ2=(costΩ2, n^sintΩ2)=(en^Ω2)t=Δte^{t\log{\Delta}} = e^{\hat{n}\frac{t\Omega}{2}} = (\cos{\frac{t\Omega}{2}},\ \hat{n}\sin{\frac{t\Omega}{2}} ) = (e^{\hat{n}\frac{\Omega}{2}})^t = \Delta^t

也就是说,要对 Δ\Delta 进行 t[0,1]t \in [0,1] 且保持旋转 不变,仅对 角度 进行 线性插值,我们只需要对原四元数 Δ\Delta实数幂 操作 Δt\Delta^t,所以 四元数幂 的实现是

Quaternion Pow(Quaternion q, float t)
{
    //---------------------------------------
    // 0)  若 q 不是严格单位四元数,先归一化
    //---------------------------------------
    q.normalize();   // 可省略,但最好保守处理
 
    //---------------------------------------
    // 1)  提取半角 θ/2
    //---------------------------------------
    float cosHalf = q.w;              // w = cos(θ/2)
    // 若 |cosHalf| ≈ 1 → 角度极小,需要特殊处理
    if (fabs(cosHalf) > 0.9999f)
    {
        // 与 Lerp 近似:q^t ≈ (1-t) + t*q
        return Quaternion::Lerp(Quaternion::Identity(), q, t).normalized();
    }
    float halfAngle = acos(cosHalf);  // θ/2  ∈ (0,π)
 
    //---------------------------------------
    // 2)  求旋转轴 n̂
    //---------------------------------------
    float sinHalf = sqrtf(1.0f - cosHalf * cosHalf);   // = |v|, > 0 因为上面排除了极小角
    Vector3 axis(q.x / sinHalf, q.y / sinHalf, q.z / sinHalf);  // 已单位化
 
    //---------------------------------------
    // 3)  把角度乘 t,再算新的 sin/cos
    //---------------------------------------
    float newHalf = t * halfAngle;     // = tθ/2
    float newCos = cosf(newHalf);
    float newSin = sinf(newHalf);
 
    //---------------------------------------
    // 4)  组合回四元数
    //---------------------------------------
    return Quaternion(axis * newSin, newCos);   // (x,y,z,w)
}
  • 若平台有 sincosf() 或 SIMD sin_cos(), 可以一次性同时取 sin 和 cos,更省性能

最终,我们可以得到四元数 Slerp指数幂形式

q(t)=q0Δt=q0(q01q1)t\boxed{ q(t) = q_0\Delta^t = q_0(q_0^{-1}q_1)^t }

它与 三角函数 写法完全等价,只需将 (q01q1)t(q_0^{-1}q_1)^t 展开成 (cosΩ2, n^sinΩ2)(\cos{\frac{\Omega}{2}},\ \hat{n}\sin{\frac{\Omega}{2}}) 并用 倍角公式 即可,但是 指数 写法更紧凑,且与实现 Pow() 函数(幂运算)天然耦合

struct Quaternion { float x, y, z, w; /* …ctor & ops… */ };
 
Quaternion Slerp(const Quaternion& a_, const Quaternion& b_, float t)
{
    Quaternion a = a_, b = b_;
 
    // ① 最短弧:同半球
    if (Dot(a, b) < 0.0f)
        b = -b;
 
    // ② 差旋转
    Quaternion delta = b * Inverse(a);
 
    // ④ 小角退化(利用 delta.w ≈ cos(θ/2))
    if (fabs(delta.w) > 0.9999f)
        return Normalize(Lerp(a, b, t));   // Nlerp 近似 
 
    // ③ Δ^t —— 直接用四元数 Pow() 函数
    Quaternion deltaPow = Pow(delta, t);
 
    // ⑤ 左乘起点
    return deltaPow * a;
}
 

以上是 指数形式 实现的 四元数 Slerp,但是不管是 指数形式 还是 三角函数形式,都无法避免 acosf,所以四元数的 Slerp 存在一定的性能消耗

四元数 Slerp vs 向量 Slerp

维度四元数 Slerp三维向量 Slerp
插值对象单位四元数 qS3q\in S^3(表示完整姿态/旋转单位向量 vS2\mathbf v\in S^2(仅表示方向;不含绕该方向的自旋)
几何空间四维单位球面 S3S^3 上的最短大圆弧三维单位球面 S2S^2 上的最短大圆弧
“距离”定义李群 SO(3) 的测地距离:Ω=2arccos(q0 ⁣q1)\displaystyle\Omega=2\arccos(q_0\!\cdot q_1)球面夹角:θ=arccos(v0 ⁣v1)\displaystyle\theta=\arccos(\mathbf v_0\!\cdot\mathbf v_1)
公式形态q(t)=q0(q01q1)tq(t)=q_0\,(q_0^{-1}q_1)^{t}
或 q(t)=sin((1t)Ω)sinΩq0+sin(tΩ)sinΩq1\displaystyle q(t)=\frac{\sin((1-t)\Omega)}{\sin\Omega}\,q_0+\frac{\sin(t\Omega)}{\sin\Omega}\,q_1
v(t)=sin((1t)θ)sinθv0+sin(tθ)sinθv1\displaystyle \mathbf v(t)=\frac{\sin((1-t)\theta)}{\sin\theta}\,\mathbf v_0+\frac{\sin(t\theta)}{\sin\theta}\,\mathbf v_1
是否“扭矩最小 / 匀速”是。角速度恒定、扭矩最小是。角速度恒定(但只对方向向量有意义)
双覆盖问题qqq-q 表示同一旋转;如果需要最短路径,则插值前必须先把终点翻到同一半球v\mathbf vv-\mathbf v 表示相反方向,语义不同,不做翻转
可组合性闭合于乘法:q(t1)q(t2)=q(t1+t2)q(t_1)\,q(t_2)=q(t_1+t_2)(当基准相同)不具备群结构,难以“相乘”组合
应用场景1. 刚体姿态、关节骨骼动画
2. 摄像机朝向 + roll
3. 任意两旋转的自然过渡
1. 纯方向插值(朝向一束射线、法线指向)
2. 只关心“看向哪里”,不关心自转
额外自由度包含“绕朝向轴”的自旋;Slerp 会正确插值该自旋没有轴向转动概念
实现成本需要四元数乘法、逆、幂;稍重只需三维向量运算;更轻

四元数保存了 “绕方向自转” 的信息;为了保持 匀速自旋,就得处理 q01q1q_0^{-1}q_1 再做幂运算,而向量只关心方向角度,公式更简单,如果只旋转镜头指向,而不想处理 roll向量 Slerp 就够;要连同 roll 一起平滑过渡,必须用 四元数 Slerp

双覆盖问题

在上面的分析中我们提到,在两个四元数之间进行插值时,插值可以采用两者之间最短或最长的圆弧,因为这两个四元数都代表 同一姿态

Quaternion double cover animation comparing the solid short path to negative q1 with the translucent long path to q1 before flipping.
同一姿态可由 q 与 -q 表示;实心箭头展示翻转后的最短 lift,紫色虚影展示未翻转时从 q₀ 走向 q₁ 的长路径。

我们一般会想让插值沿着 最短路径 进行,如果你曾经见过游戏中角色的弯曲错误,但动作却正确,那么很可能是没有沿着 最短路径 执行插值,我们在上面的代码中已经加入了 半球检测

// ① 最短弧:同半球
if (Dot(a, b) < 0.0f)
    b = -b;