Unity动态创建Avatar骨骼映射
创始人
2024-02-15 23:13:13

目录

  • 前言
  • 1 了解Avatar骨骼映射
  • 2 动态创建Avatar骨骼映射
    • 2.1 寻找相关API
      • AvatarBuilder.BuildHumanAvatar
        • Declaration
        • Parameters
        • Returns
        • Description
      • HumanDescription
        • Description
        • Properties
    • 2.2 创建Skeleton数据
    • 2.3 创建Human映射关系
    • 2.4 创建Avatar
  • 3 总结

前言

为了让单个动画可以通用于多个不同的人型模型上,Unity官方开发了一套骨骼重定向系统,把不同人型模型的骨骼映射到一套通用的骨骼映射上,然后再让动画去驱动这个通用的骨骼映射,从而实现驱动不同的模型。不过目前只支持人型模型,只需要把模型导入到Unity,就能自动生成骨骼映射(在Unity里是一个Avatar文件)。但是,并不是所有的模型格式导入到Unity都能自动生成这个Avatar,例如Glb/Gltf格式。为了让这些格式的模型也能支持通用的动画,通常需要放到一些建模软件里进行操作再导出为Fbx格式,这个过程是复杂且痛苦的。还有一种更无可奈可的情况是:完整人型模型是由各个模型在运行时动态组装生成的,并没有一个完整的模型可以导入建模软件进行操作。因此,我们应该寻找一种方式,使其可以在Unity运行阶段创建Avatar的骨骼映射。

1 了解Avatar骨骼映射

为了动态去创建这个Avatar文件,我们首先需要了解它包含了哪些内容,我们打开一个Unity已经创建好的Avatar:
在这里插入图片描述

可以看到,这个文件主要保存的是骨骼的映射关系。上面的人体里的每一个圆点代表着一个关节点,Optional Bone下面左边那一列就是Unity里设定好的关节点的名称,它们在每个Avatar文件里都是一样的,而右边部分就是当前这个模型的骨骼节点,Unity已经帮我们映射好了它们与通用人型骨骼的对应关系。当我们把这个Avatar映射文件赋值给Animator后,Animator就会去驱动固定的那些骨骼信息点,而这些固定的骨骼信息点就会去根据它们和模型真实骨骼的映射关系找到真正需要驱动的骨骼点,从而对其进行驱动,最终整个模型就动起来了。

2 动态创建Avatar骨骼映射

2.1 寻找相关API

由于通常情况下模型导入时unity就能帮我们创建好这个Avatar映射,我们根本就没真正接触这个过程,因此首先需要去查询官方是否有开放相关接口。幸运的是,确实有相关的接口,官方API描述如下:

AvatarBuilder.BuildHumanAvatar

Declaration

public static Avatar BuildHumanAvatar(GameObject go, HumanDescription humanDescription);

Parameters

go
humanDescription

Returns

Avatar Returns the Avatar, you must always always check the avatar is valid before using it with Avatar.isValid.

Description

Create a humanoid avatar.
The avatar is created using the supplied HumanDescription object which specifies the muscle space range limits and retargeting parameters like arm/leg twist and arm/leg stretch. See Also: HumanDescription.

从API中我们可以得知,使用它需要传入两个参数,第一个参数好理解,就是我们当前这个模型本身,第二个参数是个描述数据,我们再看看它的定义:

HumanDescription

struct in UnityEngine/Implemented in:UnityEngine.AnimationModule

Description

Class that holds humanoid avatar parameters to pass to the AvatarBuilder.BuildHumanAvatar function.

Properties

armStretch
feetSpacing
hasTranslationDoF
human
legStretch
lowerArmTwist
lowerLegTwist
skeleton
upperArmTwist
upperLegTwist

它具有一堆属性,光看这里我们也不知道其如何赋值,但恰好这些数据在上面的Avatar映射文件里见过:
在这里插入图片描述

可以看到,都是对骨骼的一些限制,大部分直接默认值即可,但有两个属性是图上没显示的,也就是hunman和skeleton这两个属性,但是从描述可以知道,hunman保存的刚好就是骨骼映射关系,而skeleton保存的是模型的骨骼集合,因此我们只需要创建出这两个属性需要的数据即可。

2.2 创建Skeleton数据

由于skeleton保存的是模型的骨骼集合,因此比较好操作,我们先把它创建起来,代码也很简单,就是把模型的所有Transform都收集起来即可。即使不是骨骼的Transform也无所谓,后面会根据映射关系从这些骨骼中找出对应的骨骼Transform。因此我们直接写代码:

  private static SkeletonBone[] CreateSkeleton(GameObject avatarRoot){List skeleton = new List();Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren();foreach (Transform avatarTransform in avatarTransforms){SkeletonBone bone = new SkeletonBone(){name = avatarTransform.name,position = avatarTransform.localPosition,rotation = avatarTransform.localRotation,scale = avatarTransform.localScale};skeleton.Add(bone);}return skeleton.ToArray();}

代码很简单,就是传入当前的模型,然后获取模型所有的Transform组件,然后把数据赋值给SkeletonBone即可再添加到集合中即可。

2.3 创建Human映射关系

这里的映射关系本来是在建模软件里做的,那我们如何能知道它们之间的关系呢?有两个方法:

  • 1 询问CP同学得知这个模型的基础骨骼和Avatar的对应关系,简单快捷高效
  • 2 自己根据Transform的名称和节点在模型的位置来猜测对应关系,可以在视图里对比,这样低效且不一定正确,可能需要多次尝试才能找到关系
    其实还有第三种方法:写算法去自动匹配,但是会比较困难,毕竟导入的模型不一定就是标准的T Pose,而且计算也不是那么好写的,感兴趣的大佬可以去尝试一下,到时候成功了麻烦教教我~
    从上面我们千辛万苦终于得到了映射关系,我这里是使用一个Dictionary来保存,大概如下:
 public static Dictionary HumanSkeletonMap = new Dictionary(){{"pelvis", "Hips" },{"spine_01", "Spine" },{"spine_02", "Chest"},{"spine_03", "UpperChest" },......此处省略了一堆key-value......{"neck_01", "Neck" },{"head", "Head" },{"eye_EyeJoint_L", "LeftEye" },{"eye_EyeJoint_R", "RightEye" },{"mouth_JawJoint_M", "Jaw" },};

但是看了一下上面映射属性的数据结构是HumanBone,我们还需要写个函数去做映射,代码如下:

    private static HumanBone[] CreateHuman(GameObject avatarRoot){List human = new List();Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren();foreach (Transform avatarTransform in avatarTransforms){if (HumanSkeletonMap.TryGetValue(avatarTransform.name, out string humanName)){HumanBone bone = new HumanBone{boneName = avatarTransform.name,humanName = humanName,limit = new HumanLimit()};bone.limit.useDefaultValues = true;human.Add(bone);}}return human.ToArray();}

和上面的skeleton集合的写法有点相似,就是赋值本分稍有区别,这里主要记录的是映射关系,所以就把上面的Dictionary里的映射关系赋值进去即可,最终我们得到了一个映射集合。

2.4 创建Avatar

拥有了SkeletonBone骨骼数据集合和HumanBone映射数据集合,我们就可以创建Avatar的描述文件了,其他属性都设置默认值,代码如下:

HumanDescription humanDescription = new HumanDescription(){armStretch = 0.05f,feetSpacing = 0f,hasTranslationDoF = false,legStretch = 0.05f,lowerArmTwist = 0.5f,lowerLegTwist = 0.5f,upperArmTwist = 0.5f,upperLegTwist = 0.5f,skeleton = CreateSkeleton(gameObject),human = CreateHuman(gameObject),};

现在我们连描述文件也有了,就可以创建最终的Avatar了,代码也很简单:

 Avatar avatar = AvatarBuilder.BuildHumanAvatar(gameObject, humanDescription);

到此,我们成功创建出了Avatar,使用时直接把这个Avatar赋值给Animator,即可使用通用人型动画驱动我们的模型了!

3 总结

从上面的步骤中不难看出,真正困难的只有创建骨骼映射那部分,毕竟我们不一定那么方便地就能找到模型的骨骼映射关系,但是一旦找到它们的关系,其他部分就再简单不过了。过程中我们可能会遇到一些千奇百怪的问题,比如模型动作非常诡异等,Mesh扭曲等等,这些问题大部分都是因为映射关系不对,少部分是因为骨骼集合里没收集上所有骨骼信息,不要惊慌,多细心检查,最终肯定能成功!

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...