UE4源码阅读_骨骼模型与动画系统_动画流程

0. 写在前面

本文为个人学习的笔记整理,如有错误,望不吝指出。

本篇粗略的描述UE4中每帧动画计算的主流程,该流程涉及的相关代码,因为动画设置的多样性,还有相当多的分支代码。

本篇只描述角色跑起一个最基础的动画,从SkeletalMeshComp到动画蓝图中动画播放节点的姿势计算所经历的流程。

1. SkeletalMeshComp流程

【AnimInstance】

动画实例,该类是控制动画逻辑的核心,可以理解为一个AnimInstance对象就对应着一个动画蓝图。
在这里插入图片描述
SkeletalMeshComp中持有三种AnimInstance对象:

  • AnimScriptInstance:当前激活的动画蓝图对象
  • PostProcessAnimInstance
  • LinkedInstances

【入口】

1)SkinnedMeshComp->TickComponent

SkinnedMeshComp中的TickComponent是会被每帧调用的接口,该接口也是动画更新的入口
其中两个重要的函数:

  • TickPose:处理动画计算中非并行的部分
  • RefreshBoneTransforms:处理动画计算中多线程并行的部分,且是由AnimInstanceProxy处理

注:UE4动画蓝图默认设置为多线程更新,动画计算中大部分的数据计算都由AnimInstanceProxy负责在多线程中并行处理。

2)SkeletalMeshComp->TickPose:

  • 计算tick的时间间隔 DeltaTimeForTick
  • 调用TickAnimation(DeltaTimeForTick, bNeedsValidRootMotion)更新三种动画实例
    • 调用AnimInstance的UpdateAnimation,这里处理动画在游戏线程计算的部分
    • 更新顺序为:先更新LinkedInstances,再更新AnimScriptInstance,最后更新PostProcessAnimInstance

3)SkeletalMeshComp->RefreshBoneTransforms接口:

  • DispatchParallelEvaluationTasks,负责发起多线程计算:
    • SwapEvaluationContextBuffers:
      计算前交换缓冲区中的上下文数据,把SkeletalMeshComp中缓存的一些动画数据交换到AnimEvaluationContext。
      AnimEvaluationContext(FAnimationEvaluationContext):该变量在SkeletalMeshComp中负责缓存在计算该角色当前动画姿势时的全部数据。
    • 发起两个Task:
      • FParallelAnimationEvaluationTask:该Task调用ParallelAnimationEvaluation
        • PerformAnimationProcessing:该函数主要负责调动画实例的Update和Evaluate接口,这两个接口会逐节点更新动画数据,然后更新动画姿势。最后应用动画姿势数据。
        • ParallelDuplicateAndInterpolate:
      • FParallelAnimationCompletionTask:该Task调用CompleteParallelAnimationEvaluation
        • SwapEvaluationContextBuffers:将AnimEvaluationContext的数据交换回SkeletalMeshComp中缓存,等渲染线程调用
        • PostAnimEvaluation(AnimEvaluationContext):

2. AnimInstance&&Proxy流程

【游戏线程】

前面说到SkeletalMeshComp会调AnimInstance的UpdateAnimation接口处理该动画蓝图在游戏线程计算的逻辑。

  • UpdateAnimation
    • 在动画节点逻辑更新前先更新Montage
      UpdateMontage(DeltaSeconds)
      UpdateMontageSyncGroup
      UpdateMontageEvaluationData
    • BlueprintUpdateAnimation,更新蓝图各种动画逻辑的入口,具体实现就是蓝图的动画树
    • 确定是否需要立即更新动画,如果不用立即更新动画的话,会由多线程调用Proxy进行并行计算
      const bool bWantsImmediateUpdate = bNeedsValidRootMotion || NeedsImmediateUpdate(DeltaSeconds);
      在这里插入图片描述
      如果需要立即更新,如RootMotion无法并行计算,则在该函数下调用更新接口
      ParallelUpdateAnimation();
      PostUpdateAnimation();

【多线程】

SkeletalMeshComp的PerformAnimationProcessing负责调用AnimInstance的Update和Evaluate,进行蓝图数据更新,和姿势数据更新。
在这里插入图片描述
几个关键的函数:

  • 关于Update:
    AnimInstance->ParallelUpdateAnimation
  • 关于Evaluate:
    EvaluateAnimation
    EvaluatePostProcessMeshInstance
    FinalizePoseEvaluationResult
    FillComponentSpaceTransforms

1)ParallelUpdateAnimation

  • 调用堆栈,由其他线程调用
    在这里插入图片描述

  • 该函数调的是Proxy的UpdateAnimation接口,直接看Proxy:

    	FAnimationUpdateSharedContext SharedContext;
    	FAnimationUpdateContext Context(this, CurrentDeltaSeconds, &SharedContext);
    	UpdateAnimation_WithRoot(Context, RootNode, NAME_AnimGraph);
    

    Context:创建一个上下文对象
    UpdateAnimation_WithRoot:从动画蓝图的根节点(最后一个节点,也就是动画输出姿势节点)往前回溯,遍历所有的动画节点对每个节点进行Update。

2)Proxy->UpdateAnimation_WithRoot
主要做的几件事:

  • CacheBones

  • Proxy->UpdateAnimationNode(InContext)

    • 通过调用根节点的Update_AnyThread接口,从根节点开始,并不断往前回溯,更新蓝图数据
    • FAnimNode_Root->Update_AnyThread(Context)
      通过连接线找到自己的上一个节点N,然后再调用N节点的Update_AnyThread
      不停的往前回溯,直到到达尽头节点,不再有连接线。
      FPoseLink:连接线对象。
      FPoseLink->LinkedNode:连接线指向的节点

    根节点:在这里插入图片描述

具体的更新逻辑需深入到每个不同的动画蓝图节点逻辑,暂略。

3)SkeletalMeshComp->EvaluateAnimation

  • AnimInstance->ParallelEvaluateAnimation
    调用堆栈:
    在这里插入图片描述
    创建一个临时的EvaluationContext上下文对象
    计算结束后,将Curve和Pose结果赋值到Comp的AnimEvaluationContext
    	FPoseContext EvaluationContext(&Proxy);
    	EvaluationContext.ResetToRefPose();
    	Proxy.EvaluateAnimation(EvaluationContext);
    	OutCurve.CopyFrom(EvaluationContext.Curve);
    	OutPose.CopyBonesFrom(EvaluationContext.Pose);
    
  • Proxy.EvaluateAnimation(EvaluationContext)
  • Proxy.EvaluateAnimation_WithRoot(Output, RootNode)
    • CacheBones
    • EvaluateAnimationNode_WithRoot(Output, InRootNode)
      • 和Update类似的流程,也是从根节点开始,通过连接线回溯,直到节点尽头。
      • 不过具体节点内部的Evaluate逻辑应该决定了和Update调用到的节点数不一样

4)SkeletalMeshComp->EvaluatePostProcessMeshInstance

EvaluatePostProcessMeshInstance也是调用的EvaluateAnimation,一样的更新流程,只是对象不一样:PostProcessAnimInstance

5)SkeletalMeshComp->FinalizePoseEvaluationResult

  • 计算TArray& OutBoneSpaceTransforms
    将压缩骨骼索引(CompactBoneIndex)转为网格骨骼索引(MeshBoneIndex)
    注:三种骨骼索引:CompactBoneIndex、MeshBoneIndex、SkeletalBoneIndex
  • 输出FVector& OutRootBoneTranslation:RootBone相对位移

6)SkeletalMeshComp->FillComponentSpaceTransforms

获取平移、旋转、缩放向量,以更新每个骨骼的姿势矩阵
每个骨骼矩阵会从根骨骼开始逐个应用父骨的局部矩阵,即该函数输出的是一个全局姿势

  • OutComponentSpaceTransforms :AnimEvaluationContext.CachedComponentSpaceTransforms,最终输出的全局姿势矩阵
    InBoneSpaceTransforms:由Evaluate计算的输入的关节姿势矩阵
  • 计算全局姿势
    FTransform::Multiply(SpaceBase, LocalTransformsData + BoneIndex, ParentSpaceBase);
    其中SpaceBase是OutTrans,LocalTransformData+BoneIndex是根据index索引的InTrans
    Multiply(FTransform* OutTransform, const FTransform* A, const FTransform* B);

3. AnimNode流程

动画播放节点:FAnimNode_SequencePlayer

其中动画播放的具体逻辑在UAnimSequence对象

1)UAnimSequence->GetBonePose (FCompactPose& OutPose, FBlendedCurve& OutCurve, const FAnimExtractContext& ExtractionContext, bool bForceUseRawData=false) const;

  • 初始化OutPose
    根据设置,使用Retargeting Source的T-Pose,或者是自身的T-Pose数据,先初始化OutPose
  • EvaluateCurveData:提取曲线数据
  • DecompressPose:解压压缩的动画数据,完成OutPose的计算

4. 参考资料

https://docs.unrealengine.com/4.27/zh-CN/AnimatingObjects/SkeletalMeshAnimation/Optimization

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值