← ALL ARTICLES
动画系统5 MIN READ

动画姿态混合

全库目录221
本文目录14

从走路切到跑步,直接播放另一个动画片段会让脚步和姿态跳变。姿态混合先对齐动作相位,再在局部TRS中合成基础、加法和骨骼层,最后累积层级。动作规则与根运动所有权保持明确,才能避免脚滑和重复位移。

基础姿态生成

动画片段采样

动画片段保存多个关节或属性随时间变化的轨道。运行时根据归一化时间找到相邻关键帧,对平移/缩放做 Lerp,对旋转做 Nlerp 或 Slerp。

动画片段需要明确:

  • 采样率和是否循环;
  • 根运动轨道;
  • 事件与曲线;
  • 起止姿态是否连续;
  • 参考姿态和骨架版本。

高采样率不保证更好。源动画频率、压缩误差和最终动作速度共同决定所需采样密度。

状态机

状态机把 Idle、Walk、Run、Jump、Attack 等动作组织成状态和过渡。过渡一般依赖参数、事件、退出时间和优先级。

例如移动参数在阈值附近往复变化,Walk到Run的过渡尚未结束就被Run到Walk打断。若每次重置混合进度,角色会长期停在中间姿态。过渡需要定义打断时从当前合成姿态继续,还是回到原片段,以及速度阈值是否使用滞回区间。动画事件又反向修改同一玩法状态时,还需跟踪事件的来源片段,避免淡出片段重复触发切换。

复杂系统可把玩法状态与姿态生成分开。玩法决定意图和标签,Animation Graph 负责选择、混合和修正动作。

连续姿态组合

交叉淡入淡出

两个姿态的局部 TRS 按权重混合:

T=(1−w)TA+wTBT=(1-w)T_A+wT_B

旋转通常用 Nlerp/Slerp。对共享固定局部平移、单位缩放的骨架,先混合局部旋转再累积层级,能够保持骨长。若轨道本身改变平移或缩放,混合结果也会改变长度,需要按动画契约限制或验证。

线性权重不一定产生自然速度。Ease 曲线能让过渡的起停更柔和,但打击动作可能需要短而明确的过渡。

还要处理两段动作的相位。例如同一只脚在走路片段中相对髋部向前0.3米,在跑步片段中向后0.3米;各取一半得到0,脚停在髋下,两个输入里的迈步都被抵消。可用同步 Marker 或归一化步态相位对齐脚步事件。

相位对齐之后再计算权重。例如走路和跑步都把左脚落地定义为相位零,当前步态相位为 0.25 时,各自在对应标记区间采样,不能简单让两个片段播放相同秒数。

Blend Tree

1D Blend Tree 根据速度在 Idle、Walk、Run 间插值。2D Blend Tree 根据前后和左右速度混合方向动作。

它适合连续参数,不适合用几十个离散动作强行铺满二维空间。共享顶点值一致时,三角形内的重心插值在公共边上保持连续;跨边时权重梯度可能改变。过瘦三角形还会放大参数变化对权重的影响,因此需要检查动作速度、参数覆盖和相位。

以二维参数点A=(0,0)、B=(1,0)、C=(0,1)为例,查询点(0.2,0.3)的重心权重为(0.5,0.2,0.3),和为一,且加权参数正好回到查询点。沿AB边移动时C的权重为零,相邻三角形若共享A、B的动作与时间相位,边上的姿态就保持一致。越出三角形会产生负权重,应先按项目规则投影到覆盖边界或选择其他三角形。

二维 Blend Space 常对样本点做 Delaunay Triangulation。运行参数落入某个三角形后,只采样三个顶点动画,并用重心坐标计算权重。这样能处理不规则样本分布,也避免每帧混合全部动画片段;三角形过瘦、样本覆盖不足和参数越界仍需专门处理。

运动匹配

运动匹配把动画数据库切成可检索帧,为每帧保存姿势特征和未来轨迹特征。运行时根据当前骨骼状态、期望速度与轨迹查找代价最低的候选,再跳转并混合到该片段。

特征需要归一化和加权,否则位置、速度、朝向等不同量纲会让距离失真。检索结果还要考虑姿态连续性、脚部接触、过渡代价 和最短驻留时间,避免频繁跳片与脚滑。数据库质量、标签约束和查询可视化通常比单纯更换最近邻算法更重要。

加法动画

Additive 动画片段保存相对参考姿态的差值,而不是完整姿态。

平移差值可以相加;旋转应用相对旋转:

qout=qbase(qref−1qadd)wq_{out}=q_{base}\left(q_{ref}^{-1}q_{add}\right)^w

常见用途:呼吸、瞄准、后坐力、受击、表情和局部姿势修正。参考姿态不一致会让角色突然偏移或扭曲。

加法旋转则必须约定乘法顺序:正文公式把相对参考的旋转右乘到基础姿态,因而作用在关节局部基底;换成左乘会改变旋转轴。把基础姿态先绕另一个轴转动,再添加瞄准偏移,可以直接暴露这个差别。

分层与骨骼遮罩

层可以让下半身保持移动,上半身播放持枪或攻击。骨骼遮罩决定哪些关节接收这一层。

边界不能生硬截断。只替换手臂而不处理胸椎和肩部,会在肩膀产生断裂。遮罩通常需要从骨盆/脊柱开始渐变,并处理 IK 目标和武器挂点的一致性。

根运动

根运动从动画根轨道提取位移和旋转,驱动角色实体。它能保持脚步与位移一致,适合攻击突进、翻滚和精确表演。

玩法驱动的移动由角色控制器决定速度,动画只跟随参数。它响应直接、网络预测更容易,但可能脚滑。

项目常混合使用:常规移动由代码驱动,特殊动作消费根运动。需要明确根运动在本地、服务器和重放中的权威来源,并处理碰撞阻挡后的剩余位移。

局部姿态求值

输入是两个相同骨架的局部姿态。平移与缩放线性插值,旋转使用最短弧 Slerp:

cpp
for (int joint = 0; joint < skeleton.JointCount(); ++joint) {
    TRS a = SampleLocal(fromClip, joint, fromTime);
    TRS b = SampleLocal(toClip, joint, toTime);
    float progress = blendDuration > 0.0f
        ? Clamp01(blendElapsed / blendDuration) : 1.0f;
    float alpha = SmoothStep01(progress);
    localPose[joint].translation = Lerp(a.translation, b.translation, alpha);
    localPose[joint].rotation = SlerpShortest(a.rotation, b.rotation, alpha);
    localPose[joint].scale = Lerp(a.scale, b.scale, alpha);
}
BuildModelPose(localPose, skeleton.parents, modelPose);

blendDuration 为零时直接使用目标姿态;正时长则先把进度限制到 [0,1]。片段在计算进度时处理这条分支。混合发生在局部空间,层级累积只执行一次。两个动画片段的时间推进策略、根运动和事件归属需要由状态转换明确决定。若先转成模型空间再逐关节混合,长骨链容易出现长度和轨迹异常;可用手臂快速摆动的转换检查末端轨迹是否连续。

验证方法

显示每层权重、动画片段时间与同步标记,检查左右脚相位和武器挂点。零时长过渡直接切目标,权重范围及骨架一致性在输入端核对。层级蒙皮主机制见骨骼动画与蒙皮。

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

相关主题

参考资料