引言

前置阅读:建议先阅读矩阵与变换四元数

骨骼动画是现代 3D 游戏角色的标准。它基于层级变换 (Hierarchical Transformations)。 每个骨骼(Bone)都是一个坐标系,子骨骼的变换是相对于父骨骼的。

IK vs FK

1. 正向动力学 (Forward Kinematics - FK)

这是最自然的动画方式:旋转关节,末端随之移动。 例如:旋转肩膀 -> 旋转手肘 -> 手掌移动。

数学上,这是矩阵乘法的链式反应: \[ M_{hand} = M_{shoulder} \times M_{elbow} \times M_{wrist} \]

  • 优点: 计算简单,完全确定。
  • 缺点: 很难让手精确到达空间中的某一点(例如拿杯子)。

2. 反向动力学 (Inverse Kinematics - IK)

这是 FK 的逆问题:已知末端目标位置,求各关节的角度。 例如:手要放在门把手上,肩膀和手肘应该怎么转?

这是一个非线性方程组求解问题,通常有多个解(或无解)。

常用算法:CCD (Cyclic Coordinate Descent)

CCD 是一种迭代算法,思路非常直观:从末端关节开始,逐个调整父关节,使末端指向目标。

算法步骤: 1. 遍历骨骼链,从最后一个骨骼(末端)到根骨骼。 2. 对于每个骨骼,计算”当前末端位置”到”目标位置”的向量。 3. 旋转当前骨骼,使得末端位置尽可能接近目标。 4. 重复上述过程多次,直到误差小于阈值。

代码片段 (伪代码):

void SolveCCD(Transform[] bones, Vector3 target, int iterations = 10) {
  for (int i = 0; i < iterations; i++) {
  // 从倒数第二个骨骼开始(末端本身不需要旋转)
  for (int j = bones.Length - 2; j >= 0; j--) {
  Transform bone = bones[j];
  Transform endEffector = bones[bones.Length - 1];
  
  Vector3 toEnd = endEffector.position - bone.position;
  Vector3 toTarget = target - bone.position;
  
  // 计算旋转:将 toEnd 旋转到 toTarget
  Quaternion rotation = Quaternion.FromToRotation(toEnd, toTarget);
  bone.rotation = rotation * bone.rotation;
  }
  
  if (Vector3.Distance(bones[bones.Length-1].position, target) < 0.01f)
  break;
  }
}

常用算法:FABRIK

FABRIK (Forward And Backward Reaching Inverse Kinematics) 不使用旋转角度,而是直接操作关节位置。 1. 前向: 将末端拉到目标点,然后拉直骨骼链。 2. 后向: 将根节点拉回原点,再次拉直骨骼链。 3. 迭代: 重复直到收敛。 它比 CCD 更快且更自然,避免了奇怪的扭曲。

IK 目标不可达时的处理

当目标点超出骨骼链的最大伸展长度时,IK 算法无解。实际工程中需要处理这种情况:

float maxReach = bones.Sum(b => b.length); // 骨骼链总长度
float targetDist = Vector3.Distance(bones[0].position, target);

if (targetDist > maxReach) {
    // 方案 A:将目标拉到最大可达距离
    Vector3 direction = (target - bones[0].position).normalized;
    target = bones[0].position + direction * maxReach * 0.99f;
    
    // 方案 B:完全伸直指向目标(无需迭代)
    // StretchTowards(bones, target);
}

选择哪种方案取决于游戏需求:抓取类动作通常用方案 A(手尽量伸向目标),脚部 IK 通常直接忽略不可达的目标。

3. 蒙皮 (Skinning)

骨骼动了,网格(Mesh)怎么动? 每个顶点通常受多个骨骼影响(权重 Weight)。 \[ P_{final} = \sum (w_i \times M_i \times P_{bind}) \] 这称为线性混合蒙皮 (Linear Blend Skinning - LBS)。

LBS 的局限与 DQS

线性混合蒙皮有一个经典问题:当关节弯曲角度较大时(如手肘弯曲 > 90°),网格会出现塌陷 (Candy Wrapper)膨胀现象。这是因为 LBS 对变换矩阵做线性插值,而旋转的线性插值并不正确。

双四元数蒙皮 (Dual Quaternion Skinning, DQS) 使用双四元数来插值变换,能更好地保持体积:

方法 性能 弯曲质量 实现复杂度
LBS 大角度有塌陷 简单
DQS 略慢(约 10-15%) 体积保持好 中等

现代引擎通常默认使用 LBS,但提供 DQS 选项用于需要高质量蒙皮的角色。

总结

  • FK 适合播放预制动画(走路、攻击)。
  • IK 适合环境交互(脚踩在斜坡上、手抓物体)。
  • 蒙皮 将骨骼的运动传递给模型网格。

< 上一篇: 曲线数学 | 回到目录 | 下一篇: 随机与噪声 >

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。