UE自带重定向原理

2023-05-16

UE自带重定向方法验证

核心源码在VS的解决方案中的位置:

  • UE4\Source\Developer\AssetTools\Private\AssetTypeActions\AnimSequence.cpp中第3237行RemapTracksToNewSkeleton函数

跳转方法

  • AssetTypeActions_AnimationAsset.cppRetargetNonSkeletonAnimationHandler函数调用了RetargetAnimationHandler
  • 跳转到EditorAnimUtils.cppRetargetAnimations函数
  • 跳转到AnimationAsset.cppReplaceSkeleton函数
  • 最后跳转到AnimSequence.cppRemapTracksToNewSkeleton函数

核心源码理论

  • 获取全局(Component Space)齐次矩阵的方法
    源码第3277行,有一个FillUpTransformBasedOnRig函数,各种跳转以后,可以找到AnimationRuntime.cpp中的FillUpComponentSpaceTransforms函数,其中一行代码:

    ComponentSpaceTransforms[Index] = BoneSpaceTransforms[Index] * ComponentSpaceTransforms[ParentIndex];
    

    普通但是特别,因为以前写代码时候的正常操作是
    当 前 关 节 的 全 局 旋 转 = 父 关 节 全 局 旋 转 ∗ 子 关 节 局 部 旋 转 当前关节的全局旋转 = 父关节全局旋转*子关节局部旋转 =
    但是UE是反过来的,为:
    当 前 关 节 的 全 局 旋 转 = 子 关 节 局 部 旋 转 ∗ 父 关 节 全 局 旋 转 当前关节的全局旋转 = 子关节局部旋转 * 父关节全局旋转 =
    这一点要注意,写代码时候区分好

  • 新旧骨骼依据世界坐标系迁移位移
    源码第3311行计算了比率:

    float OldTranslationSize = OldTranslation.Size();
    float NewTranslationSize = NewTranslation.Size();
    
    OldToNewTranslationRatio[NodeIndex] = (FMath::IsNearlyZero(OldTranslationSize)) ? 1.f/*do not touch new translation size*/ : NewTranslationSize / OldTranslationSize;
    

    在第3372行把比率应用到新的动画轨迹上:

    AnimatedLocalKey.ScaleTranslation(OldToNewTranslationRatio[NodeIndex]);
    
  • 新旧骨骼依据refPose(Tpose/Apose)计算旋转数据的迁移矩阵

    源码第3271行的注释内容:

    first calculate component space ref pose to get the relative transform betweentwo ref poses. It is very important update ref pose before getting here
    

    源码第3299行的注释和3414行的实现内容:

    // theta (RelativeToNewTransform) = (P1*R1)^(-1) * P2*R2 where theta => P1*R1*theta = P2*R2
    RelativeToNewSpaceBases[NodeIndex] = NewSpaceBases[NodeIndex].GetRelativeTransform(OldSpaceBases[NodeIndex]); 
    

    结合UE的重定向操作,可以大概推测出新旧骨骼都有一个refpose(一般为Tpose或者Apose),然后由于建模流程中绑骨操作可能导致每个模型的局部坐标系不一样,所以需要依据refPose计算旧骨骼到新骨骼的迁移矩阵。

  • 动画数据迁移
    源码第3413的注释:

    now convert to the new space and save to local spaces
    

    就是将旧的动画数据迁移到新的动画数据的全局空间(component space)中,然后再转化为局部旋转

    所以在第3414行代码:

    ConvertedSpaceAnimations[SrcTrackIndex][Key] = RelativeToNewSpaceBases[NodeIndex] * ComponentSpaceAnimations[SrcTrackIndex][Key];
    

    将迁移矩阵施加到旧的骨骼全局矩阵上,就得到了新骨骼的全局矩阵。

    最后计算局部旋转即可:

    • 对于非根关节:

      ConvertedSpaceAnimations[RotParentTrackIndex][Key].GetRotation().Inverse() * ConvertedSpaceAnimations[SrcTrackIndex][Key].GetRotation()
      
    • 对于根关节

      ConvertedSpaceAnimations[SrcTrackIndex][Key].GetRotation()
      

数值验证

先预备几个函数:

  • UE4的欧拉角到旋转矩阵的转换:

    # UE的欧拉角转旋转矩阵
    def euler_to_rotMat(yaw, pitch, roll):
        yaw = np.deg2rad(yaw)
        pitch = np.deg2rad(pitch)
        roll = np.deg2rad(roll)
    
        Rz_yaw = np.array([
            [np.cos(yaw), -np.sin(yaw), 0],
            [np.sin(yaw),  np.cos(yaw), 0],
            [          0,            0, 1]])
        Ry_pitch = np.array([
            [ np.cos(pitch), 0, np.sin(pitch)],
            [             0, 1,             0],
            [-np.sin(pitch), 0, np.cos(pitch)]])
        Rx_roll = np.array([
            [1,            0,             0],
            [0, np.cos(roll), -np.sin(roll)],
            [0, np.sin(roll),  np.cos(roll)]])
        rotMat = np.dot(Rz_yaw, np.dot(Ry_pitch, Rx_roll))
        return rotMat
    
  • 两个向量的夹角

    def anglebetween(v1,v2):
        v1 = v1/np.linalg.norm(v1)
        v2 = v2/np.linalg.norm(v2)
        dot_product = np.dot(v1, v2)
        angle = np.arccos(dot_product)
        return np.rad2deg(angle)
    

假设我们有两个关节,分别称为父关节和子关节:

  • 源模型的Tpose下,子关节在父关节下的局部坐标和父关节全局旋转量为:

    oriOffset = np.array([-0.000000,35.000000,-0.000000]) #子关节局部坐标
    oriSkelT = euler_to_rotMat(0.000000, 0.000000, -89.996216)#父关节全局旋转
    
  • 目标模型的Tpose下,子关节在父关节下的局部坐标和父关节全局旋转量为:

    tarOffset = np.array([45.206524,-0.000000,-0.000002]) #子关节局部坐标
    tarSkelT = euler_to_rotMat(-90.887756, 89.786018, 89.115906)#父关节全局旋转
    
  • 由于两个模型的Tpose可能有细微差距,所以先看看两个Tpose在世界坐标系下的夹角:

    print(anglebetween(np.dot(oriSkelT, oriOffset), np.dot(tarSkelT, tarOffset)))
    #输出:0.21776551527114438
    

接下来看骨骼动画的重定向部分,重定向过程就不截图了,按照官网说的,先全部递归选择skeleton,然后把rootpelvis关节调整成Animation Scaled

然后我们先提取出某一帧的源模型和目标模型的关节数值:

  • 源模型的父关节全局旋转量为:

    oriSkelAnim = euler_to_rotMat(-63.208164, 9.146088, -74.355972)
    
  • 目标模型的父关节全局旋转量为:

    tarSkelAnim = euler_to_rotMat(56.725945, 72.118958, -58.820042)
    
  • 看看他俩在同一个世界坐标系下的角度差:

    print(anglebetween(np.dot(oriSkelAnim, oriOffset), np.dot(tarSkelAnim, tarOffset)))
    #0.21275403661014525
    

    发现重定向完毕以后,和重定向之前的角度差距不大

旋转验证

看看如何通过Tpose数据(oriSkelTtarSkelT)和源模型的某帧数据(oriSkelAnim)计算得到新的模型帧数据(tarSkelAnim),经过源码分析发现,UE的计算方法如下:
t a r S k e l A n i m = o r i S k e l A n i m ∗ o r i s k e l T − 1 ∗ t a r s k e l T tarSkelAnim = oriSkelAnim*oriskelT^{-1}*tarskelT tarSkelAnim=oriSkelAnimoriskelT1tarskelT
所以可以利用一个缓存矩阵把 o r i s k e l T − 1 ∗ t a r s k e l T oriskelT^{-1}*tarskelT oriskelT1tarskelT存下来,这样就不用每帧都算这一项了。

式子知道了,直接验证:

tmpMat = np.dot(oriSkelAnim,np.matmul(np.linalg.inv(oriSkelT),tarSkelT))

打印出我们算的,和UE里面提取的看看:

print(tmpMat)
print(tarSkelAnim)
'''
[[ 0.16835118 -0.87957756 -0.44497325]
 [ 0.25672688 -0.39671296  0.8813116 ]
 [-0.95170856 -0.26260644  0.15902411]]
[[ 0.1684567  -0.87956606 -0.44495605]
 [ 0.25670405 -0.39668436  0.88133112]
 [-0.95169605 -0.26268815  0.15896403]]
'''

基本一模一样,说明算法没错。

位移验证

通过源码分析,其实就是:
目 标 模 型 动 画 帧 全 局 坐 标 = 目 标 模 型 T p o s e 全 局 位 移 长 度 源 模 型 T p o s e 全 局 位 移 长 度 ∗ 源 模 型 动 画 帧 全 局 坐 标 目标模型动画帧全局坐标 = \frac{目标模型Tpose全局位移长度}{源模型Tpose全局位移长度}*源模型动画帧全局坐标 =TposeTpose
提取了3帧数据的全局位移量:

# Tpose下源模型和目标模型(world positon)
tran1 = np.array([0.000000,-0.005569,83.999969])
tran2 = np.array([0.002693,0.000016,106.468102])

# 第10帧源模型和目标模型(world positon)
tran3 = np.array([-162.444809,-66.448837,83.561867])
tran4 = np.array([(-205.898224,-84.200836,105.923523)]) 

# 第1000帧源模型和目标模型(world positon)
tran5 = np.array([-135.590073,-85.603470,81.906609])
tran6 = np.array([-171.862610,-108.480934,103.825958]) 

打印看看根关节位移量的长度比率:

print(np.linalg.norm(tran1)/np.linalg.norm(tran2))
print(np.linalg.norm(tran3)/np.linalg.norm(tran4))
print(np.linalg.norm(tran5)/np.linalg.norm(tran6))
'''
0.7889684100664619
0.7889692163816936
0.7889695268366821
'''

几乎一毛一样,那再看看, x , y , z x,y,z x,y,z分别的比率

print(tran3/tran4)
print(tran5/tran6)
```
[[0.78895682 0.78917075 0.78888867]]
[0.78894457 0.78911074 0.78888373]
```

可以发现坐标各自的比率和长度比率其实是一致的,可能由于计算精度有少量偏差。

后记

本文主要针对UE4的动画数据重定向原理做了计算方法的探索,当然后续还会继续对源码进行深究,包括IK、实时重定向之类的。

完整的python实现放在微信公众号的简介中描述的github中,有兴趣可以去找找。同时文章也同步到微信公众号中,有疑问或者兴趣欢迎公众号私信。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UE自带重定向原理 的相关文章

  • WOC广域网加速

    WOC广域网加速 1 广域网存在的问题 xff1a 大量的路由 网关 跨区域和跨运营商的情况比较常见 丢包和延长使用传输效率降低 交互过多导致传输效率差 2 解读方案 xff1a 应用流量可视化 链路优化 数据优化 流量管理 智能报表 3
  • ros发布gps定位信息

    1 可行的主题 Robot Pose EKF节点订阅下面的主题 xff1a odom 2D消息 lt nav msgs Odometry gt imu data 3D消息 lt sensor msgs Imu gt vo 3D消息 lt n
  • tx2备份与恢复

    NVIDIA Jetson是通过Micro USB USB TYPE C接口升级系统 xff08 具体参考不同载板说明 xff09 xff0c 更新前需让Jetson进入Recovery 模式 Recovery 模式下可以进行文件系统更新包
  • debian 163各版本源

    debian8 stable deb http mirrors 163 com debian jessie main non free contrib deb http mirrors 163 com debian jessie updat
  • svn st 状态标识

    svn status 简写 xff1a stat st 显示工作副本中目录与文件的状态 用法 status PATH 未指定参数时 xff0c 只显示本地修改的条目 没有网络访问 常用可选项 xff1a q 只显示本地修改条目的摘要信息 u
  • 单片机蜂鸣器控制程序和驱动电路

    蜂鸣器从结构区分分为压电式蜂鸣器和电磁式蜂鸣器 压电式为压电陶瓷片发音 xff0c 电流比较小一些 xff0c 电磁式蜂鸣器为线圈通电震动发音 xff0c 体积比较小 按照驱动方式分为有源蜂鸣器和无源蜂鸣器 这里的有源和无源不是指电源 xf
  • Linux 下的信号量

    linux下的posix有名信号量的几个要点 博客园 最全面的linux信号量解析 csdn blog Semaphore信号量总结 博客园 sem timedwait csdn blog 一 信号量的概念 信号量的使用主要是用来保护共享资
  • x86-从实模式到保护模式

    十进制到二进制的转换 十进制转换为十六进制 十六进制表示法 在很多高级语言中 xff0c 通常不采用在后面加h的做法来表示十六进制 xff0c 而是为他添加一个0x前缀 如 xff1a 0x3f
  • MG-SOFT 导入MIB文件

    之前SNMP测试一直用的是IReasoning MIB browser xff0c 最近测试snmp V3 xff0c 需要使用MG SOFT MIB Browser 在导入之前使用的MIB文件时 xff0c 发现跟IReasoning M
  • Ubuntu更新源 Failed to fetch http://xxxxxxx Temporary failure resolving ‘mirrors.aliyun.com‘

    之前公司产品一直使用32位环境 xff0c 最近需要使用64位环境 需要更新软件库 xff0c 就换了个64位ubuntu虚拟机 xff0c 重新编译所需的库 xff08 包括pcap grpc等 xff09 更新库的时候 xff0c 想着
  • 交换机学习----网桥

    最近接触到交换机产品 xff0c 正好借机会学习下二层相关网络技术 之前一直想写点东西 xff0c 又懒得写 这次乘机会多写点 xff0c 记录下 xff0c 也算是对学习的一个交代 另自己是初学者 xff0c 也希望能和前辈多交流 有写的
  • Ubuntu配置tftp服务端

    这两天安装ONIE和sonic系统 xff0c 基于ONIE安装sonic系统时 需要使用到tftp服务 xff0c 正好电脑里面有ubuntu的虚拟机 xff0c 就直接在虚拟机上安装了tftp服务器 顺便记录下 一 安装 tftp 1
  • Ubuntu Samba配置简介

    1 Samba 三种特殊的 NAME global xff1a 其属性选项是全局可见的 xff0c 但是在需要的时候 xff0c 可以在其他 lt section gt 中定义某些属性来覆盖 global 的对应选项定义 homes xff
  • ESP8266下载

    下载引脚介绍 VCC span class token operator lt 61 span span class token operator 61 61 span span class token operator 61 61 spa
  • libssl-dev : 依赖: libssl1.0.0 (= 1.0.2g-1ubuntu4.13) 但是 1.0.2n-1ubuntu5.1 正要被安装

    ubuntu下apt get install安装软件 xff0c 报 无法修正错误 xff0c 因为您要求某些软件包保持现状 xff0c 就是它们破坏了软件包间的依赖关系 比如以下报错 一般出现这种情况的原因时 要装A xff0c 依赖B
  • 高通平台音频调试常见问题点归纳

    以下是关于高通音频调试中遇到的一些常见问题点归纳 xff0c 仅供参考 xff0c 如有错误 xff0c 请指正 xff01 1 Audio EC VOIP 软件主要需要设置EC REF echo reference 信号 xff0c 在A
  • 蚂蚁金服二轮面试(P7岗)经验分享

    特意注册了个新号 xff0c 发表下记录自己此次蚂蚁金服的面试情况 xff0c 为了感谢大家面试经历的分享 xff0c 也是对自己面试的总结和复盘 上周三面试 xff0c 截止到现在一周过去啦 xff0c 还没有消息 xff0c 面试过程也
  • 堪比当年的LSTM,Transformer引燃机器学习圈:它是万能的

    视学算法报道 转载自公众号 xff1a 机器之心 作者 xff1a 魔王 谷歌研究科学家 David Ha xff1a Transformer 是新的 LSTM 2017 年 6 月谷歌发布论文 Attention is All You N
  • linux 睡眠函数——sleep(),usleep()

    http blog csdn net gpengtao article details 7887293 include lt unistd h gt unsigned int sleep unsigned int seconds 睡眠秒 返

随机推荐

  • 软件工程复试——九、面向对象方法学引论

    九 面向对象方法学引论 面向对象方法学的出发点和原则是尽可能模拟人类思维方式 xff0c 使开发软件的方法与过程尽可能接近人类认识世界解决问题的方法与过程 xff0c 使描述空间的问题域与求解域在结构上保持一致 面向对象方法的四个要点 xf
  • FreeRTOS+TCP模块移植

    上一版本移植并没有写的很详细 xff0c 只是将改好的代码贴上去 xff0c 今天更新一版 xff0c 附带资源 上一版本用的是FreeRTOS V10 0 1 这一版采用了最新的FreeRTOS V10 3 1 在正确移植FreeRTOS
  • PID控制器讲解

    这个视频教程讲的非常好 xff0c 从理论层面到应用 xff0c 强烈推荐有兴趣的同学看一下 https www bilibili com video BV1B54y1V7hp
  • Python学习笔记丨while、for、if循环结构基础知识与易错点

    Python流程控制 本篇笔记的主要内容是 xff1a 条件控制和循环控制 xff0c 包括if语句 while语句 for语句等 Python条件控制 span class hljs keyword style color c678dd
  • R语言安装R包的方法,mac、windows、linux安装R包常见问题与解决方法

    R语言如何快速安装R包 xff1f 如果把R比作是沃土的话 xff0c 那么R包就是鲜花 xff0c 开源共享的开发者社区提供了很多功能丰富的R包 xff0c 方便使用者充分利用R语言完成工作 但是 xff0c 有时候在安装R包是会遇到各种
  • kube-ovn代码系列(四)pod 安全组功能

    kube ovn代码系列 xff08 四 xff09 pod 安全组功能 链接 https www gogo dev com index php 2022 02 19 kube ovn securitygroup 内容 kube ovn在1
  • Ubuntu20.04下运行VINS系列:VINS-Mono、VINS-Fusion和GVINS

    文章目录 一 安装VINS Mono1 1 适配Ceres2 1 01 2 适配OpenCV41 3 编译运行 二 安装VINS Fusion2 1 适配Ceres2 1 0和OpenCV42 2 编译运行2 2 1 EuRoC数据集2 2
  • 最小花费

    题目描述 在n个人中 xff0c 某些人的银行账号之间可以互相转账 这些人之间转账的手续费各不相同 给定这些人之间转账时需要从转账金额里扣除百分之几的手续费 xff0c 请问A最少需要多少钱使得转账后B收到100元 输入格式 第一行输入两个
  • 传感器融合sensor fusion

    自动控制系统中的传感器融合 传感器融合的4个作用 xff1a 1 增加数据质量 比如减少噪声 xff1b 2 增加可靠性 多传感器互为备份 xff1b 3 估计预测状态 xff1b 4 可增加被测范围 相对于单个传感器来说 xff0c 多传
  • 摄像机成像原理(模型)与标定

    一般摄像机简化为小孔成像的理想模型 xff08 线性模型 xff09 xff0c 因为摄像机镜头 xff08 视场角 xff09 很小 xff0c 相当于被拍摄物体通过小孔投影到感光元件CCD CMOS上 对于加了各种镜头的摄像机 xff0
  • 实习周记2

    在组长准备给我布置小任务的时候 xff0c 公司开了一个新的项目并且缺前端 xff0c 我就被分配到新项目中去 xff0c 这个项目使用 angular 43 bootstrap前端框架 这不是一个初次开发的项目 xff0c 而是一个需要修
  • OBJ可视化——UV还原(修正)

    前言 前面写过一篇obj格式解析的博客 xff0c 但是这篇文章中可视化的工作是参考PRNet的源码进行的 xff0c 后来细细思考了一下 xff0c 有点问题 xff0c 具体看下面 问题来源 在PRNet源码的render py中有个函
  • Unity中BVH骨骼动画驱动的可视化理论与实现

    前言 找了很久使用BVH到unity中驱动骨骼动画的代码 xff0c 但是都不是特别好用 xff0c 自己以前写过 xff0c 原理很简单 xff0c 这里记录一下 理论 初始姿态 在BVH或者其它骨骼动画中 xff0c 一般涉及到三种姿势
  • 卡通驱动项目ThreeDPoseTracker——模型驱动解析

    前言 之前解析过ThreeDPoseTracker这个项目中的深度学习模型 xff0c 公众号有兄弟私信一些问题 xff0c 我刚好对这个项目实现有兴趣 xff0c 就分析一波源码 xff0c 顺便把问题解答一下 这个源码其实包括很多内容
  • 卡通驱动项目ThreeDPoseTracker——关键点平滑方案解析

    前言 之前对ThreeDPoseTracker的深度学习模型和unity中的驱动方法进行过解析 xff0c 还有一个比较重要的就是从深度学习模型出来的3D关键点数据会有抖动 xff0c 在ThreeDPoseTracker源码中有做两次平滑
  • 卡通角色表情驱动系列一

    前言 分析完ThreeDPoseTracker来做卡通角色的身体驱动 xff0c 接下来在卡通驱动领域还有一个是表情驱动 对这个真的是一窍不通啊 xff0c 只能慢慢看论文了 国际惯例 xff0c 参考博客 论文 xff1a Landmar
  • opencv相机标定和人头姿态估计案例

    前言 头部驱动除了之前关注的表情驱动外 xff0c 还有眼球驱动和头部方向驱动 本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向 国际惯例 xff0c 参考博客 xff1a opencv Camera Ca
  • 卡通角色表情驱动系列二

    前言 之前介绍了使用传统算法求解BS系数的表情驱动方法 xff0c 其中提到过的三种方法之一是基于网格形变迁移做的 xff0c 那么这篇文章就是对 Deformation Transfer for Triangle Meshes 做表情驱动
  • HDU 1085 Holding Bin-Laden Captive!(母函数)

    HDU 1085 Holding Bin Laden Captive xff08 母函数 xff09 题目地址 题意 xff1a 给你cnt1个一元硬币 xff0c cnt2个两元硬币 xff0c cnt3个五元硬币 xff0c 问不能凑出
  • UE自带重定向原理

    UE自带重定向方法验证 核心源码在VS的解决方案中的位置 xff1a UE4 Source Developer AssetTools Private AssetTypeActions AnimSequence cpp中第3237行Remap