Games104学习笔记(9)
GAMES104:游戏引擎的动画技术基础(上)
第 8 讲(上)|主题:Animation System — Basics of Animation Technology
视频时长:约 1 小时 14 分 25 秒
说明:本文根据本地视频课件与讲解脉络整理,不是逐字稿。
一句话理解
游戏动画的核心任务,是把随时间变化的姿态稳定、可控且高效地施加到角色或物体上。对于 3D 角色,最常用的方案是用具有层级关系的骨架描述姿态,用蒙皮权重把骨架运动传递给网格顶点;而为了可靠地表示和组合旋转,引擎通常会从直观但有局限的欧拉角,转向四元数等表示。
本讲学习目标
- 了解影视与游戏中常见的动画生成方式,以及实时游戏动画面对的额外约束。
- 区分序列帧、Live2D、刚体层级、逐顶点、骨骼蒙皮与物理动画的基本思路。
- 理解骨架、关节、骨骼、Bind Pose、Pose、蒙皮权重与动画数据之间的关系。
- 掌握局部空间、模型空间、世界空间在骨骼动画中的职责。
- 理解欧拉角的旋转顺序与万向节死锁问题。
- 建立四元数用于表示、组合和施加三维旋转的基础直觉。
1. 为什么游戏动画不是“播放一段动作”
人对运动变化非常敏感。从早期视觉暂留装置,到手绘动画、定格动画、动作捕捉,动画技术一直在解决同一个问题:如何让静态画面在连续播放时形成可信的运动。
电影可以离线完成每一帧的制作与渲染;游戏则必须在运行时根据玩家输入和世界状态生成结果。因此,游戏动画除了“好看”,还要处理以下问题:
- 交互性:角色随时可能转向、停止、被击中、切换武器或改变目标。
- 实时性:动画系统必须在有限帧预算内完成采样、混合、姿态计算与蒙皮。
- 复杂状态组合:移动、瞄准、上半身攻击、表情和受击可能同时存在。
- 环境响应:脚部要接触地面,手要握住物体,身体可能受碰撞或物理影响。
- 观感连续性:状态切换不能突兀,动作节奏和重心需要维持可信。
因此,动画系统应被理解为一个把“输入状态、动作资产与角色结构”转换为“当前帧姿态”的运行时系统,而不仅是播放器。
2. 动画技术的整体分类
课件中的基本动画技术可按驱动对象分为二维、三维和物理驱动三类。
| 类型 | 主要被驱动的数据 | 典型特点 | 常见用途 |
|---|---|---|---|
| 2D 序列帧 | 图片帧 | 直接、易理解,但帧数增加会占用资源 | 像素角色、特效、UI 动画 |
| 2D 变形动画 | 图片网格与控制点 | 用少量图像产生连续变形 | Live2D 角色、立绘 |
| 刚体层级动画 | 独立刚体部件的层级变换 | 部件之间没有连续形变 | 机械、积木式角色、早期角色 |
| 逐顶点动画 | 每帧顶点位置 | 表达自由,但数据量大 | 特定变形、布料或缓存动画 |
| 骨骼蒙皮动画 | 骨架变换与顶点权重 | 角色动画的主流方案 | 人形、动物与大多数可动角色 |
| 物理动画 | 物理约束和动力学状态 | 响应真实交互,但可控性更难 | 布料、布娃娃、碰撞反应 |
这些方案不是互斥的。实际角色常以骨骼动画为主,在局部叠加物理、IK、表情或程序化修正。
3. 二维动画:帧序列与图像变形
3.1 Sprite Animation
Sprite Animation 将动画的每个状态保存为一张或多张图像,按既定顺序播放。精灵图通常把多个帧打包在同一张纹理中,以减少资源切换。
它的基本模型很直接:
当前显示帧由播放时间、帧率与循环规则共同决定。
优点是结果可完全由美术控制,风格稳定;代价是每个新姿态都需要额外图像数据。若动作持续时间更长、方向更多或分辨率更高,资源和制作量会快速增长。
循环播放需要保证首尾姿态与节奏连贯,否则在切换回第一帧时会出现跳变。序列帧适合离散、可预先绘制的动作,但不擅长根据运行时输入连续改变姿态。
3.2 Live2D:以控制点驱动二维变形
Live2D 的思路不是为每一帧都准备一张完整图片,而是将二维图像拆分为可变形部分,并通过网格控制点改变局部形状。
可以把它理解为:
- 为头发、脸部、衣物等图层建立可变形网格。
- 用参数或控制点描述转头、眨眼、呼吸等变化。
- 对网格顶点做插值与变换,让原始图像产生连续形变。
这降低了重复绘制大量中间帧的需求,也使少数参数能够驱动多种视觉变化。但二维图像中原本不可见的部分并不会自动出现,较大视角变化仍受原始素材与分层方式限制。
4. 三维动画的几种思路
4.1 刚体层级动画
刚体层级动画将角色拆成彼此独立的硬部件,再用父子层级连接。例如上臂带动前臂,前臂带动手部。每个部件只做刚体变换,不发生自身弯曲。
其优势是结构直观、计算简单;缺点是连接处容易显得割裂。对人体、布料等需要连续形变的对象,仅靠独立刚体部件通常不够自然。
4.2 逐顶点动画
逐顶点动画直接记录或计算网格顶点在各时刻的位置。它能表达非常自由的形变,例如旗帜起伏、液体扰动或预烘焙的复杂模拟。
问题在于顶点数量通常远多于骨骼数量。若每一帧都保存或传输完整顶点位置,内存、带宽和插值成本都会显著增加。它适合特定效果,不是通用角色动作的首选表示。
4.3 骨骼蒙皮动画
骨骼蒙皮动画将“控制结构”和“显示网格”分离:
- 骨架提供少量具有父子关系的关节。
- 动画数据描述关节随时间变化的局部变换。
- 网格顶点绑定到一个或多个关节,并记录权重。
- 当前骨架姿态通过权重影响每个顶点,形成连续变形。
它用相对较少的关节变换控制大量顶点,在数据规模、动作复用和表现力之间取得了很好的平衡,因此成为角色动画的主流基础。
4.4 物理动画
物理驱动动画依据质量、碰撞、约束、速度和外力计算结果。布娃娃(Ragdoll)能让肢体在受力后自然摆动;布料也可以由物理或近似物理方法生成。
物理的优势是对环境和外力反应自然,挑战则是可控性:纯物理结果未必符合角色意图、镜头需求或游戏手感。工程中常把物理作为骨骼动作的补充,而不是完全替代动画资产。
5. 骨骼蒙皮动画的组成
5.1 从网格到可动角色
将一个静态网格变为可动角色,通常需要以下几类数据:
- Mesh:角色表面,由顶点、三角形、法线、UV 等组成。
- Skeleton:用于控制姿态的关节层级。
- Bind Pose:网格绑定骨架时的参考姿态。
- Skinning Weights:每个顶点受哪些关节影响,以及各自影响比例。
- Animation:随时间变化的关节局部平移、旋转和缩放数据。
动画系统每帧先求出当前骨架姿态,再把姿态应用于网格。这里的关键是:网格顶点不需要独立保存每一帧的位置;它们由较少的关节变换间接控制。
5.2 Joint、Bone 与层级
Joint(关节) 是骨架中的变换节点,通常保存局部平移、旋转和缩放。Bone(骨骼) 常用来指两个关节之间的连接或其可视化表示。程序实现中,真正参与层级变换的通常是 Joint。
骨架是一棵树:
- 根关节定义整个角色的基础参考。
- 子关节的变换相对于父关节定义。
- 父关节变化会传递给所有后代。
例如,旋转肩关节会带动肘、腕和手指;这正是层级表示比逐部件独立存储更紧凑、更符合身体结构的原因。
5.3 人形与非人形骨架
人形骨架的关节数量取决于精度要求:基础角色可只保留躯干和四肢的主要节点,高质量角色还会加入手指、面部和辅助关节。动物或怪物并不一定遵循人形结构,但仍可用树状骨架表示躯干、四肢、尾巴、翅膀等控制关系。
骨架设计首先服务于动作需求,而不是追求关节越多越好:
- 关节过少会限制弯曲和形变质量。
- 关节过多会增加动画制作、运行时计算和蒙皮调试成本。
- 关键是让关节位置与角色主要转动、拉伸和受力区域相匹配。
5.4 Bind Pose 与当前 Pose
Bind Pose 是网格与骨架建立绑定关系时的参考姿态,常见为 T Pose (现在多用A Pose) 或其他便于蒙皮的中性姿态。它回答“顶点原本相对于骨架在哪里”。
Pose 则是某一时刻所有关节的变换集合。走路、挥手、下蹲,本质上都是不同的 Pose;动画播放是在时间轴上采样一系列 Pose,并在相邻状态之间平滑过渡。
可以把它们区分为:
| 概念 | 描述的问题 |
|---|---|
| Bind Pose | 网格在绑定参考状态下与骨架的关系 |
| Current Pose | 当前帧关节实际处于什么姿态 |
| Skinning | 如何将当前姿态相对 Bind Pose 的变化传递给顶点 |
5.5 蒙皮权重
一个顶点往往由多个关节共同影响。例如手肘附近的顶点会同时受到上臂与前臂影响。权重 (w_i) 代表第 (i) 个关节对该顶点的影响比例,通常满足:
$\sum_i w_i = 1$
线性混合蒙皮(Linear Blend Skinning, LBS)的直观形式为:
$v’ = \sum_i w_i M_i v$
其中 (v) 是绑定姿态下的顶点,(M_i) 是当前骨骼相对绑定姿态的变换,(v’) 是变形后的顶点位置。
这种方法高效且易于 GPU 并行,但在关节大幅扭转时可能出现体积塌陷或“糖纸包装”式伪影。这里需要先建立一个正确的基础认识:蒙皮不是把顶点硬塞到某根骨头上,而是按权重混合多个关节的相对运动。
5.6 绑定到角色之外的物体
骨架也可以驱动武器、装备和其他附属物。例如把剑绑定到手部关节,就能使它继承手部的世界变换。对于马匹、骑手或多部件机械体,也可以通过明确的绑定点和层级关系传递运动。
这类绑定的本质是复用已有的坐标变换链,而不是为每个附属物单独制作完整动画。
6. 坐标空间:动画计算为何离不开“相对谁”
骨骼动画中的变换必须说明所在空间。课件强调了局部空间、模型空间与世界空间的差别。
6.1 Local Space
关节在局部空间中的变换相对于父关节定义。比如手腕的局部旋转是“相对前臂旋转多少”,而不是直接相对世界坐标旋转。
局部空间让动画数据具有可组合性:如果角色整体移动,或肩膀朝向改变,手腕仍可保留自己的局部动作。
6.2 Model Space 与 World Space
- Model Space:相对于角色模型根节点的空间。骨架的层级变换累积后,通常可得到各关节在模型空间中的位置和朝向。
- World Space:相对于游戏场景世界原点的空间。角色整体位移、朝向和缩放会把模型空间中的结果进一步转换到世界空间。
对一条父子关节链,概念上可写为:
$M_{\mathrm{global}}(\mathrm{child}) = M_{\mathrm{global}}(\mathrm{parent}),M_{\mathrm{local}}(\mathrm{child})$
根节点的全局变换再与角色的模型/世界变换组合。明确空间是动画调试的重要前提:很多“骨头方向不对”“挂点偏移”的问题,根源都是把不同空间的矩阵或向量混在一起使用。
7. 旋转表示:从欧拉角开始
动画中的关节变换包含平移、旋转和缩放,其中旋转最容易产生表示与插值问题。
7.1 二维旋转
二维中,绕原点旋转角度 θ 可用矩阵表示:
$R(\theta) = \begin{bmatrix}\cos\theta & -\sin\theta \ \sin\theta & \cos\theta\end{bmatrix}$
它将一个二维向量映射到旋转后的方向。二维旋转只有一个平面和一个旋转轴,因此表示相对直接。
7.2 三维中的基础旋转矩阵
三维空间可绕 (x)、(y)、(z) 轴分别旋转,并由对应的旋转矩阵表示。将多个基础旋转相乘,就能得到更复杂的姿态。
欧拉角用三个角度描述这种组合,例如 yaw、pitch、roll。它直观、便于在编辑器中查看和输入,因此很常见;但它的含义依赖于轴顺序和坐标约定。
7.3 旋转顺序不可交换
三维旋转一般不满足交换律:
$R_xR_y \ne R_yR_x$
也就是说,先绕 (x) 轴再绕 (y) 轴,通常与反过来操作得到的结果不同。使用欧拉角时,必须明确旋转顺序,例如 XYZ、ZYX,以及旋转是相对局部轴还是世界轴。
这不是实现细节,而是数据语义的一部分:如果导入器、动画工具和运行时对顺序理解不同,角色姿态会明显错误。
7.4 Gimbal Lock(万向节死锁)
欧拉角的一个经典问题是万向节死锁。当某个中间旋转使两个旋转轴重合时,原本独立的自由度会丢失。此时:
- 某些方向无法独立旋转。
- 同一姿态可能对应多组欧拉角。
- 从一个角度平滑插值到另一个角度,路径可能不符合直觉。
这不表示欧拉角完全不能使用;它仍适合界面输入、简单控制和某些局部场景。问题在于,当系统需要稳定组合、插值和连续表示任意三维姿态时,欧拉角并不是最可靠的内部表示。
8. 四元数:更适合三维姿态的旋转工具
8.1 从复数到四元数
二维旋转可以借助复数理解:单位复数的乘法对应平面内旋转。四元数将这一思路推广到三维,通常写作:
$q = a + bi + cj + dk$
其中 (a) 是实部,((b, c, d)) 是虚部向量部分。四元数满足特定的乘法规则,例如:
$i^2 = j^2 = k^2 = ijk = -1$
对于动画工程,重要的不在于背诵全部代数历史,而在于认识到:单位四元数可以紧凑地表示三维旋转,并能通过乘法完成旋转组合。
8.2 基本运算
对四元数 (q = (w, x, y, z)):
- 模长:
|q| = √(w² + x² + y² + z²)。 - 共轭:
q* = (w, -x, -y, -z)。 - 单位四元数:模长为 1;旋转计算中通常应保持归一化。
- 逆:对单位四元数,
q⁻¹ = q*。 - 乘法:用于按顺序组合旋转,且通常也不满足交换律。
如果运算累积导致单位四元数偏离单位长度,应重新归一化;否则后续旋转可能逐渐产生数值误差。
8.3 用四元数旋转向量
将三维向量 (v) 写成纯虚四元数 ((0, v_x, v_y, v_z)),用单位旋转四元数 (q) 作用后得到:
$v’ = qvq^{-1}$
这给出了四元数“如何旋转一个向量”的核心形式。它避免了直接手工拼接多个欧拉角,同时仍对应明确的空间旋转。
8.4 轴角形式
若旋转轴是单位向量 \hat{u} = (u_x, u_y, u_z)),旋转角度为 θ,则对应的单位四元数可写成:
$q = \left(\cos\frac{\theta}{2},,u_x\sin\frac{\theta}{2},,u_y\sin\frac{\theta}{2},,u_z\sin\frac{\theta}{2}\right)$
轴角形式的直觉很清楚:围绕哪根轴旋转,以及旋转多少。四元数将这两个信息编码为四个分量,便于在运行时组合与插值。
8.5 四元数解决了什么,没解决什么
四元数避免了欧拉角的万向节死锁,也更适合作为动画姿态的连续内部表示。但它不意味着动画问题从此消失:
- 四元数有双重表示:(q) 与 −(q) 表示同一个旋转,插值时需要保证方向一致。
- 四元数必须保持归一化。
- 旋转表示正确,不等于动作本身自然;动作混合、脚底接触、约束和物理响应仍需额外系统处理。
9. 关键概念对照
| 概念 | 作用 | 容易混淆之处 |
|---|---|---|
| Skeleton | 关节层级与控制结构 | 不等同于可见网格 |
| Joint | 保存变换的节点 | 通常是程序中真正计算的层级节点 |
| Bone | 两个关节之间的连接/可视化 | 不必等于单独的数据对象 |
| Bind Pose | 绑定时的参考姿态 | 不等于当前播放姿态 |
| Pose | 某一时刻所有关节的变换集合 | 一段动画包含许多 Pose |
| Skinning | 用关节变换影响网格顶点 | 不等于只把顶点绑定到单根骨头 |
| Local Transform | 相对父关节的变换 | 不能直接当成世界空间位置 |
| Euler Angle | 三个按顺序组合的旋转角 | 顺序不同,结果不同;可能死锁 |
| Quaternion | 表示三维旋转的四元数 | 旋转用单位四元数,注意归一化与符号 |
10. 本讲的工程主线
本讲的内容可以收束为一条数据流:
动画时间 → 采样各关节局部变换 → 沿骨架层级累积姿态 → 得到相对 Bind Pose 的关节变换 → 按顶点权重蒙皮 → 输出当前帧角色网格
其中,旋转表示贯穿整条链路。欧拉角足够直观,适合编辑与展示;四元数更适合在运行时做连续旋转、组合和后续插值。理解这条链路后,再学习动画混合、状态机、动画图、IK、重定向和压缩时,才能看清它们分别修改的是“动作来源”“关节姿态”还是“最终网格结果”。
小结
- 游戏动画需要在交互、实时性、复杂状态组合与视觉连续性之间平衡。
- 2D 序列帧直接可靠,Live2D 用控制点与网格变形扩展二维表现。
- 骨骼蒙皮以少量关节控制大量顶点,是通用角色动画的基础方案。
- 骨架是关节层级;Bind Pose 说明绑定参考,Pose 表示当前时刻的姿态。
- 局部空间让父子关节关系可组合,模型空间与世界空间负责把结果放入角色和场景。
- 欧拉角直观但依赖旋转顺序,且可能发生万向节死锁。
- 单位四元数能够稳定表示和组合三维旋转,是动画系统的重要数学基础。
参考资料
- 本地视频:
08.游戏引擎的动画技术基础(上) _ GAMES104-现代游戏引擎:从入门到实践-p01-80.mp4 - B站视频:08. 游戏引擎的动画技术基础(上)