上次我们说到在 IKinema 中添加自定义模型。方法很简单,就是利用运行时重定向,这次为大家介绍一下具体做法。

Unity 中的重定向(retargeting)大家应该不陌生,重定向使得 Humanoid 模型间可以共享骨骼动画,大大降低了动画的制作成本。但是 Unity 系统的重定向是基于文件的,对于我们使用 Orion 来说,我们需要在 IKinemaMale 模型获得动作信息的同时,将这些信息应用到我们自定义的模型上,这就需要我们在运行时进行重定向。

首先我们需要搞清楚的是,在重定向时发生了什么。Unity 系统中如何实现重定向我们无从得知,而从一段 UE4 的官方视频中,我们可以得到蛛丝马迹:重定向时,(UE4)复制了各关节的旋转角度,想来 Unity 应该也类似。这其实不难理解,因为人的骨骼关节不能位移只能旋转,所以可以忽略位移信息。同时,即使两个人的骨骼比例相差很大,如果他们的每个关节都进行了同样角度的旋转的话,那他们的身体姿势应该也是相同的。所以我们需要做的,就是在运行时,将 IKinemaMale 的各个关节的旋转角度复制到我们的模型上,我们的模型就应该和 IKinameMale 做出同样的动作。这样我们就可以在动捕中用到我们的自定义模型了。

知道了如何重定向,我们来看看在 Unity 中的实现。Unity 内部使用四元数进行旋转运算。根据上面的叙述,我们知道涉及两种运算。一种是四元数相减,即我们需要在运行时将源模型某个关节的所处角度减去该关节的初始角度,得到它的旋转量。另一种当然是四元数相加,我们需要将旋转量,加到目标模型的对应关节上。在四元数中,计算 A 与 B 的差的运算为 A * Inverse(B),而计算 A 与 B 的和则为 A * B。

实际代码如下。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class RuntimeRetargeting : MonoBehaviour

{

public Transform srcModel;

Animator srcAnimator;

Animator selfAnimator;

List srcJoints = new List();

List selfJoints = new List();

Quaternion srcInitRotation = new Quaternion();

Quaternion selfInitRotation = new Quaternion();

List srcJointsInitRotation = new List();

List selfJointsInitRotation = new List();

Transform srcRoot;

Transform selfRoot;

Vector3 srcInitPosition = new Vector3();

Vector3 selfInitPosition = new Vector3();

static HumanBodyBones[] bonesToUse = new[]{

HumanBodyBones.Neck,

HumanBodyBones.Head,

HumanBodyBones.Hips,

HumanBodyBones.Spine,

HumanBodyBones.Chest,

HumanBodyBones.UpperChest,

HumanBodyBones.LeftShoulder,

HumanBodyBones.LeftUpperArm,

HumanBodyBones.LeftLowerArm,

HumanBodyBones.LeftHand,

HumanBodyBones.RightShoulder,

HumanBodyBones.RightUpperArm,

HumanBodyBones.RightLowerArm,

HumanBodyBones.RightHand,

HumanBodyBones.LeftUpperLeg,

HumanBodyBones.LeftLowerLeg,

HumanBodyBones.LeftFoot,

HumanBodyBones.LeftToes,

HumanBodyBones.RightUpperLeg,

HumanBodyBones.RightLowerLeg,

HumanBodyBones.RightFoot,

HumanBodyBones.RightToes,

};

void Start()

{

srcAnimator = srcModel.GetComponent();

selfAnimator = gameObject.GetComponent();

InitBones();

SetJointsInitRotation();

SetInitPosition();

}

void LateUpdate()

{

SetJointsRotation();

SetPosition();

}

private void InitBones()

{

for (int i = 0; i < bonesToUse.Length; i++)

{

srcJoints.Add(srcAnimator.GetBoneTransform(bonesToUse[i]));

selfJoints.Add(selfAnimator.GetBoneTransform(bonesToUse[i]));

}

}

private void SetJointsInitRotation()

{

srcInitRotation = srcModel.transform.rotation;

selfInitRotation = gameObject.transform.rotation;

for (int i = 0; i < bonesToUse.Length; i++)

{

srcJointsInitRotation.Add(srcJoints[i].rotation * Quaternion.Inverse(srcInitRotation));

selfJointsInitRotation.Add(selfJoints[i].rotation * Quaternion.Inverse(selfInitRotation));

}

}

private void SetJointsRotation()

{

for (int i = 0; i < bonesToUse.Length; i++)

{

selfJoints[i].rotation = selfInitRotation;

selfJoints[i].rotation *= (srcJoints[i].rotation * Quaternion.Inverse(srcJointsInitRotation[i]));

selfJoints[i].rotation *= selfJointsInitRotation[i];

}

}

private void SetInitPosition()

{

srcRoot = srcAnimator.GetBoneTransform(HumanBodyBones.Hips);

selfRoot = selfAnimator.GetBoneTransform(HumanBodyBones.Hips);

srcInitPosition = srcRoot.localPosition;

selfInitPosition = selfRoot.localPosition;

}

private void SetPosition()

{

selfRoot.localPosition = (srcRoot.localPosition - srcInitPosition) + selfInitPosition;

}

}

代码主体就是在 Start() 中记录下各关节初始角度,然后在 LateUpdate() 复制旋转角度。

为了测试这段代码,我们可以在商店中下载 RCP - Caucaisan Character Models。我们将用到其中的 CaucasianMale 模型。之所以选这个模型,是因为它是 AssetStore 中少数几个免费的、binding pose 为 TPose 的模型。如果你想使用非 TPose 的模型,则需要在在取得初始角度 SetJointsInitRotation() 之前进行一次校正,这里就略过了。

将 CaucasianMale 加入场景,添加我们的组件,srcModel 置为场景中已有的 IKinemaMale。

启动 Orion,就能看到两个模型共舞了。

需要指出的是,上述代码主要演示骨骼旋转角度的复制,在实际使用中,我们还需要考虑模型脚部着地,是否为 root motion 等情况,这里就不展开了。

以上就是在 Unity 中进行运行时重定向的方法,至少两周前我还是这样想的,直到发现了 HumanPoseHandler。

根据文档,HumanPoseHandler 是A handler that lets you read or write a HumanPose from or to a humanoid avatar skeleton hierarchy.

简直就是为重定向而生的。于是我们的代码,由,

变为

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class RetargetingHPH : MonoBehaviour

{

public GameObject src;

HumanPoseHandler m_srcPoseHandler;

HumanPoseHandler m_destPoseHandler;

void Start()

{

m_srcPoseHandler = new HumanPoseHandler(src.GetComponent().avatar, src.transform);

m_destPoseHandler = new HumanPoseHandler(GetComponent().avatar, transform);

}

void LateUpdate()

{

HumanPose m_humanPose = new HumanPose();

m_srcPoseHandler.GetHumanPose(ref m_humanPose);

m_destPoseHandler.SetHumanPose(ref m_humanPose);

}

}

代码量减少很多,而且重要的是我们可以不用关心四元数的取逆与交换律了。当然,用四元数处理的好处在于我们可以在 UE4 中如法炮制,因为据笔者了解现在 UE4 中还没有类似 HumanPoseHandler 的功能。

总的来说,运行时重定向有两个好处,其一,是在 IKinema Orion 这种只能对预设模型进行动捕的系统中,也能实时用上我们自己的模型。其二,即便不用 Orion,而是使用 FinalIK 之类的动捕方案,对于身体比例与常人相差较大的模型,如果直接在其上套用 IK,可能很难得到令人满意的结果,这时,就可以选用正常的人型模型进行 IK,同时将结果重定向到目标模型上去。比如下面的动画所示。https://www.zhihu.com/video/1147614851324076032

在这里我们使用的是 FinalIK 进行全身动捕。图左的皮卡丘直接进行 IK,中间的皮卡丘,则是通过右侧人型模型进行 IK 之后,重定向之后的结果。可以看到中间的皮卡丘姿势更加自然,穿模现象也少。

最后附上代码方便取用。

unity重定向_Unity 骨骼动画的运行时重定向相关推荐

  1. Unity中BVH骨骼动画驱动的可视化理论与实现

    前言 找了很久使用BVH到unity中驱动骨骼动画的代码,但是都不是特别好用,自己以前写过,原理很简单,这里记录一下. 理论 初始姿态 在BVH或者其它骨骼动画中,一般涉及到三种姿势:A-pose,T ...

  2. unity怎么显示骨骼_Unity骨骼动画的总结

    欢迎参与讨论,转载请注明出处. 前言 恰逢假期,在家继续推进Demo,骨骼动画相关的调研算是告一段落了,遂以本文记录相关要点. 首先要明确一点,本文所说的骨骼动画皆是3D模型的骨骼动画,与2D精灵的骨 ...

  3. Unity 2D教程 | 骨骼动画:创建动画

    转载自:2016-02-13 Unity官方平台 本教程主要讲解Unity引擎自带的2D骨骼动画工具,以及2D动画的基本概念.本篇会添加一些动画,如默认状态.跳动.坠落等. 基础动画理论 制作动画要牢 ...

  4. Unity之人物骨骼动画

    一.导入 a.先把主模型以下面这种形式导入:在Project面板下右键空白地方->选择import new assert->选择主模型导入 b.把其他非主模型的资源包括动画等直接复制然后黏 ...

  5. 【Steam VR 2.X】unity Skeleton Poser 骨骼姿势 编辑姿势时 找不到手的预设

    看到错误如下图 hand preview not found. Verify SteamVRSettings.previewHandLeft and previewHandRight are set ...

  6. unity怎么显示骨骼_骨骼动画的原理及在Unity中的使用

    制作骨骼动画 我们看看这几步操作后,我们得到了那些数据: 1.每个皮肤顶点的初始世界坐标. 2.每个骨骼关节顶点的初始世界坐标. 3.每个顶点被骨骼顶点的影响信息. 4.骨骼如何移动. 骨骼动画原理 ...

  7. Unity载入骨骼动画详解

    Spine Skeleton Animation(2D骨骼动画) 骨骼动画 首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时 ...

  8. [嘭嘭养成记]1. 在运行时获取unity中人物的动作曲线

    最近有一个新的idea!很激动!激动到给这个idea起了个名字!叫做-- 嘭嘭! 以后带这个标题的博客,都代表着嘭嘭在成长哟 (๑•̀ㅂ•́)و✧! 问题产生的原因: 希望能够在游戏运行过程中,修改人 ...

  9. Unity3D骨骼动画的分解(CleanData.Ani详解)

    CleanData是什么 CleanData以前没有特定的名字,(在easydown这个开源项目中,作为一个GameObjParser模块存在). 在某三国项目中,我们使用GameObjParser将 ...

最新文章

  1. 使用ffmpeg推流到Wowza
  2. 【OpenGL】三、Visual Studio 2019 配置 GitHub ( 将项目上传到 GitHub )
  3. linux查看当前shell的方法
  4. 前端基础1:HTML常用标签
  5. java中获取文件总行数_关于java:如何以有效的方式获取文件中的行数?
  6. 牛客练习赛26B 烟花 (概率DP)
  7. Activity加载View调用顺序
  8. 重写到边缘–充分利用它! 在GlassFish上!
  9. listview 模仿用户点击事件。
  10. 32位与64位应用程序速度分析
  11. ASP.NET 高级编程基础第十二篇—服务器控件
  12. 那些年,陪伴过我们的下载软件(上)
  13. 公众号零基础,只需10分钟,你的公众号也能5天500+粉丝
  14. word如何删除某一页的页眉
  15. 对账、结账、错账更正方法、划线更正法、红字更正法、补充登记法
  16. 简单测试ROS里面C++ 和 python 文件获取参数格式
  17. 4.29 C语言练习(宏定义练习:输入两个整数,求他们相除的余数。用带参的宏来实现,编程序。)
  18. 人工智能和计算机程序有什么区别,人工智能和机器学习之间有什么区别?
  19. Ureal:用ue4做出游戏中的爆炸特效真实感和力量感
  20. CSS中颜色、样式规则(字体样式、列表样式、表格样式)

热门文章

  1. 苹果收购AI音乐公司,音乐人工智能将迎来新机遇?
  2. Java汉字获取拼音、笔划、偏旁部首
  3. XCUIApplication API
  4. 物联网ESP8266配置成AP模式
  5. 永恒之蓝漏洞复现MS17-010
  6. 天刀论剑显示服务器,天刀S2论剑 各段位奖励称号外观及属性一览
  7. Python中常见的调色板: 颜色 color
  8. 关于有重根情况下微分方程根的一般形式
  9. 你觉得做程序员期间最开心的是什么?
  10. 一文看懂什么是单线、双线、三线 、BGP网络 他们有什么区别