/ Ch16 计算机动画

第16章:计算机动画(Computer Animation)— 零基础讲义

讲义说明
本讲义基于 Steve Marschner & Peter Shirley 所著《虎书5》第5版第16章(p.429-458)。
本章是图形学的"运动"——它告诉你:怎么让3D物体"动起来"。
学完本章,你将真正理解 Keyframe、Forward/Inverse Kinematics、Physics-Based Animation、Particle System、Boids Flocking 等核心技术。
本版插画采用 Guizang 材质插画风格重新绘制。


学习目标

  1. 理解动画的12条原则(Disney)
  2. 掌握Keyframing和Catmull-Rom样条
  3. 理解3种旋转表示:Euler / Quaternion / Axis-Angle
  4. 掌握Forward Kinematics vs Inverse Kinematics
  5. 掌握Skinning(骨骼蒙皮)
  6. 掌握Physics-Based Animation(布料/流体/碰撞)
  7. 掌握Procedural Animation(粒子/L-system/Boids)
  8. 理解群体动画(Flocking)

16.1 动画的12条原则

16.1.1 Disney的遗产

1981年,迪士尼资深动画师 Ollie Johnston 和 Frank Thomas 出版了《The Illusion of Life》一书,总结了12条动画基本原则。这些原则来自几十年手工动画经验,至今仍是3D计算机动画的黄金准则。

#原则含义3D实现
1Squash & Stretch挤压和拉伸——物体运动时形状变化骨骼缩放/变形器
2Anticipation预备动作——起跳前先下蹲关键帧中插入反向运动
3Staging舞台化——让观众注意关键动作相机镜头+灯光引导
4Straight Ahead / Pose to Pose逐帧画 vs 关键帧+中间帧关键帧动画
5Follow Through跟随动作——头发/披风滞后物理模拟/弹簧系统
6Slow In / Slow Out缓入缓出——开始和结束慢,中间快动画曲线(缓动函数)
7Arcs弧线运动——几乎所有自然运动都是弧线Catmull-Rom/Bézier曲线
8Secondary Action次要动作——走路时手臂摆动+呼吸分层动画/混合
9Timing时机——速度决定情感和含义关键帧间距控制速度
10Exaggeration夸张——突出动作特点关键帧幅度放大
11Solid Drawing立体感——3D空间中的重量和体积3D建模+光照
12Appeal魅力——角色的吸引力角色设计+动作风格
⚡ 核心洞察:这些原则中,Squash & StretchAnticipationFollow Through 是"让动画生动"的最关键三条。没有它们,再好的3D模型也像木偶。这也是为什么游戏动画和电影动画差距最大的地方——电影精心设计了这些细节,游戏受限于实时性。

16.2 关键帧与插值

关键帧插值
图16-1:关键帧插值——时间轴上大黑点(关键帧)之间由系统自动生成中间帧

16.2.1 关键帧思想

核心定义:Keyframe = "动画师指定少数关键状态,系统自动插值生成中间帧"

工作流程

  1. 动画师在时间轴上的几个关键点设置物体的位置/旋转/缩放
  2. 系统在这些关键点之间插值,生成所有的中间帧
  3. 动画师调整插值曲线(缓入缓出)来控制速度感

优势

  • 不用画每一帧——效率提升数百倍
  • 艺术家掌控关键点的节奏
  • 修改一个关键帧不影响其他帧(局部控制)

16.2.2 动画曲线(Animation Curves)

关键帧之间用什么曲线插值?

插值方式连续度效果使用场景
线性插值C⁰生硬,速度突变机械运动
Catmull-Rom平滑,自动缓入缓出行业标准
Hermite(TCB)可独立控制tension/continuity/bias高级动画编辑
Bézier直观的切线手柄Maya/Blender默认

16.2.3 TCB控件(Kochanek-Bartels)

TCB是三个参数的缩写,它们控制曲线在每个关键点处的行为:

参数范围默认值负值效果正值效果
Tension(紧绷度)[-1, 1]0曲线松弛(像橡皮筋)曲线紧绷(接近直线)
Continuity(连续度)[-1, 1]0曲线"急转"(停顿感)曲线"弹回"(回弹感)
Bias(偏向度)[-1, 1]0曲线偏向关键点之后曲线偏向关键点之前
💡 游戏开发实战:Maya/Blender/Unity的动画曲线编辑器都基于TCB/Bézier控制。理解Tension/Continuity/Bias = 掌握了专业动画节奏调整的核心。

16.2.4 弧长参数化与速度控制

问题:参数u在曲线上走"等距不是等弧长"。u从0走到0.1可能只移动了很短距离,而从0.5走到0.6移动了很长距离。所以直接用u控制速度是不对的。

解决方案

s(u) = ∫₀ᵘ ||df/du|| du

s(u)称为弧长函数,它把参数u映射到实际走过的距离。

实际做法

  1. 预计算:在曲线上采样N个点,累加距离,建立u→s的查找表
  2. 查表:给定目标距离s,在表中查找对应的u
  3. 插值:在表项之间线性插值得到精确的u
  4. 求值:用u计算f(u)
⚡ 核心洞察:速度控制vs参数控制 = 专业动画师和业余动画的根本区别。业余动画用等参数间隔(导致速度不均匀),专业动画用"距离-时间函数"精确控制速度曲线。

16.3 插值旋转

16.3.1 旋转的挑战

3D旋转比3D位置复杂得多。位置是3D向量(直接线性插值即可),但旋转不是向量——它有特殊的结构。

四元数vs欧拉角
图16-2:四元数(右,4D球面上平滑旋转)vs欧拉角(左,万向锁问题)

16.3.2 3种旋转表示对比

表示自由度优点缺点
欧拉角(Euler)3(X,Y,Z角度)直观、易理解万向锁(Gimbal Lock)、插值不直
轴-角(Axis-Angle)4(轴向量+角度)直观、无万向锁插值困难(直接线性插值会导致旋转加速)
四元数(Quaternion)4(w,x,y,z)无万向锁、插值平滑不直观、调试困难

16.3.3 欧拉角与万向锁

万向锁(Gimbal Lock):当绕一个轴旋转90°时,另外两个轴变得"等效"——失去1个自由度。

数值例子

初始旋转 (0°, 0°, 0°) → 普通方向
旋转到 (0°, 90°, 0°) → 万向锁!
现在绕X轴和Z轴的旋转效果相同 → 你"丢了一个轴"

目标旋转 (90°, 90°, 0°) → 应该朝向某个方向
但欧拉角插值会走"弯路":先绕Y轴90°,再绕X轴90°
而正确的路径应该是直接旋转 → 插值错误

一个具体的数学问题:旋转0°和180°的插值。

(0°, 0°, 0°) 到 (0°, 0°, 180°)

直接线性插值欧拉角:中间帧 = (0°, 0°, 90°)——这是正确的。但如果绕不同轴:

(0°, 0°, 0°) 到 (180°, 0°, 0°)

中间帧 = (90°, 0°, 0°)——也正确。但是!如果先做90°Y轴旋转再绕X轴:

中间状态 (90°, 90°, 0°) → 这个方向和 (90°, 0°, 90°) 是相同旋转!
但是路径完全不同——这就是万向锁的插值错乱。

16.3.4 四元数与SLERP插值

四元数 = 4D单位球上的点

q = (w, x, y, z), ||q|| = 1

它表示绕轴(x,y,z)旋转角度2·arccos(w)。

SLERP(球面线性插值)

Slerp(q₁, q₂, t) = sin((1-t)θ)/sinθ · q₁ + sin(tθ)/sinθ · q₂

逐符号拆解

  • q₁, q₂ = 起始和结束四元数(4D单位球上的点)
  • θ = q₁和q₂之间的夹角 = arccos(q₁·q₂)
  • t = 插值参数(0→在q₁,1→在q₂)
  • sin((1-t)θ) = 球面上沿大圆路径走"剩余角度"的权重
  • sin(tθ) = 球面上沿大圆路径走"已走角度"的权重
  • ÷sinθ = 归一化因子,确保总权重和为1
用中文说就是:SLERP在4D球面上画出一条大圆弧(最短路径),旋转从q₁平滑过渡到q₂。因为是在"球面"上而不是直线上插值,所以旋转速度是均匀的,不会出现欧拉角的轴错乱。
💡 游戏开发实战:Unity / Unreal引擎默认使用四元数存储旋转,API都提供Quaternion.Slerp()。动画师在Maya/Blender中用欧拉角编辑(直观),导出时自动转成四元数(插值时用)。

16.4 变形技术

16.4.1 全局变形

变形类型效果公式
Bend(弯曲)物体沿一个轴弯曲θ = k·x, 点绕弯曲轴旋转
Twist(扭曲)物体两端相对旋转θ = k·y, 比例随高度变化
Taper(锥化)物体一端大一端小s = 1 - k·y, 点按比例缩放
Squash(挤压)压扁或拉长整体缩放(非均匀)

16.4.2 FFD(Free-Form Deformation)

核心定义:FFD = "用控制网格变形物体"——把物体包在3D网格中,移动网格点→物体跟随变形

公式(3D三角Bézier张量积):

P(s,t,u) = Σᵢ Σⱼ Σₖ b_i(s) · b_j(t) · b_k(u) · p_{i,j,k}

工作流程

  1. 建立一个3D控制网格(lattice),把要变形的物体包在里面
  2. 计算物体每个顶点在网格中的局部坐标(s,t,u)
  3. 动画师移动网格的控制点(就像操作Bézier曲线一样)
  4. 对每个顶点,用三线性Bernstein基函数计算新的位置
💡 应用:Maya/Blender的Lattice Modifier就是FFD——用于角色表情、肌肉鼓起、布料褶皱等精细变形。

16.5 角色动画:骨骼、FK、IK、Skinning

骨骼层次链FK/IK
图16-3:骨骼层次链——FK从根到末端(蓝色箭头),IK从末端到根(红色箭头),关节为黑色圆点

16.5.1 骨骼层次(Skeleton Hierarchy)

角色动画的核心:用骨骼(Bones/Joints)的层次结构驱动皮肤(Mesh Vertices)的变形。

# 人体骨骼层次结构(简化)
Root(骨盆)
├── Spine(脊柱)
│   ├── Neck(脖子)
│   │   └── Head(头)
│   └── L_Shoulder(左肩)
│       ├── L_Elbow(左肘)
│       │   └── L_Hand(左手)
└── R_Hip(右髋)
    ├── R_Knee(右膝)
    │   └── R_Ankle(右脚踝)
    │       └── R_Toe(右脚尖)

# 每个关节存储:
# - 局部变换(相对父关节的位置/旋转)
# - 世界变换(通过从根到该节点连乘计算)

16.5.2 正向运动学(Forward Kinematics, FK)

核心定义:FK = "从根出发,按关节角计算每个子关节的世界位置"
def forward_kinematics(joints):
    # 从根关节开始,深度优先遍历
    for joint in joints:
        if joint.parent is None:
            joint.world_pos = joint.local_pos      # 根关节
        else:
            # 当前关节的世界位置 = 父关节的世界位置 + 旋转后的局部偏移
            R = joint.parent.rotation              # 父关节的旋转矩阵
            joint.world_pos = joint.parent.world_pos + R * joint.local_pos
        
        # 注意:旋转会级联传递(子关节继承父关节的旋转)
        joint.world_rotation = joint.parent.world_rotation * joint.local_rotation
特性FKIK
输入所有关节角度目标末端位置
输出末端位置所有关节角度
控制方式手动调每个关节指定末端,自动算关节
适用场景全身动作(走路、跳舞)脚踩地、手拿东西
计算复杂度O(n),简单O(n³)甚至更慢,需要迭代

16.5.3 逆向运动学(Inverse Kinematics, IK)

核心定义:IK = "指定末端位置(如脚的位置),反算每个关节的角度"

数学形式

p_target - p_current = J · Δα

其中J是Jacobian矩阵:J_{ij} = ∂pᵢ/∂αⱼ

逐符号拆解

  • p_target = 目标末端位置(你想让手碰到的地方)
  • p_current = 当前末端位置
  • Δα = 关节角度的变化量(未知数,要求解的)
  • J = Jacobian矩阵——每个元素∂pᵢ/∂αⱼ表示"第j个关节转一点,末端位置第i分量变多少"

CCD算法(Cyclic Coordinate Descent)——最简单的IK求解

def ik_ccd(joints, target_pos, max_iterations=10):
    for iter in range(max_iterations):
        # 从末端关节开始,反向调整每个关节
        for i in range(len(joints)-1, -1, -1):
            joint = joints[i]
            
            # 计算从当前关节到末端的方向
            to_end = end_effector_pos - joint.world_pos
            # 计算从当前关节到目标的方向
            to_target = target_pos - joint.world_pos
            
            # 旋转当前关节使to_end朝向to_target
            rotation = get_rotation_between(to_end, to_target)
            joint.rotation *= rotation
            
            # 更新所有子关节的位置
            update_child_positions(joint)
        
        # 检查是否达到目标
        if distance(end_effector_pos, target_pos) < epsilon:
            break
⚡ 核心洞察:FK和IK的区别 = "推"和"拉"的区别。
  • FK是"推"——从根部推,所有关节连锁运动,末端自然跟着动
  • IK是"拉"——指定末端位置,系统反向计算每个关节该转多少
  • 实际游戏中两者结合使用:身体用FK,手和脚用IK

16.5.4 Skinning(蒙皮)

蒙皮Skinning
图16-4:蒙皮——骨骼(黑色线条)驱动顶点(网格)变形,关节处显示权重分布

Rigid Skinning(刚性蒙皮):每个顶点只受一个骨骼影响。

v_world = R(骨骼) · v_local

缺点:关节处会断裂,像"木偶"。

Smooth Skinning / LBS(线性混合蒙皮):每个顶点受多个骨骼影响,按权重混合。

v_world = Σᵢ wᵢ · R(骨骼ᵢ) · v_local

逐符号拆解

  • v_local = 顶点在绑定姿势(T-pose)中的位置
  • R(骨骼ᵢ) = 第i个骨骼的变换矩阵(从绑定姿势到当前姿势)
  • wᵢ = 第i个骨骼对该顶点的影响权重(∑wᵢ = 1)
  • v_world = 顶点变形后的世界坐标

权重"绘画"过程

  1. 模型在T-pose(双臂平伸)下建立骨骼
  2. 动画师"画权重":用笔刷工具在顶点上画色标(红色=完全受骨骼A影响,蓝色=完全受骨骼B影响)
  3. 权重归一化:确保每个顶点的权重之和=1
  4. 测试:弯曲关节,看皮肤变形是否自然
  5. 调整:修正"塌陷"或"糖果纸"(关节扭曲)问题,通常增加中间骨骼或调整权重
💡 游戏引擎标准:所有现代游戏引擎都用LBS(Linear Blend Skinning)——简单、快、足够好。每个顶点最多绑4个骨骼(4 weights × 4 bytes = 16 bytes / vertex,GPU友好)。

16.5.5 面部动画

3个层次的复杂性:

层次方法例子
1. 关节变形骨骼+蒙皮下巴开合、眉毛挑起
2. 肌肉模拟FACS(面部动作编码系统)真实肌肉收缩模拟
3. 表情混合Blend Shapes(混合变形)"高兴+惊讶"→"惊喜"

16.6 基于物理的动画

16.6.1 核心思想

核心定义:物理动画 = "用微分方程描述运动,计算机求解"
dx/dt = f(x, t)  (常微分方程 ODE)
∂f/∂t = F(f, x, t)  (偏微分方程 PDE)
方法方程应用
质点弹簧F = -k·x(胡克定律)布料、头发、绳索
刚体动力学F = ma, τ = Iα碰撞、堆叠、破碎
流体模拟Navier-Stokes水、烟、火
有限元弹性力学方程肌肉、软组织

16.6.2 布料模拟

质点弹簧模型:布料 = 网格状的质点 + 弹簧

  • 结构弹簧:连接相邻顶点,抵抗拉伸(刚度k₁)
  • 剪切弹簧:连接对角线顶点,抵抗剪切变形(刚度k₂)
  • 弯曲弹簧:跳过1个顶点,抵抗弯曲(刚度k₃)

约束求解(每帧迭代Gauss-Seidel):

# 每帧执行的布料求解
for iter in range(10):        # 迭代次数越多越准确
    for each constraint:      # 对每个弹簧约束
        # 如果弹簧拉伸超过原长
        delta = constraint.length - constraint.rest_length
        if delta > 0:
            # 把两个顶点往中间拉
            p1 += stiffness * delta * direction / 2
            p2 -= stiffness * delta * direction / 2
💡 实际应用:Marvel/Disney电影中的布料(《冰雪奇缘》艾莎的裙子、蜘蛛侠的紧身衣)都用物理模拟。游戏中常用简化版:PhysX、Chaos Physics。

16.6.3 数值积分方法

将微分方程离散化求解:

方法公式稳定性速度适用
显式Eulerf(t+Δt)=f(t)+Δt·df/dt差(Δt需很小)最快简单教学
隐式Euler解方程组好(可大步长)慢(解方程)布料、流体
Verletx(t+Δt)=2x(t)-x(t-Δt)+a·Δt²游戏粒子
RK44阶Runge-Kutta中等精确模拟
⚡ 核心洞察:稳定性vs速度的trade-off。显式方法简单但Δt必须小(容易爆炸)。隐式方法可大步长但要解线性方程组。游戏常用半隐式/Verlet平衡两者。

16.6.4 流体模拟

方法思想优势劣势
SPH(光滑粒子流体动力学)用粒子表示流体简单、自适应边界处理复杂
网格法(欧拉)在固定网格上求解NS方程稳定、精确内存大、细节少
PIC/FLIP(混合法)粒子+网格混合兼得两者优点实现复杂

16.7 过程化技术

16.7.1 噪声动画

核心定义:过程化 = "用数学函数生成动画,不用关键帧"

例子

  • 漂浮:position = noise(x + v·t)——物体随时间上下浮动
  • 水面:height = noise(x, y, t)——2D噪声在时间维度演变
  • 熔岩灯:color = noise(x, y, z, t)——4D噪声的颜色变化
f(x, t) = noise(x + v·t)

其中v是"速度"参数,控制动画快慢。noise函数的值在[-1, 1]之间连续变化。

16.7.2 L-System(林系统)

核心定义:L-System = "用文法生成结构"——用字符串重写规则生成复杂结构

核心要素

  • 终结符(Terminals):最终绘制指令,如F(前进)、+(左转)、-(右转)、[(入栈)、](出栈)
  • 非终结符(Non-Terminals):会被替换的符号,如A、B
  • 产生式(Productions):替换规则,如A→F[+A][-A]
  • 初始公理(Axiom):起始字符串

例子——生成分形树

Axiom: A
Rule:  A → F[+A][-A]F

迭代0: A
迭代1: F[+A][-A]F
迭代2: F[+F[+A][-A]F][-F[+A][-A]F]F
...(迭代越多树越复杂)

F = 前进画线
+ = 左转30°
- = 右转30°
[ = 保存当前位置(分支起点)
] = 恢复当前位置(分支结束)
💡 应用:L-System被广泛用于程序化生成植物(SpeedTree)、城市天际线、血管分支、神经网络可视化等。

16.7.3 元胞自动机(Cellular Automata)

Conway's Game of Life——三规则:

  1. 死细胞 + 3个活邻居 → 活(出生)
  2. 活细胞 + 2或3个活邻居 → 活(存活)
  3. 其他情况 → 死(孤独或拥挤)

应用:森林火灾模拟、雪崩模拟、城市演化、纹理生成。


16.8 群体动画

Boids群体动画
图16-5:Boids群体——三条局部规则(分离、对齐、聚合)产生复杂的全局群体行为

16.8.1 Boids(Flocking)

核心定义:Boids = "鸟群行为 = 3条局部规则"

Craig Reynolds在1987年提出的Boids模型是群体动画的奠基之作。每只鸟(Boid)只遵循3条简单规则:

规则方向公式类比
1. 分离(Separation)远离太近的邻居force_sep = -∑(neighbor_i - self) / |neighbor_i - self|²人与人之间保持"安全距离"
2. 对齐(Alignment)与邻居速度一致force_align = (∑ neighbor_velocity_i / N) - self_velocity跟周围人保持相同步调
3. 聚合(Cohesion)往群体的中心移动force_coh = (∑ neighbor_pos_i / N) - self_pos不想落单

完整Boid行为 = w₁·force_sep + w₂·force_align + w₃·force_coh + 其他规则

逐符号拆解

  • neighbor_i = 视野内的第i个邻居(距离<可见半径)
  • w₁, w₂, w₃ = 三条规则的权重(控制群体行为类型:w₁大→松散群体,w₂大→整齐队形,w₃大→密集聚集)
  • 自我速度更新:velocity += acceleration · dt,position += velocity · dt
# 每帧更新每只Boid
def update_boid(boid, flock):
    # 1. 找到视野内的邻居(距离阈值)
    neighbors = [b for b in flock if distance(b, boid) < VISION_RADIUS]
    
    if not neighbors:
        return  # 没有邻居,保持当前速度
    
    # 2. 计算分离力(远离太近的)
    separation = vec2(0, 0)
    for n in neighbors:
        diff = n.pos - boid.pos
        if length(diff) < SEPARATION_RADIUS:
            separation -= normalize(diff) / length(diff)
    
    # 3. 计算对齐力(与邻居方向一致)
    avg_velocity = average([n.vel for n in neighbors])
    alignment = avg_velocity - boid.vel
    
    # 4. 计算聚合力(往群体中心靠)
    avg_position = average([n.pos for n in neighbors])
    cohesion = avg_position - boid.pos
    
    # 5. 合并三个力(乘以权重)
    acceleration = (SEP_WEIGHT * separation +
                    ALIGN_WEIGHT * alignment +
                    COH_WEIGHT * cohesion)
    
    # 6. 更新速度和位置
    boid.vel += acceleration * dt
    # 限制最大速度
    if length(boid.vel) > MAX_SPEED:
        boid.vel = normalize(boid.vel) * MAX_SPEED
    boid.pos += boid.vel * dt
⚡ 核心洞察:Boids之美在于"简单局部规则→复杂全局行为"。每只鸟只关心附近的几个邻居,但整个鸟群表现出"集体智能"——像是有个中央指挥在协调一切。这是涌现(Emergence)的经典例子。

16.8.2 粒子系统

粒子系统 = "大量小元素的集合"

特性典型值说明
粒子数100~100000越多效果越丰富,性能越差
生命周期1~10秒粒子从出生到死亡的时间
发射率10~10000/秒每秒生成的新粒子数
初始速度随机方向给粒子随机性
受到力重力、风力、湍流模拟物理效果

应用:火、烟、雾、雨、雪、爆炸、魔法、星光、尘土。

💡 游戏引擎标准:每个游戏引擎都有粒子系统——Unity ParticleSystem(CPU粒子)、UE Niagara(GPU粒子)。Niagara可以在一帧中模拟数百万个粒子。

16.8.3 自主角色(Autonomous Characters)

Boids的推广——每个个体有更复杂的行为:

  • AI驱动:行为由有限状态机(FSM)或行为树(Behavior Tree)决定
  • 群体协调:避免重叠、保持队形、任务分配
  • 环境感知:障碍物躲避、地形适配
  • 目标导向:巡逻、追踪、攻击、逃跑

应用:RTS游戏中的士兵群、MMO中的NPC群体、电影中的群演。

16.8.4 术语对照表

英文中文一句话
Keyframe关键帧动画师指定的关键状态
TCB SplineTCB样条Tension/Continuity/Bias控制
Catmull-RomCatmull-Rom样条自动切线的插值曲线
Euler Angle欧拉角绕XYZ轴旋转的角度
Quaternion四元数4D单位球上的旋转变换
SLERP球面线性插值四元数之间的平滑旋转
Gimbal Lock万向锁一个轴旋转90°后丢轴
Forward Kinematics正向运动学从根推,末端跟着动
Inverse Kinematics逆向运动学指定末端,反算关节角
Skinning / LBS蒙皮/线性混合蒙皮骨骼驱动顶点变形
FFD自由变形控制网格变形物体
Boids / Flocking群体动画3条局部规则产生群体
L-System林系统文法生成植物结构
Particle System粒子系统大量小元素集合

16.8.5 全章总结

讲义核心洞察:动画 = "如何让物体动起来"——
  • 12条原则 = Disney 80年经验的浓缩
  • Keyframe + Catmull-Rom = 行业标准的动画制作流程
  • 四元数 = 避免万向锁,SLERP保证平滑旋转
  • FK/IK = 骨骼动画的核心(推vs拉)
  • LBS Skinning = 所有游戏引擎的蒙皮标准
  • 物理动画 = 微分方程驱动布料/流体
  • 过程化 = 噪声/L-System生成复杂动画
  • Boids = 3条简单规则→复杂群体行为

关键公式速查

名称公式
Catmull-Rom切线mᵢ = (f_{i+1} - f_{i-1}) / 2Δt
TCB控件mᵢ = ((1-t)(1-c)(1+b)/2Δt)(f_{i+1}-fᵢ) + ((1-t)(1+c)(1-b)/2Δt)(fᵢ-f_{i-1})
SLERPq(t) = sin((1-t)θ)/sinθ · q₁ + sin(tθ)/sinθ · q₂
FFDP(s,t,u) = Σᵢⱼₖ bᵢ(s) bⱼ(t) bₖ(u) p_{i,j,k}
LBS Skinningv = Σ wᵢ · Rᵢ · v_local
Jacobian IKα = α + J⁻¹(p_target - p_current)
Boidsseparation + alignment + cohesion

下一步:第17章《使用图形硬件》——学习GPU编程、Shader、DirectX/OpenGL。这是从软件到硬件的跨越。

目录 X
章节 X