引言
骨骼动画是现代 3D 游戏角色的标准。它基于层级变换 (Hierarchical Transformations)。 每个骨骼(Bone)都是一个坐标系,子骨骼的变换是相对于父骨骼的。
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 更快且更自然,避免了奇怪的扭曲。
3. 蒙皮 (Skinning)
骨骼动了,网格(Mesh)怎么动? 每个顶点通常受多个骨骼影响(权重 Weight)。 \[ P_{final} = \sum (w_i \times M_i \times P_{bind}) \] 这称为线性混合蒙皮 (Linear Blend Skinning - LBS)。
总结
- FK 适合播放预制动画(走路、攻击)。
- IK 适合环境交互(脚踩在斜坡上、手抓物体)。
- 蒙皮 将骨骼的运动传递给模型网格。