← ALL ARTICLES
动画系统4 MIN READ

IK

全库目录221
本文目录9

一段动画的脚落在平地上,地面变成斜坡以后就会悬空。IK从已采样姿态出发,根据末端目标修正关节链。骨长、弯曲平面、接触相位和迭代预算决定修正是否稳定。

FK 与 IK

正向运动学(FK)从父到子给定关节旋转,结果稳定且符合动画制作习惯,但末端位置只能间接控制。

逆向运动学(IK)给定末端目标,反求链上关节。它适合脚贴地、手握武器、看向目标和攀爬接触。

IK 通常是在已采样动画上做最后修正,不是替代全部动画。

末端目标约束

双骨骼 IK

手臂和腿可近似为两段固定长度。给定根节点、目标和弯曲提示方向,可解析求出中间关节的弯曲平面和两个角度。

两段链的根、肘和末端构成三角形。三角形任一边不能长于另外两边之和,因此目标距离必须限制在:

∣l1−l2∣≤d≤l1+l2|l_1-l_2| \le d \le l_1+l_2

超出范围时需要限制范围或允许伸长。弯曲提示方向决定膝盖/肘部朝向;接近完全伸直时平面不稳定,应使用上一帧方向或动画提示方向防止翻转。

CCD 与 FABRIK

CCD 从末端向根部迭代旋转每个关节,使末端逐步靠近目标。实现简单,但长链可能收敛慢,姿势也容易卷曲。

FABRIK 在位置空间做向末端和向根部的交替遍历:先从末端向根部拉直,再固定根节点从根向末端恢复骨长。它收敛通常较快,也容易加入长度约束,但仍需把最终位置还原为关节旋转。

迭代 IK 必须设置最大迭代次数和误差阈值,避免不可达目标消耗无限时间。

CCD的一次二维更新可直接检查:关节J=(0,0),当前末端E=(1,0),目标T=(0,1)。从J出发的两个方向夹角为90°,旋转该关节及其后代后,末端到达目标。三维用叉积给旋转轴、atan2给角度;两向量平行时无需旋转,反向时旋转轴不唯一,需要参考弯曲面。每次更新后重算后代位置,下一关节使用更新后的末端,最后限制关节角。跳过这步重算会让后续纠正基于旧姿态。

FABRIK则先固定末端到目标,沿链反向把每个前驱放到“离已固定后继一个骨长”的位置;再固定根,正向以相同距离规则恢复每个后继。例如两段各1米,根(0,0)、中点(1,0)、末端(2,0),目标(1,1)。反向固定末端(1,1),中点距它已有1米所以保持(1,0),根也保持(0,0);这一输入一轮就满足目标。一般输入需要反复往返,距离为零时方向退化,采用上一有效方向。

位置求解完成还需恢复骨旋转:把参考骨轴旋到新线段方向,再保留或限制绕长轴的扭转。位置链满足骨长不等于旋转满足关节极限;限制旋转后末端可能再次偏离目标,必须继续迭代或报告剩余误差。

足部 IK

一个可用的足部 IK 不只是射线检测后移动脚:

  1. 从动画得到原始脚和骨盆姿态;
  2. 向地面查询命中点与法线;
  3. 计算脚底偏移和朝向;
  4. 调整骨盆,避免腿被过度拉长;
  5. 对位置、旋转和权重做时间平滑;
  6. 仅在脚处于支撑相位时锁定,抬脚时释放。

移动平台需要把锁定点保存在平台局部空间。楼梯边缘和突然失去地面时要有回退策略。

两段链的角度

算法伪代码输入正骨长 l1、l2,并要求长度容差0 < epsilon < min(l1,l2);方向容差directionTolerance无量纲且位于(0,1)。referenceAxis 是归一化的参考长轴,previousBend是上一有效单位弯曲方向。先处理重合目标,再把距离限制到可达域内部,避免伸直与完全折叠处的退化。

text
delta = target - root
rawDistance = length(delta)
axis = delta / rawDistance if rawDistance > epsilon else referenceAxis
d = clamp(rawDistance, abs(l1-l2)+epsilon, l1+l2-epsilon)
cosRoot = clamp((l1*l1+d*d-l2*l2)/(2*l1*d), -1, 1)
hint = normalize(poleHint) if length(poleHint) > epsilon else previousBend
bend = hint - axis * dot(hint, axis)
if length(bend) < directionTolerance:
    bend = previousBend - axis * dot(previousBend, axis)
if length(bend) < directionTolerance:
    fallback = (1,0,0) if abs(axis.x) <= abs(axis.y) else (0,1,0)
    bend = fallback - axis * dot(fallback, axis)
bend = normalize(bend)
elbow = root + l1 * (cosRoot * axis + sqrt(max(0, 1-cosRoot*cosRoot)) * bend)
end = root + d * axis

例如两段各1米,根为原点、目标为(1,0,0)、提示向量为(0,1,0)。余弦定理给出cosRoot=0.5,肘为(0.5,sqrt(3)/2,0);它到根和目标的距离均为1米。目标改到(3,0,0)后原距离超过链长,代码只到约2米,剩余约1米必须由接触释放、根移动或允许伸长的规则处理。增加迭代次数无法让固定骨长覆盖不可达距离。

这里的 poleHint 是根节点指向提示点的向量。投影减法先去掉沿目标轴的分量,剩下的方向决定肘部朝哪一侧弯曲;代码直接给出中间关节位置。根与目标重合时目标轴本身没有定义,先保留上一有效轴或参考姿态轴,再进入计算。上一帧弯曲方向也要重新投影到当前垂直平面,接近零时按参考姿态选取备用轴。最后由每根骨的参考长轴到新骨方向求旋转,长轴上的扭转继承动画或单独限制。

验证方法

目标在可达域内、域外与完全伸直附近分别测试。记录末端误差、骨长误差、迭代数与平面翻转,移动平台锁点保存到平台局部空间。

实现状态:正文片段用于解释本页数据流与边界条件;完整宿主的编译、运行及性能测量尚未执行。可运行的独立模型以本页列出的具体测试为准。

相关主题

参考资料

  • Buss:逆运动学方法:作者页面的求解方法与不可达目标说明;本文两段链由余弦定理直接推导。