0. 写在前面
本文为个人学习的笔记整理,如有错误,望不吝指出。
本篇粗略的描述UE4中每帧动画计算的主流程,该流程涉及的相关代码,因为动画设置的多样性,还有相当多的分支代码。
本篇只描述角色跑起一个最基础的动画,从SkeletalMeshComp到动画蓝图中动画播放节点的姿势计算所经历的流程。
1. SkeletalMeshComp流程
【AnimInstance】
动画实例,该类是控制动画逻辑的核心,可以理解为一个AnimInstance对象就对应着一个动画蓝图。
SkeletalMeshComp中持有三种AnimInstance对象:
AnimScriptInstance:当前激活的动画蓝图对象PostProcessAnimInstanceLinkedInstances
【入口】
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
UpdateMontageEvaluationDataBlueprintUpdateAnimation,更新蓝图各种动画逻辑的入口,具体实现就是蓝图的动画树确定是否需要立即更新动画,如果不用立即更新动画的话,会由多线程调用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) CacheBonesEvaluateAnimationNode_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数据,先初始化OutPoseEvaluateCurveData:提取曲线数据DecompressPose:解压压缩的动画数据,完成OutPose的计算
4. 参考资料
/4.27/zh-CN/AnimatingObjects/SkeletalMeshAnimation/Optimization