中EN
概念页

动画压缩

Animation Compression

未压缩的动画每关节每样本最多 10 个 32 位浮点通道,数据量巨大。GEA 列出的压缩手段按力度递增:省略无用通道、量化(如 16 位 RL 编码)、降低采样率或省略近似线性的关键帧、用 B 样条拟合曲线、小波压缩,以及最根本的:只把需要的片段放进内存(选择性加载与流式加载)(GEA4 卷 II 13.8)。

数据量有多大

每通道 4 字节 × 10 通道 × 30 样本/秒 = 每关节每秒 1200 字节(约 1.17 KiB)。100 关节的骨骼(以今天的标准算小)约每秒 117 KiB;1000 秒动画(对现代游戏偏少)约 114.4 MiB。PS3 只有 256 MiB 主内存和 256 MiB 显存;PS4 有 8 GiB、PS5 有 16 GiB 统一内存,但内存更应该花在动画的丰富程度上(卷 II 13.8)。

数据大的三个原因:通道多;关节多(PS3/Xbox 360 时代人形角色 250 个以上,部分 PS4/Xbox One 游戏超过 800,PS5/Xbox Series 更多);采样率高(卷 II 13.8.3)。

通道省略

  • 多数角色不需要非均匀缩放,三个缩放通道可减为一个;有些游戏除面部外完全不存缩放。
  • 人形骨头不能拉长,平移通道通常只给根、面部关节,有时加锁骨。
  • 四元数是单位长度,只存 x、y、z,运行时重建 w。(补充:w=±1−x2−y2−z2w=\pm\sqrt{1-x^2-y^2-z^2} 的符号无法从三个分量恢复。因为 qq 与 −q-q 表示同一旋转,导出时可把 w<0w<0 的四元数整体取反,保证 w≥0w\ge0;但逐帧取反会破坏相邻样本的连续性,影响插值,见 四元数。另一种常见做法是“最小三分量”(smallest three):丢弃绝对值最大的分量并用 2 位记录其下标。GEA 未提这一点。)
  • 整段不变的通道只存 t=0 的一个样本加一个"常量"标志位。
  • 例:100 关节、无缩放、只有根有平移,只需 303 个通道(每关节 3 个四元数分量 + 根的 3 个平移),而全量是 1000 个(卷 II 13.8.1)。

量化

把 32 位浮点转成 n 位整数称为量化,包括编码与解码,是有损的。四元数分量落在 [−1,1][-1,1],经验表明 16 位精度就够,用 32 位浮点等于每通道浪费 16 位(卷 II 13.8.2)。浮点格式本身见 数值表示与浮点数。

做法:把取值区间等分成 N=2nN=2^n 段,用段索引表示值。(注意:下面 RL 代码实际把 [0,1][0,1] 映射到 2n2^n 个等距格点 0,12n−1,…,10,\tfrac{1}{2^n-1},\dots,1,即间隔是 2n−12^n-1 个,两端 0 和 1 都能精确重建;和正文“2n2^n 个区间”的说法略有出入。)GEA 引用 Jonathan Blow 在 Game Developer 杂志 Inner Product 专栏的文章,编码有截断到下边界 (T) 与四舍五入到区间中心 (R) 两种,重建有返回左端 (L) 与返回中心 (C) 两种:

组合评价
TL、RC避免:会给数据整体减少或增加"能量"
TC带宽效率最高,但无法精确表示 0(0.0f 解码后变成小正数)
RL通常最佳,GEA 示例采用
U32 CompressUnitFloatRL(F32 unitFloat, U32 nBits) {
    U32 nIntervals = 1u << nBits;
    F32 scaled = unitFloat * (F32)(nIntervals - 1u);  // 映射到 [0, nIntervals-1]
    U32 rounded = (U32)(scaled + 0.5f);              // 四舍五入
    if (rounded > nIntervals - 1u) rounded = nIntervals - 1u;
    return rounded;
}
F32 DecompressUnitFloatRL(U32 quantized, U32 nBits) {
    U32 nIntervals = 1u << nBits;
    return (F32)quantized * (1.0f / (F32)(nIntervals - 1u));
}

代码只在上端钳制;输入为负时 (U32)(scaled + 0.5f) 把负浮点转成无符号整数,在 C++ 中是未定义行为(结果不在目标类型范围内,见 cppreference“Floating–integral conversions”),所以调用方必须先把值钳制到 [0,1][0,1](平移示例就先钳制了)。

任意区间 [min,max][min,max] 先归一化到 [0,1][0,1]。四元数用 min=−1、max=1、16 位。平移理论上无界,但关节实际移动不远,可定一个合理范围(示例 ±2 m),超出就报错;世界空间制作的过场里根关节平移可能很大,所以可以逐片段或逐关节选取范围并与压缩数据一起存储,开销可忽略(卷 II 13.8.2)。顶点数据的同类做法见 顶点压缩与精度。

采样率与关键帧省略

  • 整体降低采样率:有些动画以 15 Hz 导出依然好看,数据减半。
  • 省略近似线性区间内除端点外的样本,运行时线性插值恢复。需要额外存每个样本的时间,可能抵消部分收益,但确有引擎成功使用(卷 II 13.8.3)。

基于曲线的压缩

RAD Game Tools 的 Granny(已停售,GEA 作者称其为用过的设计最好的动画 API 之一)不存等间隔样本,而是把每个关节的 S、Q、T 通道表示为 n 阶非均匀非有理 B 样条:先按固定间隔采样,再在用户给定容差内拟合 B 样条。曲率大的通道也只需少量控制点,结果通常比等间隔线性插值的片段小得多(卷 II 13.8.4)。样条基础见 埃尔米特与 Kochanek-Bartels 样条。

小波压缩

把动画曲线分解为一组正交归一小波之和(小波:像波一样振荡但持续时间很短的函数),类似把信号表示为冲激串或正弦和。GEA 指向第 15 章的信号处理基础,并推荐 Eidos Montreal 在《Thief》(2014) 中实现小波压缩的文章(卷 II 13.8.5)。

选择性加载与流式加载

最便宜的片段是不在内存里的片段。多数游戏启动时加载一组核心片段常驻(玩家核心动作集、武器和道具等反复出现的物体),其余按需加载;一次性时刻的片段在需要前载入或流入,播完即卸载。很多引擎把片段打包成逻辑组整体加载和卸载(卷 II 13.8.6),加载机制见 资源管理器。

GEA 原书中的问题

  • 13.8 开头写 100 关节骨骼的未压缩片段"117 KiB per joint per second"。按前文每关节每秒 1.17 KiB 计算,100 关节应为每秒 117 KiB(不是"每关节"每秒);后面"1000 秒约 114.4 MiB"正是按每秒 117 KiB 算出的。
  1. Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
  2. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 13 章 13.8 节

相关页面

游戏开发骨骼、姿势与动画片段Skeletons, Poses & Animation Clips角色动画系统的数据核心是三样东西:骨骼(关节层级)、姿势(每个关节的 SRT 变换)和动画片段(姿势随局部时间变化的采样)。GEA 把各种动画方法看成对"逐点控制表面"这一理想的不同压缩方式;骨骼蒙皮用少量关节驱动大量顶点,是今天最主流的方案(GEA4 卷 II 13.1–13.4)。游戏数学数值表示与浮点数Numeric Representation & Floating Point游戏引擎里到处是数值,所以要清楚它们在机器中如何存储:二进制和十六进制、补码整数、定点数、IEEE-754 浮点数(量级与精度此消彼长、次正规数、机器 epsilon、ULP),以及多字节值的大小端问题和安全的类型双关 (type punning) 方法。书中一个直观的例子是:用 32 位浮点数记录以秒为单位的游戏时钟,大约 12.14 天后,加 1/30 秒就不再改变它的值。图形学顶点压缩与精度Vertex Compression & Precision网格数据像纹理一样可以压缩:传输用的整体压缩(Draco 等)一次性解码,渲染用的定长压缩让顶点着色器在 GPU 上逐顶点解码,从而降低内存、带宽并改善缓存。另一面是精度问题:大世界中单一坐标系会让远离原点的物体抖动,需要相对相机的变换或分段原点。游戏美术变形动画Morphing变形在两个模型之间插值,需要解决顶点对应和插值两个问题。顶点一一对应时可以逐顶点线性插值;更直观可控的形式是变形目标/混合形状:在中性模型上叠加加权的差分向量,常用于面部动画。过场中无法用这些方法表达的动画,可以用压缩后的几何缓存逐帧回放。游戏开发资源管理器Resource Manager游戏由网格、材质、纹理、着色器、动画、音频、关卡布局、碰撞与物理参数等大量资源(也叫资产、媒体)构成。内存有限,所以引擎要保证每个资源在内存中只有一份(五个网格共用一张纹理时只载入一份)。每个引擎都有某种资源管理器:有的是统一集中的子系统,有的散布在多年间由不同人写的各个子系统中,但职责和要解决的问题是一样的。本页讲运行时部分:职责、文件组织与归档、文件格式、资源 GUID、资源注册表、生命周期(引用计数)和资源的内存管理;交叉引用与加载后初始化见 资源交叉引用与加载后初始化,离线工具链见 资产管线与工具链。游戏数学埃尔米特与 Kochanek-Bartels 样条Hermite & Kochanek-Bartels Splines三次 Hermite 曲线用两个端点和两个端点切线定义,比贝塞尔控制点更直观;把多段 Hermite 曲线串起来经过一系列点时,Kochanek-Bartels 用张力、偏置、连续性三个参数从相邻弦计算切线,默认参数即 Catmull-Rom 样条。均匀三次 B 样条则以不经过控制点为代价换来处处 C^2 连续。游戏开发动画管线与动作状态机Animation Pipeline & Action State Machines低层动画管线把片段和混合规格变成局部姿势、全局姿势和蒙皮矩阵调色板;其上的动作状态机 (ASM) 给游戏代码提供"按状态驱动动画"的接口。每个状态是任意复杂的片段混合,跟踪当前所有片段的方式有扁平加权平均和混合树两种;过渡用数据驱动的过渡矩阵描述,控制参数通过节点搜索、命名变量或控制结构体暴露给代码(GEA4 卷 II 13.9–13.10)。游戏开发 · UE5动画蓝图与动画系统Animation Blueprint & Animation SystemUE5 角色动画以动画蓝图 (Animation Blueprint) 为核心:它每帧为骨骼网格体 (Skeletal Mesh) 计算最终姿势。动画蓝图内部用状态机 (State Machine) 组织“何时播什么”,用混合空间 (Blend Space) 按速度、方向等参数连续混合,用蒙太奇 (Animation Montage) 在插槽 (Slot) 中插播攻击、交互等一次性动作,用 Control Rig 做程序化骨骼控制(如贴地)。算出的骨骼姿势最终交给蒙皮 (Skinning) 驱动顶点。游戏开发数字音频技术Digital Audio Technology游戏音频程序员需要了解从麦克风到扬声器的整条硬件链路:模拟端的换能器、扬声器布局、电平、放大器和音量控制;数字端的脉冲编码调制 (PCM) 采样、香农-奈奎斯特采样定理、位深与量化误差、各种音频格式与编解码器、多声道数据的并行与交错布局,以及数字接口。GEA 用它们说明音频引擎的输入输出到底是什么(GEA4 卷 II 15.3)。游戏数学四元数Quaternion单位四元数可以紧凑地表示任意三维旋转,与轴-角表示互相转换很简单,并能做稳定、匀速的朝向插值(slerp),这些都是欧拉角做不好的。它可以高效地转换为矩阵以便与其他变换合并。游戏开发音频引擎架构Audio Engine Architecture音频渲染软件和多数系统一样是分层的"栈":硬件 → 驱动 → 底层 API → 3D 音频引擎,功能集通常模仿录音棚的多通道调音台。每个 2D/3D 声音是一条语音 (voice),沿语音总线经过编解码、增益、预发送滤波、辅助发送分出干湿两路、混响、后发送滤波和声像,然后由主混音器合成多声道信号,再经主输出总线(预放、LFE 生成、EQ、压缩、主音量)送到扬声器。资产上分片段、音效提示 (cue)、声音库和流式声音;混音靠分组、闪避、总线预设与混音快照、实例限制和语音抢占(GEA4 卷 II 15.5)。游戏美术技术动画Technical Animation第 8 章「Our Project: Technical Animation」覆盖角色从建模约束、蒙皮、绑定,到命名/目录、引用、导出格式与引擎规则、内存与切片、引擎内事件标签与混合时间、测试关与资产卫生。目标是让动画师少踩导出坑、系统可共享缩放。游戏美术动画抛光与调试Animation Polish & Debug第 13 章对应项目收尾:Alpha / Beta / RC / Gold 阶段动画职责,以及脚滑、pop、接触点、动量不一致、穿透等抛光清单;强调定向抛光、内存与压缩,以及一次只改一个因素的调试纪律。游戏美术游戏动画技术基础Game Animation Technical Foundations《GAME ANIM》第 5 章按基础→中级→高级梳理游戏动画师必须掌握的引擎侧概念:三类动画形态(循环 / 线性 / 过渡)、骨骼导出与数值运算、样条与碰撞移动、FK/IK,以及状态机、参数化混合、局部动画、叠加层、布娃娃,再到全身 IK、Look-At、混合形状与程序化系统。本页只收录书中实际讲到的内容,并与引擎架构页互链。

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原