如何设计渲染等级?

如何设计渲染等级?

本期聚集话题:PBR在手游开发中的适用性、如何设计渲染等级(进阶版)、如何判断硬件支持GPU Instancing、25个你需要规避的DrawCall增长的问题...


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

UWA 问答社区:answer.uwa4d.com
UWA QQ群:793972859(仅限技术交流)


渲染

Q1:现思考如何设计项目中的渲染等级,求有经验的大佬指个方向,例如ShaderLOD,还有其他什么思路吗?谢谢!

整理了一下我们游戏中效果分级的内容,供参考:
请输入图片描述

感谢贾伟昊@UWA问答社区提供了回答,欢迎大家转至社区进行进一步交流:

https://answer.uwa4d.com/question/5acc208b425802635474fc7d


渲染

Q2:我用2017.2.0渲染一张地图,为什么在小米4上Camera.Render会有很高的耗时?一个空场景,为什么Camera.Render耗时这么高?

UWA:我们做了一个简单测试来研究一下这个问题,即在场景中只渲染一个Cube。
请输入图片描述
机型:
(1) 华为6Plus
(2) 小米4
Profiler截图如下,蓝色框:华为6Plus 红色框:小米4
请输入图片描述
由上图可知,小米4的Camera.Render耗时不稳定,有时候会出现10+ms的峰值。
通过查看Unity Profiler以及和其他设备进行对比,发现了以下两点是造成小米4上耗时不稳的原因:

1、Culling不稳
下图为小米4和华为6Plus设备上,Culling操作的耗时比较。蓝框为华为6Plus设备,红框为小米4设备。通过Timeline可以看到是场景SceneNode的计算导致。
请输入图片描述

2、Drawing不稳
渲染耗时也同样不稳定,如下图所示:通过Timeline发现,小米4设备上,主线程的渲染进程会经常等待子线程完成后再进行后续操作,而同样的App,在华为6Plus上则不会如此表现,如下图所示。
请输入图片描述

小米4:
请输入图片描述

华为6Plus:
请输入图片描述

因此,对于上述测试做个总结:

(1) Culling中的SceneNode计算操作在小米4设备上经常伴随更为耗时的CPU开销;
(2) 小米4上更容易表现出主线程渲染等待子线程的情况。

以上两点是题主问题的主要原因,但至于更深层次的解释,这个恐怕要联系手机研发厂商甚至底层芯片硬件厂商了。但需要说明的是,这仅是一个简单测试例子的表现情况,复杂场景并不一定同样适合。但测试和分析方法都是不变的,题主后续如有需求,可自行测试寻找答案即可。

欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5ada00363dba2a108c7e3236


渲染

Q3:我们用的Unity 5.5场景带有2级LOD,我们使用Lightmap渲染场景,结果次级的LOD模型渲染出来的阴影面是纯黑的,这个该怎么办呢?烦请大神指点!

Untiy官方在5.5版本里其实是不支持LOD的Lightmap的,即便是到了2017.4版本里,官方文档也是让你去使用Light Probes:https://docs.unity3d.com/Manual/LODForBakedGI.html

当然它也说到:
“When you use the Progressive Lightmapper, there is no need to place Light Probes around the LOD Group to generate baked indirect lighting. However, to make Realtime GI affect the Renderers in the LOD Group, you must include the Light Probes.”

我们也是在用Unity 5.5版本,没有试过Progressive Lightmapper,不知道效果如何。所以第一个解决方案是升级到Unity 5.6或者Unity 2017尝试下Progressive Lightmapper。

如果想在Unity 5.5的下面做,我们是使用手动拷贝烘焙后的scale和offset的方式来实现,就是在运行时将原始模型的光照题图信息完整地拷贝给LOD低层级的模型。但是这有一个非常强的限制:要保证两个模型的UV2是完全可以对应上的,否则UV2可能会出现一些错乱。使用Simpylgon这样的中间件可以做到尽量保持UV2不变。

这样做有两个问题:

  • 如果是减少了很多面的模型,比如删除掉了一个组件,结果组件露出来的面因为之前是被遮挡的,会是黑色的。不过大部分情况下减面很多的LOD模型也会距离很远,这种通常可以接受;
  • 运行时设置光照信息的材质不能进行静态合批,否则在设备上会有问题。

另外还有一个就是因为是运行时修改的,所以在编辑器模式下如果不让设置组件可以运行的话,会有预览效果不对的问题,运行状态下可以保证正确。

原始LOD0烘焙效果:
请输入图片描述

LOD1为了避免黑所以不参与烘焙的效果:
请输入图片描述

运行时修改了光照贴图参数的效果:
请输入图片描述

设置光照贴图参数的Component代码:

public class RendererLightMapSetting : MonoBehaviour
    {
        public int lightmapIndex;
        public Vector4 lightmapScaleOffset;

        public void SaveSettings(SceneLightMapSetting setting)
        {
            if (!IsLightMapGo(gameObject))
            {
                return;
            }
            Renderer renderer = GetComponent<Renderer>();
            if (setting)
            {
                lightmapIndex = setting.GetGlobalIndex(renderer.lightmapIndex);
            }
            else
            {
                lightmapIndex = renderer.lightmapIndex;
            }
            lightmapScaleOffset = renderer.lightmapScaleOffset;
        }

        public void SetSettings(Renderer renderer)
        {
            if (renderer != null)
            {
                lightmapIndex = renderer.lightmapIndex;
                lightmapScaleOffset = renderer.lightmapScaleOffset;
            }
        }

        public void LoadSettings()
        {
            if (!IsLightMapGo(gameObject))
            {
                return;
            }
            
            Renderer renderer = GetComponent<Renderer>();
            renderer.lightmapIndex = lightmapIndex;
            renderer.lightmapScaleOffset = lightmapScaleOffset;
        }

        public static bool IsLightMapGo(GameObject go)
        {
            if (go == null)
            {
                return false;
            }
            Renderer renderer = go.GetComponent<Renderer>();
            if (renderer == null)
            {
                return false;
            }
            return true;
        }

        void Awake()
        {
            if (Application.isPlaying)
            {
                LoadSettings();
            }
        }
    }

然后,给美术提供一些可以做一键设置和清理的功能,逻辑可以自己写。
请输入图片描述

感谢贾伟昊@UWA问答社区提供了回答,欢迎大家转至社区进行进一步交流:

https://answer.uwa4d.com/question/5ad836acbdcd031091afdb80


渲染

Q4:什么情况下Transparent的材质在Render.OpaqueGeometry过程中渲染?
请输入图片描述

UWA:Render.OpaqueGeometry和Render. TransparentGeometry的执行,是依据RenderQueue<2450来区分的,RenderQueue的大小可以完全控制物体渲染的顺序,比如把半透物件渲染顺序调整到不透明物件之前,让Grass在Terrain前面画,在一些使用多层纹理的项目中,是一个比较不错的选择。通过RenderQueue来控制物体的渲染顺序,改变RQ来达到想要做的特殊事情。

欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5ad6be3e9f0be03717942a9b


2D

Q5:之前是看UWA相关的视频,把这个脚本挂在预设上的,然后通过这次的测试结果看到这个函数在解压的时候占了很高的堆内存,这个脚本是在解压资源的时候也会被调用么?
请输入图片描述
另外,我们昨天用UWA GOT在跟踪解压时堆内存的问题时,发现并没有出现网站上报告的PolygonImage这个问题啊?是因为什么呢?

UWA:PolygonImage的实现是作为一个MeshEffect来修改对应的Image的几何(把四边形变为多边形),过程中会出现一定量的堆内存分配(多边形越复杂,分配的堆内存越多)。

而MeshEffect的触发,只发生在对应的Image被重建的时候:被激活、修改颜色(透明度)、修改长宽等等…

所以该效果不建议使用在频繁变化的Image上,会造成大量的堆内存分配。
而题主在UWA GOT中没有观察到这一项,很可能是因为没有打开(激活)某些使用了PolygonImage的界面。

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


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

本文封面图来源:经典动作游戏《战神》
http://jonvilma.com