Bootstrap

UE里的动画重定向(Retargeting)

参考:https://docs.unrealengine.com/4.27/en-US/AnimatingObjects/SkeletalMeshAnimation/AnimationRetargeting/
参考:https://docs.unrealengine.com/4.27/en-US/AnimatingObjects/SkeletalMeshAnimation/AnimHowTo/Retargeting/
参考:https://www.youtube.com/watch?v=FDbpHamn2eY&t=3s&ab_channel=UnrealEngine
参考:https://www.youtube.com/watch?v=vZW8mxlQJEk&ab_channel=UnrealEngine
参考:https://www.youtube.com/watch?v=Io76DagpS-8&ab_channel=RyanLaley(UE4里的操作演示)
参考:https://zhuanlan.zhihu.com/p/25064011
参考:https://www.jianshu.com/p/d67cb85e762b

此篇文章写的时间比较久了,所以涉及到不同版本的UE的Retargeting功能,这里先提出一些问题:

  • 什么是动画重定向
  • 了解Skeleton Asset
  • 如何在UE4里设置动画重定向
  • 如何在UE5里设置动画重定向
  • 动画重定向的原理
  • UE5新增的IK Retargeting
  • 动画重定向相关代码分析
  • UE4里动画重定向里,骨骼的旋转是怎么算的,官方文档的说法貌似是直接使用SourceAnimation里的数据,那么到底是咋使用的?是直接读取localRotation,再直接overwrite,还是算出相对于Reference Pose里对应骨骼的DeltaLocalRotation,再叠加到Target模型对应的Reference Pose的骨骼Rotation上?也就是说,旋转是直接写入,还是算delta叠上去的?
  • 新旧的重定向系统的算法原理差别,UE4里的好像就是直接apply rotation数据,然后根据骨骼位置不同决定要不要同步修改Bone长度,而UE5的好像完全不一样,是通过IK和FK算法实现Chain的重定向的(具体怎么做的?)

几个重要问题:

  • UE通过重定向生成的Sequence是否还与原本的Sequence有依赖,还是说他只是单纯的Copy并修改了一份动画数据,并没有减少动画量
  • UE的动态重定向在代码里是怎么实现重定向的,是不是动态重定向就可以减少动画量了?


什么是动画重定向

动画重定向是指把A模型的动画,搬到B模型上的过程。为了处理不同模型之间的动画重定向,UE4根据以下标准把动画重定向分为两种类型:

  • 模型使用相同的Skeleton Asset
  • 模型使用不同的Skeleton Asset

后者需要一个额外的Rig信息,负责处理俩模型的Skeleton之间的映射

第一种类型,两个模型的Skeleton的Hierarchy是一样的(Hierarchy一样,指的是拓扑结构和joint的名字是完全相同的,但对joint在Skeleton里的Transform是没有要求的),如下图所示,是第一种动画重定向的结果,各个模型的Skeleton Hierarchy相同:
Translation Retargeting



了解Skeleton Asset

UE4里有一种资产,叫做Skeleton Assets,而Unity里没有这个概念,Unity里类似的概念叫做Avatar。

UE4的Skeleton Assets有以下特点:

  • a list of Bone & Hierarchy information(注意,Bone的Transform它是不存的)
  • 用于驱动Animation
  • 可以被多个Skeletal Meshes共享
  • 可以帮助动画重定向
  • 可以帮助创建sockets, notifies, curve names and slot names

注意,UE里的Skeleton与常规理解的Skeleton不同,正常来说,Skeleton应该有两个东西:

  • 一个string数组和一个int数组,二者一起代表Joints组成的Hierarchy,其中int数组里记录的是每个joint的parent的id,string记录的是joint的名字
  • 一个Transform数组,每个元素对应一个joint的Transform,一般这个Transform是模型的BindPose下的Joint的Transform

Animations are bound to a Skeleton asset. The Skeleton asset is really just a list of bone names and hierarchy data, but it also stores the initial proportions from the original Skeletal Mesh used to define the Skeleton asset. This data is stored as bone translation data.

但UE的Skeleton Asset里,只存了上述第一条信息(第二条信息应该是存在了UE的Skeletal Mesh里),如下图所示,它由多个Skeletal Mesh共享,它不记录任何joint具体的Transform数据,这里面的Skeletal Mesh会记录每个Bone的长度:
在这里插入图片描述

由于不同的Skeletal Mesh可能会附加额外的joint(这些joint都是作为叶节点存在的),所以USkeleton里记录的Hierarchy,其实是一个比较大的Hierarchy,实际使用时,每个Skeletal Mesh很可能只会取里面一部分的子集,作为自己的Skeleton Hierarchy。

如下图所示,当我打开一个Skeleton资产时,其实可以选择只显示特定Skeletal Mesh的Bones:
在这里插入图片描述

顺便提一下UE里Bone这个概念:Skeleton不是按照joint分的,而是按照Bone来分的,每个Bone其实是对应的joint与其parent joint对应的这段区间,root bone由于没有parent节点,所以该bone就是一个点,如下图所示,分别是root对应的bone和pelvis对应的Bone的图片:
在这里插入图片描述
在这里插入图片描述


动画重定向可能只会使用到Skeleton Asset的部分Hierarchy

一个Skeleton Asset可能是多个模型公有的Skeleton,每个模型的Skeletal Mesh使用的Skeleton其实是共享的Skeleton Asset的子集,如果说一个Skeleton ,比正常Skeleton多两个手,多一个尾巴,如下图所示,这种情况下,只要其他的Joints的Hierarchy与Skeleton资源里的Hierarchy相同,那么也是可以使用这个Skeleton的,无非是绿色的部分需要额外的代码去驱动而已(比如supplemental animation):
在这里插入图片描述

上面的操作,不可以修改原本Skeleton的拓扑结构。在Hierarchy上,尾端进行添加这种操作,原本的Skeleton资产还是可以复用的,但是原本的结构一旦被破坏,就得使用新的Skeleton资产了:
在这里插入图片描述
比如说,我在中间关节加一个joint,也是不行的:
在这里插入图片描述



如何在UE4里设置动画重定向

对于使用相同Skeleton Asset的动画,也就是第一种动画重定向,需要对共同的Skeleton Asset进行相关设置,主要是设置每个Bone的translation data模式


相同Skeleton的Retargeting

这里只是骨架的名字都相同,但是骨骼的长度可能不同,它们共享同一个Skeleton Asset,具体步骤如下:

  1. 找到不同模型的共同的Skeleton Asset,打开其资源窗口

  2. 在Skeleton Tree窗口里,勾选Show Retargeting Options选项,打开每个Bone的重定向设置,如下图所示:
    在这里插入图片描述

  3. 窗口里,每个Bone有五种重定向的模式选择,需要手动进行设置,如下图所示(后面会具体介绍怎么选择):
    . 在这里插入图片描述

  4. 设置完毕后,可以预览一下动画重定向的效果,这里需要选择预览的Skeletal Mesh和对应的动画,UE5的预览方法如下:
    在这里插入图片描述
    为了方便预览骨骼,还可以把整个Skeleton显示出来:
    在这里插入图片描述


如何设置bone translation data
模型里每个参与动画重定向的Bone都可以设置bone translation data,有五种模式:

  • Animation:就是不改Bone的长度,直接使用Animation里的Bone长度,可以理解为不做任何Retargeting的电话,此时的重定向效果如下图所示:
    在这里插入图片描述

  • Skeleton:直接采用新模型的Bind Pose里的Bone的长度

  • AnimationScaled:可认为是第二种模式的改进,它先算出两个模型Bind Pose下相同Bone的长度的比例,再把实际播放时A的Bone的长度,乘以这个比例,得到新的模型的Bone的长度,所有想要bone长度随动画变化的,都应该使用这种模式

Animation模式一般用于以下骨骼,其实也是不受骨骼重定向影响的Bone:

  • Root骨骼,因为它要应用Root Motion,一般不受Retargeting影响
  • IK节点,一般是Root骨骼的直接子节点
  • 武器骨骼,因为武器都是一样的,不需要重定向

PS:我一开始没理解为什么要有AnimationScaled模式,因为我觉得骨骼的长度一般不是固定的吗,但其实不是的。举个例子,如下图所示,这里的Hips代表的骨骼是一个很特殊的骨骼,它的头是Root,尾是Hips,那么随着人物Idle动画的播放,可以认为这块Bone的长度是不断变化的,如果我此时的Hips选择Skeleton模式而不是AnimationScale模式,那么重定向后的模型的Hips到Root的长度是不会变化的,人物的高度也不会轻微改变
在这里插入图片描述

这三种是跟bone translation data相关的设置,但是还有额外的两种:

  • Animation Relative
  • Orient and Scale

UE文档里推荐的设置方法为:

  • 对于根节点(比如Pelvis bone),使用AnimationScaled模式,理由前面解释了
  • 对于Root bone(人物脚底的Bone,不是Hips)、所有的IK、武器或起标记挂点作用的bone,都应该使用Animation模式,具体原因不太清楚(UE里面应该是先对原始数据进行IK相关的计算,再应用到重定向的角色上),这个问题值得后续研究一下
  • 其他的bone长度不需要动态变化的bone统统使用Skeleton模式


不同Skeleton的Retargeting

参考:https://www.youtube.com/watch?v=Io76DagpS-8&ab_channel=RyanLaley

其实跟相同Skeleton的重定向差不多,无非是需要多做一个骨骼映射的设置。

举个例子,我这里有两个模型,一个是UE4自带的模型的Mannequin Skeleton、Skeletal Mesh和对应的动画资源,还有一个Mixamo上面下载的模型。现在要把UE4的Third Person Controller里的自带模型,换成我自己下载的Mixamo的模型。

首先,需要为UE4的自带模型生成Rig,打开模型,找到其对应的Skeleton界面(或者直接打开对应的Skeleton资产),打开Retarget Manager窗口,选择SelectRig -> CreateHumanoidRig,然后点击AutoMap,因为这里是UE4自带的Skeleton,它的名字应该是与UE4里的Rig是一模一样的名字,这里点击AutoMap应该不会有任何变化,如下图所示:
在这里插入图片描述

同样的,再打开Mixamo的模型,重复上面的操作,生成Humanoid Rig,点击Auto Map,如下图所示,可以看到这里的Pelvis是空的,而且明显匹配的不对,所以还需要进行手动修正:
在这里插入图片描述

这里Mixamo的骨架和UE4自带模型的骨架不太一样,Mixamo的Root Bone是在Hips位置的,而UE4自带模型的Root Bone是在脚底的,如下图所示:
在这里插入图片描述
所以这里从头部开始,就匹配错了,因此这里Mixamo的Rig匹配的全都是错的,需要手动从Root开始矫正,这里由于Mixamo的模型没有脚底这块Bone,所以它的Rig里的Root和Pelvis都应该是hips,后面的再手动进行更改。

手动匹配完以后,点击Show Advanced,继续对手指这些的细节Joint进行手动map,还挺麻烦的,最后反正是弄完了。


T-pose与A-pose
这里好像,Retargeting的时候,是作为Additive Pose用在原始的Pose上的,所以两个Pose应该是越接近越好,而这里UE4的模型的默认pose是A-pose,而Mixamo的模型是T-pose,所以这里改变UE4模型的Transform,将A-pose修改成T-pose,把两边各向上旋转50°,如下图所示:
在这里插入图片描述

生成重定向之后的动画资产
然后选中原本的AnimationSequence,比如说UE4的模型的Idle动画,右键选择Retarget Anim Assets -> Duplicate Anim Assets and Retarget,选择对应的Skeleton、选择路径之后保存,就会发现出现了一个新的AnimationSequance资源,里面的模型是Mixamo里下载的模型

由于Mixamo模型的RootBone在Hips位置,这里的人物的半身是被挡住了的。如果在Mixamo的Skeleton的Retarget Manager页面,修改原本的Root Bone,从Hips改成None,如下图所示:
在这里插入图片描述
然后把原本导出的动画文件删除,重新Duplicate Anim Assets and Retarget,即可:
在这里插入图片描述
这里甚至可以对动画蓝图进行重定向,对ThirdPerson_AnimBP右键选择,Duplicate Anim Assets and Retarget,如下图所示,这里可以顺便把蓝图使用到的资产全部创建一个新的版本:
在这里插入图片描述
然后就需要把场景里的第三人称的模型替换成Mixamo这个模型了,在World Outliner里选择Third Person Character,编辑其蓝图,替换里面的Skeletal Mesh和Anim Class即可。

我看了下,UE4里正常的动画数据,分为了三个部分,存在不同的地方:

  1. Hierarchy和joint的name,被存在了Skeleton资源里
  2. joints之间的距离,即Bone的长度,被存在了Skeletal Mesh里
  3. Joints里的Rotation数据,来自AnimationSequence


UE5新增的IK Retargeting

参考:https://docs.unrealengine.com/5.0/en-US/ik-rig-animation-retargeting-in-unreal-engine/#ikretargeter

Once you have created both IK Rigs and created the necessary Bone chains, you can now begin the retargeting process in the IK Retargeter



动画重定向的原理

无论动画重定向之间的模型是否共享同一个Skeleton Asset,都不重要,动画重定向的本质的是一样的。这里直接分析共享Skeleton Asset的动画重定向,动画一般不会考虑Transform里的Scale数据,所以动画重定向,主要是两个数据:

  • Rotation数据
  • Bone的长度数据

It is important to note that the retargeting system only retargets the bone’s translation component. The bone’s rotation always comes from the animation data.

重定向其实很简单,把源动画数据的Rotation直接Apply过来,就行了,不需要Apply骨骼的Translation数据,除了RootM,其他的骨骼不需要特殊处理




UE4里的Bone

Bone Length和Proportions
文章最开头的Skeletal Asset的图里,还有个信息,是说Skeletal Mesh里存了Bone Length(Proportions),


UE4的PreviewMesh

在UE4的Skeleton.cpp里看到这么个代码:

// 很明显可以看到, PreviewMesh就是SkeletalMesh
USkeletalMesh* USkeleton::GetPreviewMesh(bool bFindIfNotSet/*=false*/)
{
	...
}

UE4的官方文档解释:
Preview Mesh (Animation): The Skeletal Mesh to use for previewing.

那么它是不是BindPose对应的Skeletal Mesh呢?

还有个Preview Mesh Collection,就是多个PreviewMesh形成所谓的引用的Binding关系的集合。


Retarget Manager里的Pose

一般retarget时,需要要求模型是同样的Pose,作为BindPose,可以通过Skeleton Hierarchy下手动旋转关节修改Pose,然后在Retarget Manager里Modify Pose -> Use Current Pose即可,参考https://www.youtube.com/watch?v=Io76DagpS-8&ab_channel=RyanLaley第七分钟


Retarget Options

如下图所示:
在这里插入图片描述
一共有四个选项:

  • Remap referenced assets
  • Allow remapping to existing assets
  • Convert Spaces to new Skeleton
  • Show Only Compatible Skeletons

前面三个选项,都是涉及到具体生成Retarget Anim Sequence文件的选项,而最后一个是界面显示的选项,如果取消勾选应该会在左边栏位显示出所有的Skeleton Asset,对应到源码里为:

// 这里的NameRule就是在Duplicate Anim Assets and Retarget选项里设置的新文件的命名规则, 这里有三个布尔值, 分别对应Retarget界面的三个选项
// bRemapReferencedAssets: Remap referenced assets
// bAllowRemapToExisting: Allow remapping to existing assets
// bConvertSpaces: Convert Spaces to new Skeleton
void FAssetTypeActions_AnimationAsset::RetargetAnimationHandler(USkeleton* OldSkeleton, USkeleton* NewSkeleton, bool bRemapReferencedAssets, bool bAllowRemapToExisting, bool bConvertSpaces, const EditorAnimUtils::FNameDuplicationRule* NameRule, TArray<TWeakObjectPtr<UObject>> InAnimAssets)




附录

Virtual Bones

UE4里可以通过Virtual Bone来添加IK或者Anim contraint joints,通过Virtual Bone,可以改善动画里计算瞄准或者IK问题里的迭代时间,Virtual Bone最好在3D建模软件里就加好,然后导入到UE4里。

右键选择任意Skeleton里的joint,选择Add Virtual Bone之后选择另一端的joint,即可创建新的Virtual Bone,颜色都跟普通的bone不一样,如下图所示:
在这里插入图片描述

除了前面提到的bone translation data的三种模式,还有:Skeleton代表使用重定向,也就是Skeletal Mesh里的skeleton propertions的数据、Animation代表只使用动画里的值。右键点击Root bone,然后选择Recursively Set Translation Retargeting Skeleton,来统一设置所有的Bones,

,点击左上角的Scene Setup按钮,在Viewport里选择一个不同的Preview Mesh(Try out different animations on different Skeletal Meshes to ensure that your Skeleton is set up properly for each of your animations.)

看这个代码就行了,几种模式都很清楚了:

foreach (var boneMapping in rp.boneMappings)
{
    // 对于重定向的Bone, 它的长度可以选择Skeleton, Animation和AnimationScaled
    // Animation:就是不改Bone的长度,直接使用Animation里的Bone长度,上面错误的图里每个Bone就是选择的这种模式
    // Skeleton:直接采用新模型的Bind Pose里的Bone的长度
    // AnimationScaled:可认为是第二种模式的改进,它先算出两个模型Bind Pose下相同Bone的长度的比例,再把实际播放时A的Bone的长度,乘以这个比例,得到新的模型的Bone的长度
    switch (boneMapping.targetBone.boneRetargetType)
    {
        // 直接采用新模型的Bind Pose里的Bone的长度, 这种情况下, 沿用原本的localPosition即可
        case (AniRetargetBoneData.RetargetType.Skeleton):
            boneMapping.targetBoneTransform.localPosition = boneMapping.targetBone.position;
            break;
        // 这个算法是选择加上DeltaRotation和DeltaPose
        case (AniRetargetBoneData.RetargetType.AnimationRelative):
            // 由于目前的localPosition已经是动画数据了, 减去原本的BindPose的position, 加上targetBone的position即可
            boneMapping.targetBoneTransform.localPosition = boneMapping.targetBoneTransform.localPosition - boneMapping.sourceBone.position + boneMapping.targetBone.position;
            boneMapping.targetBoneTransform.localRotation = boneMapping.targetBoneTransform.localRotation * Quaternion.Inverse(boneMapping.sourceBone.rotation) * boneMapping.targetBone.rotation;
            break;
        case (AniRetargetBoneData.RetargetType.AnimationScaled):
            // 根据BindPose下俩bone的长度比例, 对target的bone进行伸缩
            boneMapping.targetBoneTransform.localPosition *= boneMapping.targetBone.length / boneMapping.sourceBone.length;
            break;
        // 这个是我自己加的, 由于已经是读的Animation的数据了, 所以啥也不用管
        case (AniRetargetBoneData.RetargetType.Animation):
            break;
    }
}


UE5静态生成动画重定向的Sequence

参考:UE5里的Animation Retargetting

UE5里设置Retargeting的方式与UE4完全不同,UE4里的Retargeting是基于bone to bone system的,就是说会有一个统一的人形骨架,然后需要制定出各个Skeleton上哪些Bone映射了人形骨架的各个Bone。而UE5的Retargeting是基于bone chain stystem来做的,比如说,你要告诉我源模型和要重定向的模型的arm chain和leg chain分别是什么,然后UE会根据Chain去做动画重定向

整体思路为:

  1. 基于源模型和被重定向的模型的Skeletal Mesh,创建对应的IK Rig资产
  2. 调整IK Rig资产里的模型Pose,让各个模型的Pose一致
  3. 每个模型的IK Rig里把各个Bone Chain设置好(有点类似于UE4里RetargetManager里设置骨骼映射)
  4. 基于上面的两个IK Rig创建IK Retargeter
  5. 基于IK Retargeter和源模型的动画(比如AnimSequence),Retarget出来新的重定向模型的动画(比如AnimSequence)

具体操作步骤如下:

  • 打开需要被Retarget的模型,为其创建IK Rig(即使用不到IK,也需要创建它),这是一种动画资产,创建时需要选择对应的Skeletal Mesh
  • 打开创建的IK Rig,点击retarget pose -> modify pose调整对应的Pose,让其与Source模型的Pose相同
  • 在右下角的IK Retargeting界面,点击Add New Chain,设置各个关节Chain,比如head chain,如下图所示:
    在这里插入图片描述
    最终的设置大概是这样:
    在这里插入图片描述
  • 接下来需要指定Retarget的Root,这里重定向的Skeleton和被重定向的Skeleton上面都应该设置,如下图所示
    在这里插入图片描述
  • 为需要对Source模型也创建一个IK Rig,重复上述操作,注意这里的IK Chain的名字需要是相同的
  • 然后创建IK Retargeter,选择为Source创建的IK Rig,然后打开资产,再右上角选择Target IK Rig资产,如下图所示:
    在这里插入图片描述
  • 在右下角的Chain Mapping窗口,保证找到与Souce模型的IK Rig里的Chain对应的Chain列表,由于这里俩IK Rig的Chain名字是相同的,直接一一对应即可:
    在这里插入图片描述
  • 此时就可以预览俩角色播放相同的动画了

貌似在Skeleton Asset里修改的Retarget base Pose在UE5.0.3的版本里,不会在IK Retargeter里生效,所以我需要修改一下Pose之后,再去播放动画即可:
在这里插入图片描述
不过这里也只是可以预览而已,接下来还需要创建Retarget得到的Sequence资产,具体做法为,选中对应的Sequence后,点击Exported Selected Animations:
在这里插入图片描述
也可以用老的方法,选中Source模型的某个动画资产,右键选择Duplicate and Retarget Animation Assets,选择对应的IK Retargeter即可:
在这里插入图片描述

这种做法,能够很快的大批量在Asset Browser里生成重定向后的Sequence资产,比以前的右键选择动画生成资产的方式要简单一些。

顺便问一句,UE4里的重定向里是规定了,不同的Skeleton的Chain的结构必须是一样的,举个例子,如果Source模型的Spine链条是spine01->spine02->spine03,那么Target模型的Spine链条不可以是spine01->spine02,不知道UE5的重定向是不是还有这种规定


UE5里的Runtime动画重定向

参考:UE5的运行时(动态)重定向治好了我的精神内耗

知乎这篇文章介绍了UE5的动态Retargeting,但介绍的不怎么详细,感觉UE5这里设置动态重定向的方式有点蹊跷,这里再记录一下

总的步骤如下,前四条跟前面的静态动画重定向一模一样:

  1. 基于源模型和被重定向的模型的Skeletal Mesh,创建对应的IK Rig资产
  2. 调整IK Rig资产里的模型Pose,让各个模型的Pose一致
  3. 每个模型的IK Rig里把各个Bone Chain设置好(有点类似于UE4里RetargetManager里设置骨骼映射)
  4. 基于上面的两个IK Rig创建IK Retargeter
  5. 打开自己源模型的Character蓝图(如果没有就创建Character蓝图,然后在里面指定Mesh即可),然后把被重定向的模型的SkeletalMesh拖拽到Character蓝图的Mesh层级下,然后把源模型的Mesh隐藏
  6. 创建俩蓝图,一个是正常的源模型的播放Sequence的动画蓝图,一个是被重定向模型的特殊的重定向的动画蓝图,然后分别指定给角色蓝图的两个Mesh里,此时播放起来就OK了

具体细节补充两点,一点是角色Mesh的层级结构,如下图所示:
在这里插入图片描述
另一点是俩角色的动画蓝图的设置,先是源模型的动画蓝图,非常简单:
在这里插入图片描述
然后是目标模型的动画蓝图,以及这里的Retarget Pose From Mesh的设置界面:
在这里插入图片描述
这里的Retarte Pose Form Mesh节点只有一个Input,就是这里的Skeletal Mesh Component,对应的是要重定向的Source模型,右上角有个Use Attached Parant选项,当不勾选它时,需要手动在动画蓝图里加入对应的Skeletal Mesh Component,勾选它就不需要再加入了,它会自动去此蓝图对应的Skeletal Mesh Component的父节点链去寻找对应的Skeletal Mesh Component。相关代码如下:

// 当外部节点没有指定SourceMeshComponent时, 初始化SourceMeshComponent数组
// 根据此节点的SkeletalMesh, 找到Parent上的SkeletalMesh, 如果没找到直接return 
// if user hasn't explicitly connected a source mesh, optionally use the parent mesh component (if there is one) 
if (!SourceMeshComponent.IsValid() && bUseAttachedParent)
{
	// 从Instance里获取USkeletalMeshComponent
	USkeletalMeshComponent* TargetMesh = InAnimInstance->GetSkelMeshComponent();

	// 根据USceneComponent逐级往上找, 直到找到skeletal mesh component
	// Walk up the attachment chain until we find a skeletal mesh component
	USkeletalMeshComponent* ParentMeshComponent = nullptr;
	for (USceneComponent* AttachParentComp = TargetMesh->GetAttachParent(); AttachParentComp != nullptr; AttachParentComp = AttachParentComp->GetAttachParent())
	{
		ParentMeshComponent = Cast<USkeletalMeshComponent>(AttachParentComp);
		if (ParentMeshComponent)
		{
			break;
		}
	}

	if (ParentMeshComponent)
	{
		SourceMeshComponent = ParentMeshComponent;
	}
}

// has a source mesh been plugged in or found?
if (!SourceMeshComponent.IsValid())
{
	return; // can't do anything if we don't have a source mesh
}

总的来说,这个思路就是,我先弄一个正常情况的角色,它有它的蓝图,然后去把它的Mesh隐藏,再在原本的Mesh下面添加一个新的重定向模型的Skeletal Mesh,然后再给这个Skeletal Mesh加一个特殊的新的动画蓝图,这个蓝图的作用就是专门负责Retargeting,如下图所示:
在这里插入图片描述

这个的RetargetPoseFromMesh输出的就是Retarget Pose,后续在蓝图里应该还可以继续连其他的动画节点



IK Retargeter的相关设置

具体有两个地方:

  • IK Retargeter资产的Details界面
  • Chain Mapping里选择特定Chain Map时的Details界面

第一个是Retargeter资产的设置参数,如下图所示,里面绝大多数参数调一调就知道是用来干啥的了,预览的模型只允许在一个方向(Actor Offset)上移动位置,只有这个Retarget Phases有点不太理解是干什么的:
在这里插入图片描述


第二个界面是点击每个ChainMapping时出现的设置,如下图所示,图里的Chain Mapping栏位没啥用,主要的是FK Adjustments和IK Adjustments下面的参数,应该是具体用于重定向时的IK和FK设置,此外还有个待定的俩参数:
在这里插入图片描述


UE5的动态Retargeting代码分析

前面提到过,这里动态Retargeting的操作是,先取一个正常情况的角色,它有它的蓝图,然后去把它的Mesh隐藏,再在原本的Mesh下面添加一个新的重定向模型的Skeletal Mesh,然后再给这个Skeletal Mesh加一个特殊的新的动画蓝图,这个蓝图的作用就是专门负责Retargeting。而这个特殊的动画蓝图,里面只有一个动画节点连向Output Pose节点,也是这里最需要研究的节点,即Retarget From Pose节点,对应的FAnimNode_RetargetPoseFromMesh类。

先看看类声明:

// UE5新添加的负责runtime进行重定向的动画节点, Editor下预览Retargeting效果其实核心也是此动画节点
// 所以说, 此类的UE5动画重定向的绝对核心
// 此类对象也存在于Editor下预览的UIKRetargetAnimInstance(: public UAnimPreviewInstance)里
USTRUCT(BlueprintInternalUseOnly)
struct IKRIG_API FAnimNode_RetargetPoseFromMesh : public FAnimNode_Base
{
	GENERATED_BODY()

	/** The Skeletal Mesh Component to retarget animation from. Assumed to be animated and tick BEFORE this anim instance.*/
	UPROPERTY(BlueprintReadWrite, transient, Category=Settings, meta=(PinShownByDefault))
	TWeakObjectPtr<USkeletalMeshComponent> SourceMeshComponent = nullptr;

	// 暴露在蓝图节点界面上的其实就只有bUseAttachedParent和IKRetargeterAsset
	/* If SourceMeshComponent is not valid, and if this is true, it will look for attached parent as a source */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Settings, meta = (NeverAsPin))
	bool bUseAttachedParent = true;
	
	/** Retarget asset to use. Must define a Source and Target IK Rig compatible with the SourceMeshComponent and current anim instance.*/
	UPROPERTY(EditAnywhere, Category = Settings)
	TObjectPtr<UIKRetargeter> IKRetargeterAsset = nullptr;

#if WITH_EDITOR
	/** when true, will copy all setting from target IK Rig asset each tick (for live preview) */
	bool bDriveTargetIKRigWithAsset = false;
#endif
	
	// FAnimNode_Base interface
	virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
	virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
	virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
	virtual void Evaluate_AnyThread(FPoseContext& Output) override;
	virtual bool HasPreUpdate() const override { return true; }
	// 这里还有个重要的PreUpdate函数
	virtual void PreUpdate(const UAnimInstance* InAnimInstance) override;
	// End of FAnimNode_Base interface

#if WITH_EDITOR
	/** Force reinitialization. */
	void SetProcessorNeedsInitialized();
#endif

	/** Read-only access to the runtime processor */
	const UIKRetargetProcessor* GetRetargetProcessor() const;

private:
	
	void EnsureInitialized(const UAnimInstance* InAnimInstance);
	void CopyBoneTransformsFromSource(USkeletalMeshComponent* TargetMeshComponent);
	
	// source mesh references, cached during init so that we can compare and see if it has changed
	TWeakObjectPtr<USkeletalMesh> CurrentlyUsedSourceMesh;
	TWeakObjectPtr<USkeletalMesh> CurrentlyUsedTargetMesh;

	/** the runtime processor used to run the retarget and generate new poses */
	UPROPERTY(Transient)
	TObjectPtr<UIKRetargetProcessor> Processor = nullptr;// 核心数据成员

	// cached transforms, copied on the game thread
	// 会在节点的初始化阶段读取SrcSkeletalMesh里的ComponentSpaceBoneTransforms数组存起来
	TArray<FTransform> SourceMeshComponentSpaceBoneTransforms;

	// 记录Source Pose与Target Pose之间的骨骼映射
	// mapping from required bones to actual bones within the target skeleton
	TArray< TPair<int32, int32> > RequiredToTargetBoneMapping;
};

重定向遇到的问题

模型的轴向不一致

有俩模型,一个是Unity里的轴,(0,0,1)代表Forward,(1,0,0)代表Right,(0,1,0)代表Up:
在这里插入图片描述
另一个是UE里的动画模型,导入到Unity后的效果:
在这里插入图片描述

看了下UE里的轴向,它是认为(0,1,0)代表Foward,(0,0,1)代表Right,(1,0,0)代表Up:
在这里插入图片描述

这种情况下,正常Retargeting算法应该是行不通的,因为它是按照Source Model的动画和轴朝向进行更改的


为什么Retarget动画节点会在PreUpdate阶段读取Src模型的Pose

难道读取的是上一帧的Src模型的Pose么,否则PreUpdate阶段不是还没算出当前帧的Pose,难道Retarget之后的Pose与源模型的Pose差了一帧?


位移在RootM上的带RootMotion的动画的重定向

如果说RootMotion数据位于RootM的parent的RootBone上,那么很好说:

  • RootBone选择Animation类型的动画重定向,此时它直接沿用原本的动画数据即可
  • RootM选择AnimationScale类型的动画重定向,此时会主要修改人物的RootM的高度

但我碰到的情况是,没有RootBone这个骨骼,只有一直在原点的Root,和承载了RootMotion信息的RootM,此时我的困难点在于:

  • 此时的RootM应该选择Animation类型还是AnimationScale类型?

选择Animation类型显然不对,因为不同人物的RootM的高度是不同的,其实不只是高度,准确的说是RootBone(角色的底部对应的Bone)->RootM这根骨骼是成比例变化的。如果选择AnimationScale类型,也不大对,因为RootMotion信息是不应该被Scale的

对于这种动画,RootM本身的轻微摇晃与RootMotion信息混到一起去了,很难区分出来,这里只能不考虑在水平方向上的RootM本身的轻微摇晃了,思路是:

  • 对于RootM,其x和z值选择Animation模式,其y轴选择AnimationScale模式,那么会记录BindPose下各自RootM相对于Root的高度差,算出比例
;