动画系统
游戏引擎动画系统 - 骨骼蒙皮、状态机与 Motion Matching
引言:角色动画的主流是骨骼蒙皮
角色动画几乎都用骨骼蒙皮(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 把每根骨骼的蒙皮矩阵上传(常用矩阵数组或纹理),顶点着色器按权重混合 —— 现代引擎默认。
GPU Resident Drawer 只处理
MeshRenderer,不支持SkinnedMeshRenderer—— 因为蒙皮网格每帧按各自骨骼姿态变形,无法用"共享网格 + 一个变换矩阵"实例化。角色动画走的是 GPU Skinning 这条独立路径。
三、动画混合与状态机
游戏里动画不是单个剪辑播放,而是动态混合:
| 机制 | 作用 |
|---|---|
| 动画状态机(State Machine) | 用状态 + 转换(Transition)组织动画(Idle→Walk→Run),按条件切换 |
| 混合树(Blend Tree) | 按参数(速度/方向)插值混合多个剪辑(如行走↔奔跑平滑过渡) |
| 分层(Layers) | 上半身/下半身分层叠加(如跑动时挥手) |
| IK(反向动力学) | 由末端目标反解骨骼(脚踩地、手抓物),修正贴合环境 |
| 过渡混合(Crossfade) | 切换时在两剪辑间淡入淡出,避免跳变 |
四、动画压缩
动画数据(大量骨骼 × 关键帧 × 变换)体积可观,需压缩:
- 关键帧削减:去掉可由插值还原的冗余帧。
- 曲线拟合:用样条近似骨骼曲线,存控制点而非逐帧。
- 量化(Quantization):降低旋转/位移的存储精度(旋转常用量化四元数)。
- 误差阈值:在可接受误差内最大化压缩。
动画压缩是质量 vs 体积/内存的权衡;过度压缩会导致抖动/穿帮。
五、Motion Matching(动作匹配)
较新的技术,弱化手工状态机:
- 思想:维护一个大动作数据库;每帧根据当前姿态 + 期望轨迹,在数据库里搜索最匹配的动作片段继续播放。
- 优点:过渡自然、响应灵敏、减少手工连线状态机的工作量。
- 代价:动作数据量大、搜索有开销、需要大量动捕数据。
六、性能与实践
- 蒙皮开销:大量角色时用 GPU Skinning;远处角色降骨骼数 / 降更新频率(动画 LOD)。
- 动画 LOD:远处角色降低采样频率、简化骨骼、关闭 IK。
- 可见性剔除:屏幕外角色不更新动画。
- 实例化蒙皮:人群系统常把骨骼动画烘焙进纹理(Animation Texture / VAT),再用 GPU Instancing 画海量角色 —— 绕开 SkinnedMeshRenderer 的限制。
- 内存:动画剪辑压缩 + 按需加载。
附:核心概念速查
| 概念 | 说明 |
|---|---|
| 骨骼蒙皮 | 骨骼层级驱动网格顶点变形 |
| 蒙皮权重 | 顶点绑定骨骼的权重(≤4,和为1) |
| LBS / DQS | 线性混合蒙皮 / 双四元数蒙皮 |
| GPU Skinning | 顶点着色器里做蒙皮,卸载 CPU |
| 状态机 / 混合树 | 组织与插值动画剪辑 |
| IK | 反向动力学,由末端反解骨骼 |
| 动画压缩 | 关键帧削减/曲线拟合/量化 |
| Motion Matching | 数据库搜索最匹配动作 |
| VAT / 动画纹理 | 把动画烘焙进纹理,供实例化人群 |
总结
本文系统阐述了游戏引擎动画系统的核心技术与实践。骨骼蒙皮动画(LBS/DQS)是角色动画的基础,其中双四元数蒙皮有效解决了 LBS 在大弯曲关节处的体积塌陷和糖纸收窄问题。GPU Skinning 将蒙皮计算卸载至顶点着色器,是处理大量角色的主流方案。动画状态机、混合树、分层与 IK 共同构成了游戏的动画混合与状态管理框架。动画压缩通过关键帧削减、曲线拟合与量化来平衡质量与内存。Motion Matching 作为一种替代方案,通过搜索动作数据库实现更自然的过渡。性能优化方面需关注蒙皮开销、动画 LOD、可见性剔除及实例化蒙皮(VAT)等技术。
