1)角色固定部位闪白的实现方案
2)LoadFromStreamAsync 加载报错
3)Separate Axes 设置的影响
4)特效在贴近摄相机时明显变卡
5)不透明渲染开销中的粒子耗时


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

Rendering

Q:前提:有一个 3D 模型,只有一个 Skinned Mesh Renderer,只用一个 Material。我想实现在运行时,左手部受击,左手部闪白效果。整个模型闪白效果已经简单实现了,针对局部闪白效果,现在想到的有 3 种方案:

1、出一张局部遮罩图,与 UV 对应,如:这张图只有左手部分是黑色的,其它部分是白色的。修改 Shader,可以用这张图与 MainTex 做运算,实现运行时修改左手部分颜色。
2、建模时在左手刷顶点颜色,Shader 加入顶点颜色处理。
3、建模时不刷顶点颜色,但是在运行时用脚本修改左手上所有顶点颜色,也是要修改 Shader 加入顶点颜色处理。

想用第 3 种方案,但是有一个问题,怎么能在运行时高效修改模型指定部位的所有顶点颜色?网上有一种方法,通过指定 bone 来修改,如下:

void Highlight(Transform bone) {
        Debug.Assert(smr != null);
        var idx = GetBoneIndex(bone);
        var mesh = smr.sharedMesh;
        var weights = mesh.boneWeights;
        var colors = new Color32[weights.Length];

        for (int i = 0; i < colors.Length; ++i) {
            float sum = 0;
            if (weights[i].boneIndex0 == idx && weights[i].weight0 > 0)
                sum += weights[i].weight0;
            if (weights[i].boneIndex1 == idx && weights[i].weight1 > 0)
                sum += weights[i].weight1;
            if (weights[i].boneIndex2 == idx && weights[i].weight2 > 0)
                sum += weights[i].weight2;
            if (weights[i].boneIndex3 == idx && weights[i].weight3 > 0)
                sum += weights[i].weight3;

            colors[i] = Color32.Lerp(regularColor, highlightColor, sum);
        }

        mesh.colors32 = colors;
    }

这只是指定一个 bone 的,但是模型的一个部位有很多个节点,如:

一个手臂有这么多。上面的方法计算量有点大。请问有更好的方案可以实现修改指定部位颜色的需求吗?

A:优化的常规思路就是空间换时间,如果你要采用第三种方法,而且每次闪白的位置固定,可以把要闪白的顶点离线存储下来,或者运行时计算过一次存储在内存中。

个人感觉,如果题主需求下闪白的位置是固定的,那第三种方法未必是一种效率最好的方式:

1、如果要运行时修改,Mesh 需要可以读写,内存会占用两份;
2、运行时修改的 Mesh,每次修改之后都要重新提交给 GPU,这也会占用一些时间和带宽;
3、如果网格较为密集,顶点数量较多,这种计算本身就更加适合 GPU 去做,除非你明确是 GPU 瓶颈,要优化到 CPU,常规思路下是不太推荐的。

第一种方法多一张贴图的内存消耗,也多一次采样过程,但是可以控制得更加细致,尤其在顶点较为稀疏的情况下;第二种方法减少了贴图的消耗,增加一些顶点的数据,个人感觉已经是一种很不错的做法了。

运行时修改顶点色,我能够想到的就是为了方便控制区域,比如:不固定的任何区域都需要有各自亮起来的需求,这样要根据受击或者攻击的触碰位置来做计算,固定的情况下,我觉得还是离线做好数据比较好。

感谢贾伟昊@UWA问答社区提供了回答


加载

Q:我们最近为了 AssetBundle 包加密,使用了 LoadFromStreamAsync 接口加载,Unload(true) 卸载,并使用了 C# 的 FileStream 作为二进制文件的读取方式,但是在 Android 手机上资源一加载多了就会报 Too many open files 错误,如图所示:

我们的 FileStream 是在 AssetBundle 卸载的时候释放的,释放方式如下所示:

mFs.Close();
mFs.Dispose();
mFs = null;

同时我们用 adb shell ulimit 查看了手机的句柄上限是 1024,我们当时加载的 AssetBundle 数量是 500 左右,在网上没有找到类似的信息,想问下有没有人遇到过类似的问题,感谢。

补充:我们用 adb 看了进程实际使用的句柄,并与之前使用 LoadFromFile 不报错的包做了对比,发现每个 AssetBundle 都多出来一个句柄,而之前的方式是没有的。现在我们的 FileStream 流是在 Unload 以后释放,是否应该在加载完成后就释放?但是这样,后续再加载这个 AssetBundle 里的东西就会报错,下图是多出来的句柄的截图:

A1:建议使用 LoadFromFile 测试,看是否有问题。这里是使用 LoadFromFile + File offset 加密的方案,可参考Unity AssetBundle 高效加密案例分享

4.5.4. iOS file handle overuse
Current versions of Unity are not affected by this issue.
In versions prior to Unity 5.3.2p2, Unity would hold an open file handle to an AssetBundle the entire time that the AssetBundle is loaded. This is not a problem on most platforms. However, iOS limits the number of file handles a process may simultaneously have open to 255. If loading an AssetBundle causes this limit to be exceeded, the loading call will fail with a “Too Many Open File Handles” error.

This was a common problem for projects trying to divide their content across many hundreds or thousands of AssetBundles.

For projects unable to upgrade to a patched version of Unity, temporary solutions are:

Reducing the number of AssetBundles in use by merging related AssetBundles

Using AssetBundle.Unload(false) to close an AssetBundle’s file handle, and managing the loaded Objects’ lifecycles manually

Assets, Resources and AssetBundles - Unity Learn
感谢狂飙@UWA问答社区提供了回答

A2:可以先加载完就释放 FileStream 流试试,看看句柄数是否减少。但我个人认为很可能是 LoadFromStream 本身就会产生 2 个句柄,不过没有做过测试,题主可以试试看。

LoadFromStream 现在用的团队已经非常少了,一般都是用于加密所致,但是否确实需要对每个 AssetBundle 都加载,这个需要研发团队考虑一下了。同时,如果 LoadFromStream 确实需要,那么后续可能最为现实的方式就是减少内存中驻留的 AssetBundle 数量了。

该回答由 UWA 提供


Particle

Q:这个问题是项目从 Unity 5.6 升级到 2018 时发现的,目前怀疑是 Unity 2017/2018 引入的粒子系统 Bug。

发生了什么:

1、使用的版本是 Unity 2018.4.9 LTS(测试了 Unity 2017 LTS 也会出现同样的问题,而 Unity 5.6 是正确的);
2、粒子由两层组成,back 是 triangles 的一个 sub-emitter(继承了 size),勾选了 Size over Lifetime,但只给了一个常数(一条 1.0 的直线);

triangles

3、若不勾选 |Separate Axes|,则一切正常,画面显示为三角形粒子带着一个正方形的背景,符合预期(见图中蓝色粒子);

4、若勾选 |Separate Axes|,并且将三个轴都设置成一条 1.0 的直线,画面显示为三角形粒子带着一个很扁的矩形背景,完全不正确(见图中红色粒子)。

按照我的理解,是否勾选 |Separate Axes| 不应该会影响显示结果,因为三个轴都设置同样的曲线(1.0 的直线)。需要说明的是,这个情况仅当 back 是 triangles 的 sub-emitter 才会发生,如果是独立的 ParticleSystem,简单测试下来行为是正确的(显示均为正方形)。

如何重现:

希望得到大家的帮助,看看是否是哪里设置错了,或者如何避免这个问题。感谢不尽!

A:把 back_red 的 3D Start Size 勾上,发现 X 和 Y 轴缩放不一样,如果都改成 2 就好了。同样把 back_blue 的 3D Start Size 勾上,发现效果和 back_red 一样了。个人感觉 3D Start Size 和 Separate Axes 起同样的作用,所以要保持一致。

感谢一天到头困来的@UWA问答社区提供了回答


Optimize

Q:自由视角游戏,某些技能离 Camera 较远的时候看正常,但当 Camera 贴近特效中心的时候,游戏整体有很明显掉帧的情况,而在 Profiler 中却未发现有什么明显的异常。请问可能的原因会有哪些?

A:很可能是 GPU 端的计算压力太大,靠近特效时,Overdraw 一定是大幅增加的。

该回答由 UWA 提供


Particle

Q:如图所示,这样的情况是正常的吗?以我的理解粒子的开销是在 Render.TransparentGeometry 之下的,为什么会在 Render.OpaqueGeometry 这里也有体现?这说明了什么问题呢?

A:这个和你的粒子的材质的 Render Queue 有关;queue 是 Transparent,那自然就是在 Render.TransparentGeometry 下,queue 是 Geometry,那就在 Render.OpaqueGeometry 下了。

感谢李星@UWA问答社区提供了回答


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


↙↙↙阅读原文可查看相关链接,并与作者交流