GAMES104-08:动画:动画技术基础
八、动画:动画技术基础
8.1 2D游戏动画技术
8.1.1 Sprite Animation
记录每一帧的动作,循环播放
在不同视角采样一系列的动作,根据当前视角播放不同动画
现代游戏引擎中,粒子效果就是Sprite Animation
8.1.2 Live2D
将一个角色的所有元素,变成一个个小图元,通过变换图元,组成人物的不同姿态
首先,定义不同图元的深度,以控制图元是否出现,不同图元之间的层次关系
然后,生成ArtMesh,通过拖动ArtMesh上的控制点,控制图元的变化
设置动画关键帧,从而进行动画插值
8.2 3D游戏动画技术
8.2.1 DoF:Degress of Freedom 自由度
一个物体可以在多少个维度进行变化
- 刚体:6个自由度,平移3个,旋转3个
8.2.2 Rigid Hierarchical Animation
- 将mesh分为层次化的多个可以变化的刚体,动画即是改变这些刚体的平移/旋转
- 缺点:不同关节之间可能会穿插
8.2.3 Per-vertex Animation 顶点动画
顶点动画:
- 记录每个顶点的位置随时间的变化
- 一般存两个texture,一个texture记录顶点的位置,另一个记录顶点的法向
- 通常是用物理引擎离线计算好,然后存到texture中
8.2.4 Morph Target Animation
- 也是一种顶点动画,但是记录的是顶点影响权重
- 存储多个Key Poses,然后在Key Poses之间进行插值
- 在捏脸系统中更好用
8.2.5 Skinned Animation 蒙皮动画
刚体骨骼驱动外表的蒙皮进行动画,每个顶点由多个骨骼同时影响
8.2.6 Physics-based Animation
- 布娃娃系统
- 衣料、流体模拟
- IK反向动力学
8.2.7 Animation Content Creation
- 动画师设置关键帧
- 动捕
8.3 蒙皮动画的实现
8.3.1 如何让Mesh播放动画
- 创建mesh的binding pose
- 创建binding skeleton
- 刷上蒙皮,告诉每个顶点应该受到哪些骨骼的影响
- 移动skeleton
- 蒙皮上的顶点会跟着运动
8.3.2 不同的空间
- 世界空间:全局坐标系
- 模型空间:以角色为中心,相对世界空间有平移&旋转
- 局部空间:每根骨骼的坐标系,每根骨骼都不相同,需要逐步累加进来
8.3.3 两种骨骼
Humanoid Skeleton:两足动物的骨骼
- 起点在人的胯部:Pelvis
- 有时会在Pelvis之上添加一个root
Non-humanoid Skeleton:四足动物的骨骼
8.3.4 骨骼 Bone & 关节 Joint
- 在引擎中,存储的是关节的信息
- 相邻两个关节定义一个骨骼
- joint是严格的刚体,无法被扭曲;而bone是可以扭曲的
8.3.5 游戏中的关节
- 通常会添加一些额外的骨骼,用于控制角色的面部表情、衣物等附加物
- 武器、骑乘等都会有对应的关节,用于绑定外部物品
8.3.6 根骨骼
- 通常会放置于两脚之间,便于表示离地高度、移速等属性
- 四足动物的Pelvis在尾椎处,而root在肚子下四足中心
8.3.7 绑定动画
- 当人物骑乘在马身上时,两者的bind point就会绑定到一起
- 不仅仅是位置的绑定,还有旋转的绑定
- 两者分别播放自己的动画
8.3.8 绑定姿势:T-pose vs A-pose
T-pose:肩胛处的骨骼是压缩的,精度不够
A-pose:人相对自然的站在那里,肩胛处的精度会更高
8.3.9 骨骼姿势
关节姿势有9个自由度:平移、旋转、缩放
8.4 3D旋转的数学原理
8.4.1 2D空间的旋转
8.4.2 3D空间的旋转:欧拉角
8.4.2.1 旋转矩阵
8.4.2.2 Yaw & Pitch & Roll
8.4.2.3 问题:欧拉角的运算是严格依赖顺序的
8.4.2.4 万向锁:Gimbal lock
8.4.2.5 欧拉角的退化现象:β=90°
当β=90°时,α和γ各自都没有意义,只有α-γ有意义,此时最终的DoF变为了1
8.4.2.6 欧拉角的问题
- 万向锁
- 难以插值
- 难以进行旋转的叠加,必须通过旋转矩阵相乘
- 难以进行任意轴旋转,必须将其分解为XYZ轴的旋转
8.4.3 四元数 Quaternion
8.4.3.1 用复数表示2D旋转
8.4.3.2 四元数的定义
定义:\(q=a+bi+cj+dk \ (a,b,c,d\in R)\),其中 \(i^2=j^2=k^2=ijk=-1\)
性质:单位四元数的共轭,就是它的逆
8.4.3.3 欧拉角 => 四元数
8.4.3.4 用四元数表示旋转
8.5 关节与蒙皮
8.5.1 三种数据:旋转、平移、缩放
Orientation 空间上的朝向
Position
- 通常是不变的
- 但是当角色蹲下时,是通过pelvis相对于root的位置变换表示的
- 表情动画也会用到位置变换
Scale:缩放变换
- 通常也不会变
- 但是在更改角色面部关节时,可能会改变
8.5.2 Affine Matrix 仿射矩阵
8.5.3 局部空间 => 模型空间
对于每一个关节\(j\):
- \(p(j)\):父关节
- \(M_{p(j)}^l\):父关节在局部空间的关节姿势
- \(M_j^m\):关节在模型空间的关节姿势
\[ M_j^m=\prod_{j=J}^0 M_{p(j)}^l \]
即从当前节点开始,逐步乘父节点的关节姿势,直到到达根节点
8.5.4 关节姿势的插值:局部空间 vs 模型空间
将动画存储在局部坐标系
- transform中的数据更少
- 更容易进行插值/混合
8.5.5 Single Joint Skin
8.5.5.1 顶点相对于关节的局部坐标永远不变
将mesh上的一个顶点,绑定到一个骨骼上
- 每个顶点可以绑定一个或多个关节,每个关节有不同的权重
- 顶点在每个绑定关节的局部空间的位置是固定的
8.5.5.2 Skinning Matrix Palette
- 先计算每一个关节的Skinning Matrix,保存起来
- 在渲染顶点的时候,可以直接查表,不用重新计算蒙皮矩阵
优化:通常会再乘上\(M^w\),将模型空间转化为世界空间
8.5.5.3 在内存中表示一个骨骼
8.5.6 Weighted Skinning with Multi-joints
- 每个mesh上的顶点会由多个关节同时作用
- 每个关节有不同的权重,但是要求权重和为1
- 计算每个关节对应的顶点的模型空间坐标,在模型空间中进行混合
8.5.7 Clip
将每一帧的骨骼姿势放在一个序列里面,称为一个clip
8.5.8 在不同Pose之间进行插值
位移&缩放 lerp:线性插值
旋转 Nlerp:q1和q2的线性插值,然后进行归一化
最短路径插值:判断一下q1·q2,如果是<0,则需要反向插值
Nlerp的问题:插值的角速度不恒定
Slerp:通过arccos,计算两个方向的夹角,通过θ插值
- 但是计算比较费资源
- 并且当角度很小时,结果不精确
- 通常会指定一个角度,小于它用NLERP,大于它用SLERP
8.5.9 简单动画的Runtime Pipeline
8.6 动画压缩技术
8.6.1 减少DoF
Scale:直接忽略
Translation:只存储最初的值
Rotation:通过关键帧进行插值
- 以第0帧作为第一个关键帧,通过插值计算下一帧
- 如果误差高于某个值,则将其作为下一个关键帧
Catmull-Rom曲线:
- 由四个控制点生成一个曲线,拟合原有的旋转曲线
8.6.2 用定点数代替float
8.6.3 误差传播
由于关节的模型坐标是从根节点累乘得到的,因此会导致误差的累积
衡量精确度:Visual Error
误差补偿:子骨骼对父骨骼产生的误差进行补偿
- 缺点:父骨骼的误差叠加后,可能在子骨骼上产生高频数据,导致末端骨骼产生高频抖动
8.7 动画制作流程
构建mesh,用低精度的mesh做动画
在关节处添加网格,防止关节处变形
骨架绑定,并添加Game Play关节(如武器关节、Pelvis、root)
自动计算skinning
手动调整skinning
制作动画:设置关键帧及时间间隔
导出动画
- 如果在动画中root产生位移,会将root的移动单独导出为一个位移曲线给引擎用