UE4 骨骼动画姿势提取过程源码浅析

2

主题

7

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2023-3-10 13:37:11 | 显示全部楼层
前言:本文主要介绍UE4骨骼动画Pose的获取大致过程,文章不一定全部正确,如有错误欢迎指正。本文分析的源码对应引擎版本为UE4.26。
1.问题引入

我们知道骨骼动画是通过Pose姿势来表现的,那这个姿势究竟是什么?又是怎么计算得到的呢?


2.Pose姿势介绍

其实所谓的Pose就是一个Transform数组,存储着每个骨骼当帧的位置、旋转、缩放信息,有了Pose数据和蒙皮信息就能渲染出当帧模型的最终表现。
那这个Transform数组存在哪儿,每帧又是怎么更新的呢,答案是在USkeletalMeshComponent中:


BoneSpace:
如USkeletalMeshComponent类图如所示,BoneSpaceTransforms存储的是每个骨骼的本地Transform信息,也就是相对于父骨骼的相对坐标,在预览界面其实就是如下的值:


ComponentSpace:
ComponentSpaceTransformsArray存储的是每个骨骼的组件Transform信息,也就是游戏里相对于SkeletalMeshComponent的Transform,也可以理解为以 root所在的坐标系为原点,每个骨骼在这个坐标系下的Transform。(注意不是相对于root,举个例子root坐标为(0,0,20),pelvis骨骼相对root为(0,0,50),那么pelvis的Component坐标就为(0,0,70)),在预览界面其实就是如下的值:


另外ComponentSpaceTransformsArray数组是两份数据,一份只读一份可写,USkinnedMeshComponent提供了几个函数用于获取只读的、可写的ComponentSpaceTransforms:


3.姿势提取过程

接下来我们来看下这些骨骼数据的提取过程。
如上面类图所示,在USkeletalMeshComponent上还有一个AnimEvaluationContext成员


这个结构也有一份ComponentSpaceTransforms和BoneSpaceTransforms,在进入Evaluate提取姿势之前,会先执行一次USkeletalMeshComponent::SwapEvaluationContextBuffers:


那这里交换两份数据的意义是什么呢,笔者的理解是:动画更新存在多线程执行,两份buffer骨骼数据的设计是为了避免线程冲突等问题,在动画线程上就访问AnimEvaluationContext成员来更新骨骼姿势,在主线程就访问USkeletalMeshComponent上的Transform数据。
在骨骼姿势提取到AnimEvaluationContext里面后,会再次调用SwapEvaluationContextBuffers函数来把最新骨骼tranfrom信息又交换回USkeletalMeshComponent上的tranform数据里。
即对于多线程动画更新:
// 进入动画线程前交互数据
USkeletalMeshComponent::DispatchParallelEvaluationTasks
    USkeletalMeshComponent::SwapEvaluationContextBuffers
   
// 在动画线程更新AnimEvaluationContext数据
USkeletalMeshComponent::ParallelAnimationEvaluation
   
// 完成动画更新回到主线程再次交互数据
USkeletalMeshComponent::CompleteParallelAnimationEvaluation
    USkeletalMeshComponent::SwapEvaluationContextBuffers  
对于主线程动画更新:
USkeletalMeshComponent::DoParallelEvaluationTasks_OnGameThread
    // 交互数据
    USkeletalMeshComponent::SwapEvaluationContextBuffers
   
    // 更新AnimEvaluationContext数据
    USkeletalMeshComponent::ParallelAnimationEvaluation
   
    // 再次交互数据
    USkeletalMeshComponent::SwapEvaluationContextBuffers
3.1 提取BoneSpaceTransform

在执行第一次交换后,在ParallelAnimationEvaluation函数就会更新AnimEvaluationContext数据:
USkeletalMeshComponent::ParallelAnimationEvaluation
    USkeletalMeshComponent::PerformAnimationProcessing:


如代码所示,首先创建FCompactPose临时对象EvaluatedPose,通过EvaluateAnimation函数将骨骼信息提到此对象中:


也就是获取名为Bones的FTransform数组,这个数组存的是每个骨骼的BoneSpace数据。
以一个动画序列的RawData(原始数据)提取过程为例,首先动画序列上有如下数据:


RawAnimationData数组的每个元素代表每个骨骼信息,具体信息包括起始帧到末尾帧的位置、旋转、缩放等;
TrackToSkeletonMapTable为RawAnimationData的元素下标所对应的BoneTreeIndex。
以UE自带的第三人称Walk动画资源为例,以上两个数组成员的元素下标均是按骨骼从上往下排列(仅供参考,不代表所有情况):


提取动画序列的上的姿势过程如下:
USkeletalMeshComponent::EvaluateAnimation
    ...
        FAnimNode_SequencePlayer::Evaluate_AnyThread
            ...
                BuildPoseFromRawDataInternal:


遍历RawAnimationData数组元素,根据TrackIndex下标通过TrackToSkeletonMapTable映射得到SkeletonBoneIndex,再通过FBoneContainer的SkeletonToCompactPose映射得到PoseBoneIndex:




FCompactPoseBoneIndex PoseBoneIndex正是我们需要获取的FCompactPose对象中Bones数组的元素下标,接着就对骨骼Bones数组元素进行更新来提取我们最终的Pose:




将姿势提取到FCompactPose EvaluatedPose对象后,USkeletalMeshComponent会调用FinalizePoseEvaluationResult函数将姿势应用到AnimEvaluationContext的BoneSpaceTransforms数组中:


会先通过FBoneContainer的BoneIndicesArray将FCompactPoseBoneIndex转换成FMeshPoseBoneIndex:


然后再通过MeshPoseIndex下标根据FCompactPose中的骨骼位置等信息对BoneSpaceTransforms数组进行填充。
3.2 计算ComponentSpaceTransform

前文提到,BoneSpace是每个骨骼相对父骨骼的,ComponentSpace是每个骨骼相对root所在坐标系的,所以有了BoneSpaceTransforms就可以计算得到ComponentSpaceTransforms数据。这一步是在FillComponentSpaceTransforms函数中处理:
USkeletalMeshComponent::FillComponentSpaceTransforms:
root骨骼排在数组第一个元素,因为没有父骨骼,所以ComponentSpace和BoneSpace都是一致的,所以直接赋值:


然后按骨骼链依次计算,当前骨骼的ComponentSpace信息就等于当前骨骼的BoneSpaceTransform相对于父骨骼ComponentSpaceTransform后的结果(因为按骨骼链计算能保证父骨骼先算,如有了root就能计算出pelvis,有了pelvis就能计算出spine_01):


至此,骨骼数组信息即姿势就提取到了AnimEvaluationContext中,最后就是执行上面提到的SwapEvaluationContextBuffers操作来把数据交换给USkeletalMeshComponent上的tranform数据里:



本文旨在交流学习,同时方便自己在实际开发中随时查阅笔记。感谢阅览,如有疏漏错误之处还望批评指正!
回复

举报 使用道具

1

主题

3

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2023-3-10 13:37:49 | 显示全部楼层
刚点进来就爆炸了
回复

举报 使用道具

2

主题

8

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2023-3-10 13:38:27 | 显示全部楼层
坤味太重,律师函警告
回复

举报 使用道具

您需要登录后才可以回帖 登录 | 立即注册
快速回复 返回顶部 返回列表