Runtime重定向

前言

虽然UE4动画蓝图系统不支持跨骨架的动画使用, 但是实际上UE4自己写了一套Runtime的动画重定向机制, 当然这个跟离线重定向是不同的概念, 理论上讲, Runtime的重定向只满足骨架相同, 但是参考姿势不同的动画表现

实时重定向

FSkeletonRemapping

USkeleton即我们的骨架资源里保存着这样一个数据, 他保存了本骨架与资源里所有其他骨架的比对信息, 见下图

image-20211117102615321

image-20211117102632491

骨架资源在编辑器启动加载资源完以后会创建此数组, 在编辑器有骨架更新以后也会刷新此数组

考虑线程安全, UE直接使用一个全局数组来保存所有加载的骨架资源

在骨架删除以后自己移除自己, 然后广播所有其他骨架来删除对应的Mapping数据

image-20211117102748263

当然, UE的规则是通过名字查找匹配骨架, 与层级无关系, 也不能通过离线重定向的Rig文件来查找

image-20211117102832520

除了匹配骨骼, 后面还保存了一个类似map格式的数据表, 用于比对source和target骨骼对应的旋转关系, 计算方式非常复杂, 可以看下图参考或者Skeleton.cpp中262行

image-20211117102849113

重点就是计算得到这个Q0和Q1, 用下图的方式计算

image-20211115114804975

在重定向的时候会用到这个表格设置新的cs变换信息

image-20211115113718863

另外, Scale是不做处理的

这个算法完全是推导出来的, 在debug的时候很不直观, 所以导致修改或者测试成本比较高, 目前得出的结论是这一套计算只会正对参考姿势不同的骨架的重定向, 不能完成差别非常大的骨架的重定向, 似乎在runtime中要完成离线的重定向(开销很大)也是不太合理的事情

DecompressPose

1
2
void DecompressPose(FCompactPose& OutPose, const FCompressedAnimSequence& CompressedData, const FAnimExtractContext& ExtractionContext, USkeleton* SourceSkeleton, float SequenceLength, EAnimInterpolationType Interpolation, bool bIsBakedAdditive, const TArray<FTransform>& RetargetTransforms, FName SourceName, const FRootMotionReset& RootMotionReset)
{}

动画蓝图里面随意播放一个动画序列, 从调用栈可以看到, 动画Node提取Pose数据最终是来自DecompressPose()库函数, 字面意思就是解压缩Pose, 我们重点也是要来看一下这个函数的内容

image-20211117102426140

下面我们简述一下解压缩的流程

  1. 首先会从动画资源的骨架与作用目标骨架获取 SkeletonRemapping, 这是每个骨架都会保存的一个匹配字典, 可以看↑SkeletonRemappings , UE自己会去查找骨骼是否匹配, 匹配就能使用其他骨骼的动画, 当然如果是同一套骨架, 就直接跳过这部分匹配逻辑

image-20211112100429353

  1. 从全局单例获取5个数据结构对象,并重置, 干啥用?? 看下面

image-20211112100521687

  1. 遍历所有Track(从压缩数据获取的骨骼数), 并且如果第一根骨骼是root骨骼(基本都是),那么从序号1开始遍历. 目的是把所有Compact序号作为key, 把所有Track或者Bone序号最为value添加到第2步的各种键值对中; 用于后续Runtime重定向需要用

image-20211112110356261

  1. 单独解压缩Root骨骼, 同时如果跨骨骼, 那么还会根据Mapping信息校正root骨骼的旋转和位移, 重定向根骨骼

  2. 对旋转缩放类型(基本上是全部)的骨骼进行解压缩

image-20211117133519560

  1. 根据上面用到的键值对, 对各种类型的骨骼进行重定向, 这之前的pose信息都是原始的(新骨架对应的原始动画数据)

  2. 输出pose

思考

通过修改FSkeletonRemapping类, 因为毕竟这个类的创建是一次性的, 我们就在创建或者刷新的时候扩展通过Rig来查找骨架匹配信息, 那么理论上可以做到真正的跨骨架的使用同一套动画

image-20211117103304663

image-20211117103432419

上图中的做法比较简单粗暴, 在动画配置里加一个bool和数组, 符合要求的骨架会去Rig查找骨架匹配信息


离线重定向是从模型获取的元数据, 通过新旧骨架的比对计算, 最终的目的是把修改后的BoneTrack信息写入新动画资源(保存在Model类中), 而实时重定向是提取或者说是解压缩了动画资源的动画数据来进一步的校正, 实际上两者的数据来源是不一样的, 或者是前后关系

动画资源Node比如SequencePlayer节点, 他获取的基础Pose信息就是参考姿势的局部空间数据, 然后再结合解压缩出来的动画数据进行处理的;

至于不同骨架解压出来的数据是怎么处理的?

UE解压缩动画数据使用的是ispc接口, 这里具体方式就不在讨论范围了, 有兴趣的可以去看 AnimEncoding_PerTrackCompression.ispc的实现