骨骼蒙皮动画
骨骼蒙皮动画 - LBS、DQS 与 GPU Skinning
角色动画几乎都用骨骼蒙皮(Skeletal / Skinned Animation):
一套骨骼(层级变换) 驱动网格顶点变形。动画数据只记录骨骼随时间的变换,顶点通过蒙皮权重跟随骨骼运动 —— 用很少的数据驱动大量顶点。
一、骨骼蒙皮原理
- 骨骼(Bone / Joint):一棵层级变换树,子骨骼相对父骨骼。
- 蒙皮权重(Skin Weights):每个顶点绑定若干骨骼(通常 ≤4)及权重,和为 1。
- 线性混合蒙皮(LBS / Linear Blend Skinning):顶点最终位置 = Σ(权重ᵢ × 骨骼矩阵ᵢ × 顶点)。简单高效,但极端弯曲处有"糖纸/塌陷"瑕疵。
- 改进:双四元数蒙皮(DQS)缓解体积塌陷。
1.1 双四元数蒙皮(DQS)与体积塌陷
先看 LBS 为什么会塌陷。 LBS 直接对骨骼矩阵做加权线性混合:
问题出在旋转不能线性插值。旋转矩阵位于一个弯曲的流形()上,两个旋转矩阵的线性加权和通常不再是旋转矩阵 —— 它带有收缩分量。直觉上,把两个方向差异很大的旋转"取平均",得到的向量会被拉向两者之间、并缩短:
两根骨骼各转 +90° / -90°:
↑ M1(v) LBS: 0.5·M1(v)+0.5·M2(v)
\ 落在弦的中点 → 明显短于 v
\ → 线性平均 → • (缩短、向内塌陷)
/
↓ M2(v)于是在大幅弯曲的关节(肘、膝、指关节)和扭转处,被两根姿态差异大的骨骼共同影响的顶点会被"平均"到内侧,表面向内凹陷、体积丢失;纯扭转时则表现为经典的 "糖纸(candy-wrapper)" 收窄。权重和虽为 1,但矩阵平均破坏了刚性。
DQS 的做法:混合"刚体变换"本身,而不是矩阵元素。 用双四元数紧凑地表示一个刚体变换(旋转 + 平移):
- :单位四元数,编码旋转;
- :对偶部,把平移 编码进去;
- 单位双四元数()与刚体变换一一对应。
蒙皮时对各骨骼的双四元数做加权线性混合再归一化(DLB, Dual-quaternion Linear Blending):
关键在那个归一化:它把混合结果投影回单位双四元数流形,因此结果始终是一个合法的刚体变换(纯旋转 + 平移,无收缩、无剪切)。旋转分量被当作"真正的旋转"来插值(近似最短弧 ScLERP),而不是被逐元素平均掉 —— 这正是它保持长度、保持体积、消除糖纸收窄的根本原因。
需注意混合前让各 同号(点乘为负时取反),否则会走"长弧"产生翻转。
取舍:
| LBS | DQS | |
|---|---|---|
| 关节弯曲/扭转 | 体积塌陷、糖纸收窄 | 保体积、无糖纸 |
| 典型瑕疵 | 内凹、变细 | 关节处轻微外鼓(bulging) |
| 变换类型 | 支持含缩放/剪切的非刚性矩阵 | 只处理刚体(旋转+平移),缩放需另行处理 |
| 成本 | 最低 | 略高,但顶点着色器可承受,实时可用 |
因此 DQS 是很多引擎对 LBS 的可选升级:默认 LBS 便宜够用,需要肘/膝/手指等大弯曲、扭转部位保形时切到 DQS。两者都能放进 GPU Skinning 的顶点着色器(见下节)。
二、CPU Skinning vs GPU Skinning
蒙皮变形(对每个顶点按骨骼矩阵混合)计算量大,可放 CPU 或 GPU:
| CPU Skinning | GPU Skinning | |
|---|---|---|
| 蒙皮计算 | CPU 每帧算变形顶点 | 顶点着色器里算(骨骼矩阵传入) |
| 开销 | 占用 CPU、每帧写顶点缓冲 | 卸载到 GPU,CPU 轻 |
| 适用 | 少量角色 | 大量角色(主流) |
GPU Skinning 把每根骨骼的蒙皮矩阵上传(常用矩阵数组或纹理),顶点着色器按权重混合 —— 现代引擎默认。
Unity 的 GPU Resident Drawer 只处理
MeshRenderer,不支持SkinnedMeshRenderer—— 因为蒙皮网格每帧按各自骨骼姿态变形,无法用"共享网格 + 一个变换矩阵"实例化。角色动画走的是 GPU Skinning 这条独立路径。
总结
骨骼蒙皮是角色动画的基础。LBS 简单高效但在大弯曲关节处产生体积塌陷和糖纸收窄,DQS 通过双四元数混合保留刚体变换的旋转特性,以略高的代价消除这些瑕疵。蒙皮计算可放 CPU 或 GPU,现代引擎默认使用 GPU Skinning 将计算卸载至顶点着色器,以支持大量角色同时渲染。
