Unity3D性能优化——渲染篇

2021-06-25  本文已影响0人  忆中异

渲染优化

在了解优化渲染前,我们需要了解在unity中,每一帧的渲染CPU和GPU都做了些什么:

1)CPU检查场景中每个对象,决定他们是否应该被渲染。(这些对象只有满足一定的条件才会被渲染。)

2)CPU收集即将被渲染的对象信息,并把这些信息分类为渲染指令(也就是draw calls,我们在之前的文章中也有提到,Draw Call实际上就是一个命令)。一个draw call包含网格数据以及网格如何被渲染。在某些场景,共享设置的一些对象可能会被合并为一个draw call。合并不同对象的数据到同一个draw call被称作batching。

3)CPU给每个draw call创建一个数据包,称为batch。每一个batch必须包含一个draw call。

4)CPU会发出指令,使GPU改变一些渲染状态。这个指令被称为SetPass call。SetPass call通知GPU,如何去渲染下一个网格。只有在渲染下一个网格时,其渲染状态相对于渲染上一个网格发生了变化时,才会调用SetPass call。

5)CPU把draw call发送给GPU。draw call通知GPU使用最近的SetPass call去渲染指定的网格。

6)有时,batch可能需要不止一个的pass。pass是shader代码的一部分,而新的pass需要改变渲染状态。对于batch中的每个pass,CPU必须发送一个新的SetPass call然后必须要再次发送draw call。

7)GPU按照CPU发送的指令顺序处理这些指令。

8)如果当前任务是SetPass call,那么GPU更新渲染状态。

9)如果当前任务是draw call,那么GPU渲染网格。渲染网格发生在很多阶段,不同阶段的shader代码可以定义渲染。其中:顶点着色器vertex shader告诉GPU怎么处理网格的顶点。片元着色器fragment shader告诉GPU怎么绘制单独的像素。

10)以上过程会重复执行,直到所有CPU发送的任务都被GPU完成。

理解渲染最重要的是:为了渲染一帧,CPU和GPU必须都完成他们的任务。他们中的任何一个花费了过长的时间去完成任务,都会造成渲染延迟。渲染问题有两个基本的原因。第一类问题是由低效的渲染管线引起。当渲染管线中一步或者多步花费了太长时间,打断了平滑的数据流时,渲染管线会很低效。渲染管线的低效被称为瓶颈。第二类问题是由于,渲染管线被塞入了太多的数据。即使是最高效的渲染管线,对于一帧中可以处理的数据量也是有限制的。

渲染优化的主要目的就是减少渲染的工作量,控制渲染的工作量是保证效率的根本,而每帧渲染的顶点数量是衡量渲染工作量最直观的标准之一:

每帧可渲染的顶点数量主要取决于设备的CPU和GPU。 不过通常来说PC游戏每帧渲染的顶点个数不宜超过2M,移动游戏每帧渲染的顶点数量不宜超过0.1M。


CPU渲染优化

CPU限制

image

我们下面列举以上几个方面具体的优化方法及技术

减少要渲染的对象数量

1.手动减少场景中物体的数量

这是一个最直观且有效的方法,比如在多人游戏中,我们可以减少可见玩家的数量,如果不影响游戏性和玩家体验,那这是就是一个即方便又快捷的方法。

2.Occlusion Culling(遮挡剔除)

遮挡剔除的原理就是当一个物体被其他物体遮挡住,不在摄像机的可视范围内时不对其进行渲染。具体方法如下所示:
把所有物体选中,Inspector面板中的static下拉菜单中勾选Occlusion Static 和 Occludee Static。

image

在Window中选择Occlusion Culling。

image

然后在Occlusion Culling面板中选择Bake,并点击右下角Bake按钮

image

在bake后,我们可以对比遮挡剔除前后的差别
(遮挡剔除前)

image

(遮挡剔除后)

image

可以看到,在场景中被遮挡部分并没有被渲染。

Occlusion Culling(遮挡剔除)是对于场景优化最重要的技术之一,对于较大的场景一定要使用遮挡剔除,大场景中过多的绘制渲染会造成很大的性能损耗。

3.摄像机Clipping Planes

我们可以通过摄像机的Clipping Planes 的Far裁剪远端,从而降低摄像机的绘制范围,如同所示:

image

为了降低性能损耗同时保证游戏质量,Far的值应该合理控制,不要造成不好的游戏体验,或者我们可以用雾来掩盖不被渲染的远端。

减少渲染对象的渲染次数

在游戏中,实时的光照、阴影、反射可以极大的提升观感,但这些操作需要耗费极高的性能。

1.Lightmap(光照贴图)

Unity灯光默认是实时光照,也就是说物体在灯光下不同位置会产生不同灯光效果,由于动态光源在实时光照下会友大量的Setpass Calls,为了减小Setpass Calls,我们可以烘焙灯光效果,Unity会为我们生成光照贴图,这样大大减少了Setpass Calls。

为了了解光照贴图,我们先新建一个项目

image

我们有五个点光源,在运行的时候可以看到Setpass Calls比较大。而我们的目的就是减少Setpass Calls。

image

首先我们把被光照到的物体勾选上Lightmap Static。

image

然后把所有光源的Mode设置为Baked

Realtime: 实时(默认)/ Baked: 烘焙 / Mixed: 混合

image

选择window ——> Lighting ——> Settings

image

我们在Lighting设置面板中可以进行一些Lightmapping设置,这里我们点击右下角的Generate Lighting,也可以勾选旁边的Auto Genenrate(不建议勾选)然后开始生成光照贴图,完成后会出现以下文件

image

并且可以看到Setpass Calls也从之前的23降到了8

image

Lightmapping更多的使用方法请参照Unity 官方文档 这里只介绍基本的优化相关

2.阴影

在unity中,阴影相关的优化我们可以在质量设置中进行

image
  1. )阴影 (Shadows):此项决定应该使用哪种阴影类型。
    硬阴影和软阴影 (Hard and Soft Shadows):硬阴影和软阴影都将得到渲染。
    仅硬阴影 (Hard Shadows Only):仅硬阴影 (hard shadows) 将得到渲染。
    禁用阴影 (Disable Shadows):没有阴影会被渲染。
  2. )阴影分辨率 (Shadow resolution):阴影可以按以下几种不同分辨率进行渲染: 低 (Low) 、中 (Medium) 、高 (High) 和很高 (Very High)。分辨率越高,处理开销就越大。
  3. )阴影投射 (Shadow Projection):从平行光源投射阴影有两种方法。紧密配合 (Close Fit) 渲染分辨率更高的阴影,但是如果摄影机移动,这些阴影有时就会有些许摇晃。稳定配合 (Stable Fit) 渲染分辨率更低的阴影,而阴影不会随摄影机的移动而摇晃。
  4. )阴影距离 (Shadow Distance):从摄影机处可以看见阴影的最大距离。超出此距离的阴影将不会被渲染。
  5. )阴影层叠 (Shadow Cascades):阴影层叠 (shadow cascades) 数可设置为零、二或四。层叠数越高质量越好,但这要以处理开销为代价。

3.反射探头

反射探头没有很好的优化方法,在我们实际的项目中却常会用到,以创建更真实的反射,但却会增加batches, 所以我们应该在性能消耗较大的场合尽量最小化其使用率。

合并要渲染的对象的数据

  1. 动态批处理和静态批处理
image image

但同时我们注意CPU时间,同一材质的CPU时间明显更高,所以我们要注意增加的CPU时间是否会高于优化节省的时间。

image

可以从下图看到,Setpass Calls同样降低,且CPU时间也没有增加。

image

2.纹理图集

纹理图集是把大量的小纹理合并为一张大的纹理图的技术,当我们使用这个技术为游戏创建美术资源时,我们可以确保物体共享同一图集,因此适合合并。Unity内置了图集工具老版本为Sprite Packer,新版本为SpriteAtlas。具体使用方法如下图。

image image image image

GPU渲染优化

1.顶点处理。顶点处理是指GPU需要渲染网格中每一个顶点的工作。
顶点处理的消耗受两方面影响:必须渲染的顶点数量,以及在每个顶点上要进行的操作数量。

2.填充率。填充率是指GPU在屏幕上每秒可以渲染的像素数。如果我们的游戏受到填充率的限制,意味着我们的游戏每帧尝试绘制的像素数量超过了GPU的处理能力。

3.显存带宽。显存带宽是指GPU读写其专用内存的速度。如果我们的游戏速度受限于显存带宽,通常可能是我们使用的纹理太大,以至于GPU无法快速处理。

image

对于GPU优化问题我们主要可以通过下面几个技术来进行

GPU渲染优化中,我们常会进行一些shader的优化,这部分内容我希望放到后续shader相关的文章中,以便新手可以有更清晰的概念。

1.纹理压缩

纹理压缩技术可以同时极大的降低纹理在磁盘和内存中的大小。如果是显存带宽的问题,那么使用纹理压缩减小纹理在内存的大小可以帮助改善性能,Unity提供了很多纹理压缩的格式和设置,运用也十分简单,根据不同的需求、机器和场景我们使用的方式也有所不同,具体可以参照Unity 官方文档

2.Mipmap

如果我们的场景包含距离摄像机很远的物体,我们可以通过使用miapmap来缓解显存带宽的问题,mipmap的主要作用便是模型的贴图会根据摄像机距离模型的远近而调整不同质量的贴图显示,以达到优化目的。其用法如下图所示

image

注意这张图片的大小(右下角)
选中Generate Mip maps,点击Apply确定

image

这里可以发现图片大小发生了变化,这是因为我们使用MipMap技术之后,会对此贴图生成八张精度质量不同的贴图,所以内存占用变大。

下图中两个物体其一没有设置mipmap,我们来查看发生了什么变化

image

很明显,左边没有设置mipmap的物体,即使摄像机拉远,其清晰度也并没有发生变化

3.LOD

LOD与Mipmap类似,根据距离的远近使用不同精度模型,远处选择低精度的模型,近的时候选择高精度模型,这样就可以减少模型上面的顶点和面片数量从而提高性能。
其和Mipmap的差异主要在一Mipmap针对贴图,而LOD针对模型。Mipmap可以自动生成八张精度不同的贴图,而LOD必须由我们自己提供三个不同精度的模型。
我们来了解一下LOD的用法,首先需要三个相同的模型,但是这三个模型他们的三角面和顶点数都不同,这样就可以代表三种从高—中—低的层级(这里我使用粒子来代替, 分别用rate 50-20-5)

image

在TestLOD中添加组件LOD Group

image

在LOD Group中,我们把对应百分比可见度的物体添加到进去

image

接下来我们看运行时的效果及性能分析

image

可以看到随着摄像机位置的移动,我们的粒子会发生大、中、小三种变化(如果我们使用三种精度的模型,同样,随着摄像机的位置改变,模型的精度也就会发生变化。那么可想而知,性能损耗也就会发生改变)
转载于:https://zhuanlan.zhihu.com/p/40900056

上一篇 下一篇

猜你喜欢

热点阅读