骨骼捏脸功能的实现方法

骨骼捏脸功能的实现方法

本期我们聚集了这些话题:项目骨骼规范已经确定的情况下如何增加骨骼捏脸功能、网格重建定位、ScriptableObject加载速度分析、光照贴图和材质组合导致额外DrawCall...


这是第132篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群:793972859(原群已满员)


骨骼动画

Q1:我所在的项目的骨骼规范已经确定,cs骨骼和bone骨骼加起来有60根。现正在调研捏脸的方案,基本确定通过增加面部骨骼来进行捏脸。先描述下我对面部骨骼的理解,如有错误请大家指正。

增加的骨骼与原有的骨骼同时导出制作Avatar供骨骼动画使用,但是骨骼动画并不控制这些骨骼。玩家捏脸后的数据,也就是骨骼的缩放和旋转,直接应用到这些面部骨骼上,完成脸部的定制。实际应用到项目里,有如下几个问题

1、现有项目中,导入时Optimize Game Object选项是勾选的,这样就无法在C#层拿到骨骼信息,Expose出来的骨骼是“只读”的,没办法把捏脸数据直接应用到运行时的骨骼上。 暂时只能通过“使用额外的未开优化的Avatar动态生成目标Mesh”的方法来绕过,不知道还有没有其他更直接的方法?
2、已有的骨骼规范没有脸部的骨骼,如果要加上,就需要重新导出之前所有的动画。
有没有不修改现有骨骼的方案呢?

头部和身体分离,不过接缝处可能会穿帮。
另外还脑洞了一个方案:专门为头部做GPU Skin。导入模型时做预处理,将头部的骨骼和权重添加Mesh的uv2和uv3上(代码里区分一下是否是头部骨骼,身体部分的全是零就好了),另存为asset。然后每帧传骨骼矩阵到Shader里,在vs里面修改顶点的坐标。不过这样会占用额外的带宽。
感谢凯奥斯@UWA问答社区提供了回答

接楼上,头和身体分离,QualitySettings->Blend Weights改成4,可以解决大部分接缝穿帮问题。其实现在游戏中,大部分头发也会有动画,所以头发也要分离。捏脸后的Mesh保存下来,在游戏中和身体拼接的时候,用保存的Mesh(大部分游戏脸部没有动作)。实际游戏中,并不需要我们捏脸时的骨骼。
感谢郑骁@UWA问答社区提供了回答

离线骨骼采样的时候不需要勾Optimize Game Object。等整合出脸了以后再,把脸型贴回去。或者只限定脸部的骨头可读写。
感谢马古斯@UWA问答社区提供了回答

该问答来自UWA问答社区,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bbc2dba17780b3478091926


UI

Q2:在Profiler中,我们能否看到重建的具体是哪些网格呢? 目前我只能通过VB Uploads的大小来判断是否引起了网格重建,但是并不知道一个界面中哪些元素会引起网格重建。
这里我有个测试案例,image放进动态canvas和不放进canvas里,这个上传的绘制内容会有很大的变化,但是具体是哪些东西如果每个东西都去测试一遍,感觉很耗时。

请输入图片描述
请输入图片描述

用VB Uploads来判断重建可能不太直观,因为SkinnedMesh和粒子系统应该也会引起这一项的变化。
在UGUI里,网格重建分为两个部分:Rebuild和Rebatch。其中Rebuild是以UI元素为单位的,比如一个Image的Color改变了,那么这个Image就会发生Rebuild操作,开销是包含在Canvas.SendWillRenderCanvases中的,只要是改变了UI元素的顶点属性的操作都会引起SendWill的开销。至于是哪一个UI元素发生了Rebuild,从Profiler里确实是看不出来的。
而Rebatch是以Canvas为单位的,只要Canvas里有任何一个UI元素发生了变化(也包含位置变化),那么这个Canvas就要进行Rebatch,开销是包含在Canvas.BuildBatch和子线程中的Canvas.SortJob、Canvas.GeometryJob等函数中的。至于是哪一个Canvas发生了Rebatch,可以通过Canvas.BuildBatch函数来确定,在Profiler里选中Canvas.BuildBatch,然后在右侧显示相关的对象,里面列出的就是当前帧发生Rebatch的Canvas的名字,但这个Rebatch是由哪些UI元素的变化引起的,在Profiler里也是看不出来的。

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bbb2aa217780b34780918ed


渲染

Q3:发现Unity内部的光照贴图分配方式有一些问题。很多同材质的物体,因为光照贴图被分配到不同的Atlas上,导致无法合并批次。
比如下面三个连续的DrawCall,实际MeshRenderer的材质完全一样,偏偏由于光照贴图在几个不同的Atlas上,而无法实现合并。
请输入图片描述
请输入图片描述
请输入图片描述
经观察,如果能够将出现这种情况的物体实现合并,场景的Draw Call消耗可能降低超过1/3。
所以,现在希望有一个办法,能够优化光照贴图的分配流程,实现同材质物体尽量被分配到同一张Atlas上。

两种做法:
1)自定义LightmapParameters,通过设置Bake tag,相同tag的物件,会烘焙到同一张Lightmap中。Terrain_1_1是新创建的LightmapParameters,替换掉默认的Pamameters。
请输入图片描述
2)通过Renderer的uv,从完整的Lightmap贴图中获取对应的贴图,然后把这些贴图合并生成一个新的Lightmap贴图(可以用tinyexr结合stb的库来读写exr)。

感谢江南@UWA问答社区提供回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bbc460690b1a8347db6dd46


内存管理

Q4:我在优化项目的时候发现当删除人物模型,其材质和网格与Texture2D依然存在内存中。使用的删除方法是(DestroyImmediate)。查看Texture2D的引用如图:
请输入图片描述
发现其被一个实例化的Material引用,进一步查找之后,如图:
请输入图片描述
为什么会有这么多被引用项呢?还是我查找的方式不对?请问如何释放这部分内存?(人物模型确认被删除了)

材质和贴图如果要立即释放,也要手动Destroy才行,否则就是当没有其他引用的时候,调用Resources.UnloadUnusedAssets才行,不过Resources.UnloadUnusedAssets会造成卡顿,建议还是手动管理材质和贴图。

感谢旭军@UWA问答社区提供回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bbc6bb290b1a8347db6dd4a


序列化

Q5:ScriptableObject的加载速度,同插件MessagePack,或者一般Json插件反序列化的速度相比,大概在什么水平?现在自己测试的结果,Unity直接用C#序列化 / 文本读取再Parse / MessagePack插件,三者的加载同样的一堆散碎配置文件的时间比大概是 10:4:3。
但是即使是最快的MessagePack插件的读取时间也不令人满意。所以,希望能知道 ScriptableObject 或者 Sqlite是否可以比MessagePack更快地加载配置文件。

Sqlite其实读取数据是最快的,但是重点是数据结构,可以使用反射进行反序列化,但是效率上没有MessagePack高。个人认为还是从设计上解决问题,用空间换时间,提前解析数据,占用内存,使用时直接读取内存数据比较合理。

感谢郑晓@UWA问答社区提供回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5bb8a578afd2174f6fe946b1

今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:793972859(原群已满员)