Unity优化方向——优化Unity游戏中的图形渲染(译)

CPU bound:CPU性能边界,是指CPU计算时一直处于占用率很高的状况。
GPU bound:GPU性能边界,一样的是指GPU计算时一直处于占用率很高的状况。
 

简介

在这篇文章中,咱们将学习Unity渲染一帧时在幕后会发生什么,渲染时会出现什么样的性能问题,以及如何修复与渲染相关的性能问题。
在阅读本文以前,有一点须要记住的是,即没有适合全部状况的方法能够提升渲染性能。渲染性能受到游戏中许多因素的影响,同时也高度依赖于游戏所运行的硬件和操做系统。最重要的是要记住,咱们经过研究、实验和严格分析实验结果来解决性能问题。
这篇文章包含了关于渲染的最多见的性能问题的信息,以及如何修复这些问题的建议和进一步的延伸阅读连接。咱们的游戏可能会有一个或多个问题这里并无提到。然而,这篇文章仍然会帮助咱们理解咱们的问题,并给咱们知识和词汇来有效地寻找解决方案。
 

渲染的简要介绍

在咱们开始以前,让咱们来看看Unity渲染一帧时会发生什么。理解事件流和正确的术语将帮助咱们理解、研究和解决性能问题。
注:在本文中,咱们将使用术语“object”来表示游戏中可能渲染的对象。任何带有Renderer组件的GameObject都会被称为object。
在最基本的层面上,渲染能够描述以下:
  • 中央处理器,即CPU,计算出必须绘制什么以及如何绘制。
  • CPU向图形处理单元(即GPU)发送指令。
  • GPU根据CPU的指令绘制物体。
 
如今让咱们仔细看看发生了什么。在本文后面的部分中,咱们将更详细地介绍这些步骤,可是如今让咱们先熟悉所使用的单词,并理解CPU和GPU在渲染过程当中扮演的不一样角色。
描述渲染经常使用的短语是渲染管线,这是一个须要牢记的过程;高效的渲染就是保持数据高效地流动。
对于渲染的每一帧,CPU都作了如下工做:
  • CPU检查场景中的每一个对象,已肯定是否应该渲染它。对象只有在知足某些条件时才被渲染;例如,它的包围盒的某些部分必须位于摄像机的视锥体内。将不渲染的对象称为剔除对象。有关视锥体和视锥体剔除的更多信息,请参见此页
  • CPU收集关于将要渲染的每一个对象的信息,并将这些数据排序为称为draw call的命令。一个draw call包含关于单个网格的数据以及如何渲染该网格;例如,应该使用哪些纹理。在某些状况下,共享设置的对象能够组合到同一个draw call中。将不一样object的数据组合到同一个draw call中称为批处理。
  • CPU为每一个draw call建立一个称为批处理的数据包。批处理有时可能包含draw call调用以外的数据,可是这些状况不太可能致使常见的性能问题,所以咱们在本文中不考虑这些状况。
对于每一个包含一个draw call的批处理,CPU如今必须执行如下操做:
  • CPU能够向GPU发送一个命令来更改一些变量,这些变量统称为渲染状态。这个命令称为SetPass call。SetPass call告诉GPU使用哪些设置渲染下一个网格。只有当渲染的下一个网格须要从前一个网格更改渲染状态时,才会发送SetPass call。
  • CPU将draw call发送到GPU。draw call指示GPU使用最近SetPass调用中定义的设置来渲染指定的网格。
  • 在某些状况下,批处理可能须要不仅一个pass。pass是着色器代码的一部分,新的pass须要对渲染状态进行更改。对于批处理中的每个pass,CPU必须发送一个新的SetPass call,而后必须再次发送draw call。
 
同时,GPU还作了如下工做:
  • GPU按照从CPU发送的顺序处理任务。
  • 若是当前任务是SetPass call,GPU将更新渲染状态。
  • 若是当前任务是一个draw call,GPU渲染网格。这是分阶段进行的,由着色器代码的不一样部分定义。这部分渲染是很复杂的,咱们不会详细讨论它,可是理解一段叫作顶点着色器(vertex shader)的代码是颇有帮助的。顶点着色器告诉GPU如何处理网格的顶点,而后一段称为片断着色器(fragment shader)告诉GPU如何画出每一个像素。
  • 这个过程重复进行,直到全部从CPU发送的任务都被GPU处理。
如今咱们已经了解了 Unity渲染一帧时所发生的事情,让咱们考虑一下渲染时可能出现的的问题。
 

渲染问题的类型

关于渲染最重要的一点是:为了渲染一帧,CPU和GPU都必须完成全部的任务。若是这些任务中的任何一个花费太长时间来完成,它将致使帧的渲染延迟。
渲染的问题有两个基本缘由。第一类问题是由低效的管线引发的。当渲染管线中的一个或多个步骤花费太长时间没法完成时,就会出现低效率的管线,从而中断数据的顺畅流动。管线内的低效被称为瓶颈(bottlenecks)。第二种类型的问题是因为试图经过向管线推送太多的数据而致使的。即便是最有效的管线也有一个限制,即在一帧中能够处理多少数据。
当咱们的游戏是由于CPU花费太多的时间去执行任务而致使须要花费很长的时间渲染一帧时,咱们的游戏就是所谓的CPU边界(CPU bound)。当咱们的游戏由于GPU执行它的渲染任务而花费太多的时间渲染一帧时,咱们的游戏就是所谓的GPU边界(GPU bound)。
 

理解渲染问题

在进行任何修改以前,使用Profiler分析工具来了解性能问题的缘由是很是重要的。不一样的问题须要不一样的解决方案。一样重要的是,咱们要衡量咱们所做的每个修改的效果;解决性能问题是一种平衡的行为,改进性能的一个方面可能会对另外一个方面产生负面影响。
咱们将使用两个工具来帮助咱们理解和修复渲染问题:Profiler和Frame Debugger。这两个工具都内置在Unity中。
 

Profiler Window

Profiler窗口容许咱们查看关于游戏执行状况的实时数据。咱们可使用Profiler查看关于游戏的许多方面的数据,包括内存使用、渲染管线和用户脚本的性能。
若是你还不熟悉使用Profiler,那么Unity 手册是一个很好的介绍, 这里一篇文章将详细介绍如何使用它。
 

Frame Debugger

帧调试器容许咱们一步步地查看帧是如何渲染的。使用帧调试器,咱们能够看到详细的信息,例如每次draw call中绘制的内容、每次draw call的着色器属性以及发送到GPU的事件顺序。这些信息帮助咱们理解游戏是如何渲染的,以及咱们能够在哪里提升性能。
若是你还不熟悉帧调试器的使用,Unity手册中 关于帧调试器的部分是一个很是有用的指南。
 

发现形成性能问题的缘由

在咱们尝试提升游戏的渲染性能以前,咱们必须确保游戏是因为渲染问题而运行缓慢的。若是问题的真正缘由是过于复杂的用户脚本,那么尝试优化渲染问题是没有意义的。若是你不肯定你的性能问题是否与渲染有关,请查看这一篇 教程
一旦咱们肯定了咱们的问题与渲染有关,咱们还必须了解咱们的游戏是CPU bound,仍是GPU bound。这些不一样的问题须要不一样的答案,因此在尝试解决问题以前,咱们必须了解问题的缘由,这一点很是重要。若是你还不肯定你的游戏是CPU bound仍是GPU bound,你应该查看 这一个教程。
若是咱们肯定咱们的问题与渲染有关,而且咱们知道咱们的游戏是CPU bound仍是GPU bound,咱们就能够继续下面的内容了。
 
若是咱们的游戏是受到了CPU的限制
通常来讲,CPU渲染一帧所必须完成的工做分为三类:
  • 决定绘制什么
  • 为GPU准备命令
  • 向GPU发送命令
这几个类包含许多单独的任务,这些任务能够跨多个线程执行。线程容许独立的任务同时发生;当一个线程执行一个任务时,另外一个线程能够执行一个彻底独立的任务。这意味着工做能够作得更快。当渲染任务被分割到不一样的线程时,这称为多线程渲染。
在Unity的渲染过程当中有三种类型的线程:主线程,渲染线程和工做线程。主线程是咱们游戏的大部分CPU任务发生的地方,包含一些渲染任务。渲染线程是一个专门的线程,它向GPU发送命令。每一个工做线程执行单个任务,例如剔除或者网格蒙皮。哪一个线程执行哪些任务取决于游戏的设置和游戏运行的硬件。例如,目标硬件的CPU内核越多,能分配的工做线程就越多。所以,在目标硬件上描述咱们的游戏是很是重要的;咱们的游戏在不一样设备上的表现可能会很是不一样。
由于多线程渲染是复杂的,而且依赖于硬件,因此在咱们尝试提升性能以前,咱们必须了解是哪些任务致使咱们的游戏受到了CPU的限制。若是咱们的游戏运行缓慢是由于在一个线程上的剔除操做花费太多的时间,那么它将没法帮助咱们减小在另外一个线程上向GPU发送命令所花费的时间。
注意:并不是全部平台都支持多线程渲染;在撰写本文时,WebGL不支持这个特性。在不支持多线程渲染的平台上,全部CPU任务都在同一个线程上执行。若是咱们的CPU限制出如今这样的平台上,那么优化任何CPU的工做都将提升CPU性能。若是这是咱们游戏的状况,咱们应该阅读一下全部部分并考虑哪些优化可能最适合咱们的游戏。
 

图形工做

在Player Settings中的 Graphics jobs 选项决定了Unity是否使用工做线程来执行渲染任务,而这些任务在其余状况下是在主线程上完成的。在有此功能的平台上,它能够提供至关大的性能提高。若是咱们但愿使用这个特性,咱们应该在启用和不启用Graphics jobs的状况下对游戏进行概要分析,并观察它对性能的影响。
找出哪些任务致使了性能问题
咱们能够经过使用Profiler来肯定哪些任务致使咱们的游戏受到CPU的限制。本教程展现了如何肯定问题所在。
如今咱们已经了解了哪些任务致使咱们的游戏受到CPU的限制,让咱们来看看一些常见的问题及其解决方案。
 

向GPU发送命令

向GPU发送命令所花费的时间是游戏受到CPU限制的最多见缘由。这个任务是在大多数平台上的渲染线程上执行的,尽管在某些平台上(例如paystation 4)这多是由工做线程执行的。
向GPU发送命令时发生的代价最大的操做是SetPass call 。若是咱们的游戏因为向GPU发送命令而受到CPU限制,那么减小SetPass call的数量多是提升性能的最佳方法。
咱们能够在Unity的Profiler Window中的Rendering profiler区域看到发送了多少SetPass call和批处理(batches )。在性能受到影响以前能够发送的SetPass call的数量在很大程度上取决于目标硬件;在性能受到影响以前,高端PC能够比移动设备发送更多的SetPass calls。
SetPass calls的数量及其与批处理数量的关系取决于几个因素,咱们将在本文后面更详细地讨论这些主题。然而,一般状况是:
  • 在大多数状况下,减小批处理和/或让更多的对象共用相同的渲染状态设置将减小SetPass call的数量。
  • 在大多数状况下,减小SetPass call的数量将提升CPU性能。
若是减小批处理的数量并不能减小SetPass call的数量,那么它自己仍然能够提升性能。这是由于CPU处理单个批处理比处理多个批处理更有效率,即便它们包含相同数量的网格数据。
总的来讲,有三种方法能够减小批处理和SetPass call的数量。咱们将更深刻地研究这些问题:
  • 减小要渲染对象的数量可能会同时减小批处理和SetPass call。
  • 减小每一个对象必须渲染的次数一般会减小SetPass call的数量。
  • 将必须渲染的更少批处理对象的数据组合起来将减小批处理的数量。
不一样的技术将适用于不一样的游戏,因此咱们应该考虑多有这些选项,决定哪些能够适合应用于咱们的游戏和试验中。
 

减小渲染对象的数量

减小必须渲染的对象数量是减小批处理和SetPass call数量的最简单方法。咱们可使用几种技术来减小渲染对象的数量。
  • 简单地减小场景中可见对象的数量是一个有效的解决方案。例如,若是咱们在人群中渲染大量不一样的角色,咱们能够简单地在场景中减小这些角色。若是场景看起来仍然很好,性能获得了改善,那么这多是比更复杂的技术更快的解决方案。
  • 咱们可使用相机的远裁剪面属性来减小相机的绘制距离。此属性是摄像机再也不渲染对象的距离,若是咱们想要掩盖遥远物体再也不可见的事实,咱们能够尝试用雾来掩盖遥远物体的缺少。
  • 对于基于距离的更细粒度的隐藏对象的方法,咱们可使用相机的Layer Cull Distance属性为位于不一样层的对象提供自定义的裁剪距离。若是咱们有不少小的前景装饰细节,这种方法是有用的;咱们能够在比大型地形特征更短的距离内隐藏这些细节。
  • 咱们可使用一种称为遮挡剔除(occlusion culling)的技术来禁用被其余对象遮挡的对象的渲染。例如,若是咱们的场景中有一个大型建筑,咱们可使用遮挡剔除来禁用其背后的对象的渲染。Unity的遮挡剔除并不适用于全部场景,这可能会致使额外的CPU开销,设置起来也可能很复杂,但它能够大大提升某些场景的性能。这有一篇关于在Unity中采用遮挡剔除的最佳实践。除了使用Unity的遮挡剔除,咱们还能够经过手动去使玩家看不到的对象变得无效来实现咱们本身的遮挡剔除。例如,若是咱们的场景包含用于过场动画的对象,可是在以前或以后不可见,咱们应该停用它们。使用咱们本身的游戏知识老是比要求Unity动态解决问题更有效。
 

减小每一个对象必须渲染的次数

实时光照,阴影和反射为游戏添加了大量的真实性,但可能会很是耗费性能。使用这些特性会致使对象被屡次渲染,这会极大地影响性能。
这些特性的确切影响取决于咱们为游戏选择的渲染路径。渲染路径是在绘制场景时执行计算顺序的术语,在不一样的渲染路径之间的主要区别在于它们如何处理实时光照,阴影和反射。通常来讲,若是咱们的游戏运行在高端硬件上,而且使用了大量的实时光照、阴影和反射,那么延迟渲染(Deferred Rendering)多是一个更好的选择。若是咱们的游戏运行在低端硬件上而且不使用这些特性的话,前向渲染(Forward Rendering)可能会更适合。然而,这是一个很是复杂的问题,若是咱们但愿利用实时光照、阴影和高光,最好研究一下这方面的主题和作不一样的试验。Unity手册的 这个页面提供了更多关于Unity中可用的不一样渲染路径的信息。这篇 教程包含了关于Unity中有关灯光为主题的有用信息。
不管渲染路径如何选择,实时灯光、阴影和反射的使用都会影响咱们的游戏性能,所以了解如何优化它们时很是重要的。
  • Unity中的动态照明是一个很是复杂的主题,深刻讨论它超出了本文的范围,可是这个教程是对这个主题的一个很好的介绍,Unity手册的这一页详细介绍了常见的照明优化。
  • 动态照明是昂贵的,当咱们的场景包含不移动的对象时,例如风景,咱们可使用一种称为烘焙的技术来预先计算场景的光照,这样就不须要运行时计算光照计算了。这一篇教程介绍了这种技术,Unity手册中也有对烘焙光照这部分主题的详细介绍。
  • 若是咱们但愿在游戏中使用实时阴影,这多是咱们须要提升性能的方面。Unity手册的这一页是一个很好的参考,能够调整阴影属性的质量设置,以及这些设置如何影响外观和性能。例如,咱们可使用阴影距离属性确保只有附近的对象投射阴影。
  • 反射探针 能够建立真实的反射,可是在批处理方面花费的成本很是高。在考虑性能的状况下,最好将反射的使用控制在最低限度,并在使用反射的状况下尽量地优化反射。Unity手册的这一页提供了一些有关优化反射探针的指导。

将对象合并成更少的批处理

当知足某些条件时,批处理能够包含多个对象的数据。要符合批处理条件,对象必须知足:
  • 共享相同的材质实例
  • 有相同的材质设置(例如,纹理,着色器和着色器参数)
批处理符合条件的对象能够提升性能,尽管与全部优化技术同样,咱们必须仔细分析以确保批处理的成本不超过性能收益。
对于符合批处理条件的对象,有一些不一样的技术:
  • 静态批处理是这样一种技术,它容许Unity批处理附近不移动的符合条件的对象。能够从静态批处理中获益的一个很好的例子是一堆相似的对象,例如巨石。Unity手册的这一页包含了在咱们的游戏中设置静态的说明。静态批处理会致使更高的内存使用,因此在分析咱们的游戏时,咱们应该牢记这个成本。
  • 动态批处理是另外一种容许Unity批处理合适对象的技术,无论对象是否移动。对于使用这种技术批处理的对象有一些限制。这些限制和说明一块儿列在Unity手册这一页上。动态批处理对CPU使用有影响,这可能致使它在CPU时间上的开销大于节省的时间。在试验这种技术时,咱们应该牢记这一成本,并谨慎使用它。
  • 批处理Unity的UI元素有点复杂,由于它可能会受到UI布局的影响。这个来自2015年Unite Bangkok的视频很好地概述了这个主题,还有这一个教程提供了关于如何确保UI批处理按照咱们的意愿工做的深刻信息。
  • GPU instancing 是一种容许对大量相同对象进行高效批处理的技术。它的使用是有限的,并非全部的硬件都支持它,可是若是咱们的游戏在屏幕上同时有许多相同的对象,咱们可能会从这项技术中收益。Unity的这一页包含了对Unity3d中GPU实例化的介绍,详细介绍了如何使用它,哪些平台支持它,以及在什么状况下它可能会对咱们的游戏有帮助。
  • 纹理图集(Texturing atlasing)是一种将多个纹理合并为一个较大纹理的技术。它一般用于2D游戏和UI系统,但也能够用于3D游戏。若是咱们在为咱们的游戏创造美术资源时使用这种技术,咱们就能够确保共享纹理的对象能够进行批处理。Unity有一个内置的纹理图集工具,叫作Sprite Packer,用于2D游戏。
  • 能够在Unity编辑器中或者在运行时经过代码手动组合共享相同材质和纹理的网格。当以这种方式组合网格时,咱们必须意识到阴影、光照和剔除仍然会在每一个对象上运行。这意味着,合并网格带来的性能提高可能会被抵消,由于不这样作,这些对象就没法被渲染。若是咱们但愿研究这种方法,咱们应该去查看Mesh.CombineMeshes这个函数。Unity的Standard Assets资源包里面的CombineChildren就是应用了这项技术的一个例子。
  • 咱们在脚本中访问Renderer.material时必须很是当心。这会复制一个材质而且返回新副本的引用。这样作将打破批处理,若是渲染器再也不具备对同一材质实例的引用。若是咱们但愿在脚本中访问批处理对象的材质,咱们应该使用Renderer.sharedMaterial。
 

剔除、分类和批处理

对将要绘制的对象进行剔除、收集批处理数据和生成GPU命令均可能致使CPU限制(CPU bound)。这些任务能够在主线程上执行,也能够在单独的工做线程上执行,这取决于游戏的设置和目标硬件。
  • 剔除(culling)自己不太多是很是昂贵的,可是减小没必要要的剔除可能有助于性能。对于全部活动的场景对象,甚至那些没有被渲染的图层上的对象每一个对象都有一个相机开销。为了减小这种状况,咱们应该禁用摄像机,并禁用当前未使用的渲染器。
  • 批处理能够大大提升向GPU发送命令的速度,但有时会在其余地方增长没必要要的开销。若是批处理操做致使咱们的游戏受到CPU限制,咱们可能但愿限制游戏中手动或者自动批处理操做的数量。
 

Skinned meshes

SkinnedMeshRenderers 是用来当咱们的网格变形动画而使用的技术,称为骨骼动画。它最经常使用于动画角色,根据咱们游戏的设置和目标硬件,渲染蒙皮网格的相关任务一般会在主线程或单个工做线程上执行。
渲染蒙皮网格多是一个昂贵的操做。若是咱们能在Profiler中看到渲染蒙皮网格会致使咱们的游戏受到CPU的限制,那么咱们能够尝试作一些事情来提升性能:
  • 咱们应该考虑是否须要为当前正在使用的对象添加SkinnedMeshRenderer组件。例如,咱们可能已经导入了一个使用SkinnedMeshRenderer组件的模型,但实际上咱们并无对它采用动画。在这种状况下,用MeshRenderer组件替换SkinnedMeshRenderer组件将有助于提升性能。在将模型导入Unity时,若是咱们选择不在model Import Settings 中导入动画那么模型将会带有一个MeshRenderer而不是SkinnedMeshRenderer组件。
  • 若是咱们只是在某些时候在模型上使用动画(例如,只有在启动或只有当它是在相机的必定距离内),咱们能够切换它的网格为一个低细节版本或把MeshRender组件替换SkinnedMeshRenderer。SkinnedMeshRenderer组件有一个BakeMesh函数,能够以匹配的姿态建立一个网格,这对于在不一样的网格或渲染器之间进行交换很是有用,而不会对对象产生任何可见的改变。
  • Unity手册中的这一页面包含了关于优化使用蒙皮网格的动画角色的建议,而在Unity手册在SkinnedMeshRenderer组件的页面包含了能够提升性能的调整。除了这些页面的建议外,值得注意的是,网格蒙皮的成本增长是基于每一个顶点的;所以,在咱们的模型中使用更少的顶点能够减小必须完成的计算量。
  • 在某些平台上,蒙皮能够由GPU而不是CPU来处理。若是咱们在GPU上有很大的容量,这个选项可能值得一试。咱们能够在Player Settings中为current platform和quality target启用GPU蒙皮。
 

与渲染无关的主线程操做

理解许多与渲染无关的CPU任务发生在主线程是很重要的。这意味着,若是咱们在主线程上遇到了CPU限制,那么咱们能够经过减小在与渲染无关的任务上花费的CPU时间来提升性能。
例如,咱们的游戏可能在主线程上执行昂贵的渲染操做和昂贵的用户脚本操做,使咱们的CPU达到性能边界。若是咱们在不丢失视觉效果的状况下尽量地优化渲染操做,咱们就有可能下降本身脚本的CPU成本,从而提升性能。
 

若是咱们的游戏达到了GPU性能边界

若是咱们的游戏达到了GPU性能边界,首先要作的就是找出是什么致使GPU瓶颈。GPU的性能一般是受到填充率的限制,尤为是在移动设备上,可是内存带宽和顶点处理也会受到影响。让咱们检查一下这些问题,并了解致使这些问题的缘由,并学会诊断以及修复这些问题。
 

填充率 Fill rate

填充率是指GPU每秒能够渲染给屏幕的像素数。若是咱们的游戏受到填充率的限制,这意味着咱们的游戏试图在每一帧中绘制比GPU所能处理的更多的像素。
检查填充率是否致使咱们的游戏被GPU限制是很简单的:
  • 部署运行游戏并记录GPU时间。
  • 下降Player Settings中的显示分辨率。
  • 再次部署运行游戏。若是性能获得改善,那么填充率可能就是问题所在。
若是填充率是问题的缘由,有一些方法能够帮助咱们解决这个问题:
  • 片断着色器是着色器代码中告诉GPU如何绘制单个像素的部分。这段代码由GPU逐像素执行的,所以若是代码效率低下,那么性能问题就很容易堆积起来。复杂的片断着色器致使填充率问题的常见缘由;
    • 若是咱们的游戏使用内置的着色器,咱们应该尽量使用最简单和最优化的着色器来得到咱们想要的视觉效果。在分组为mobile的着色器是高度优化的;咱们应该尝试使用它们,看看这是否可以在不影响游戏外观的状况下提升性能。这些着色器是为移动平台设计的,可是它们适用于任何项目。在非移动平台上使用“mobile”着色器来提升性能是彻底能够的。若是它们提供了项目所需的视觉效果的话。
    • 若是咱们游戏中的对象使用Unity的标准着色器,理解Unity基于当前材质设置编译这个着色器是很重要的。只编译当前使用的特性。这意味着删除detail maps之类的特性能够获得更简单的片断着色器代码,从而大大提升性能。一样,若是咱们的游戏是这样的状况的,咱们应该尝试这么作,看看咱们是否可以在不影响视觉质量的状况下提升性能。
    • 若是咱们的项目使用定制的着色器,咱们应该尽量优化它们。优化着色器是一个复杂的主题,可是Unity手册的这一页这一页包含了优化着色器代码的参考。
  • 过分绘制(Overdraw)是指同一个像素被屡次绘制的状况。当对象被绘制在其余对象之上时就会发生这种状况,这对填充率问题有很大的影响,为了理解过分绘制,咱们必须理解Unity在场景中绘制对象的顺序。一个对象的着色器决定它的绘制顺序,一般是经过指定对象所在的渲染队列。Unity使用这些信息以严格的顺序绘制对象,详见Unity手册的这一页 。此外,在绘制不一样渲染队列中的对象以前,对它们进行不一样的排序。例如,Unity在Geometry队列中对其中对象进行从前到后排序,以最小化过分绘制,但在Transparent队列中对对象进行从后到前进行排序,以达到所需的视觉效果。这种从后到前的排序实际上能够最大化透明队列中对象的过分绘制。过分绘制是一个复杂的课题,没有一个大小合适全部的方法来解决过分绘制问题,可是减小Unity不能自动排序的重叠对象的数量是关键。最好从Unity的场景试图开始调查这个问题;有一个绘制模式,容许咱们看到咱们的场景过分绘制,并在那里,肯定咱们能够从这个地方入手来减小过分绘制。过分绘制最多见的罪魁祸首是透明材质、未优化的粒子和重叠的UI元素,因此咱们应该尝试优化或减小这些。Unity的学习网站上这篇主要有关Unity UI优化的文章,但也包含了关于过分绘制的通常性指导。
  • 使用image effects会大大增长填充率的问题,特别是若是咱们使用多个image effect。若是咱们的游戏使用了Image effect,而且在填充率问题上遇到了困难,咱们可能但愿尝试使用不一样的设置或image effect的更优化版本(如Bloom(Optimized)代替Boom)。若是咱们的游戏在同一个相机上使用多个image effect,这将致使多个着色器Pass。在这种状况下,把咱们的着色器代码和image effect合并到一个Pass中,好比在Unity's PostProcessing Stack 。若是咱们优化了image effect,但仍然存在填充率问题,咱们可能须要考虑禁用image effect,特别是在低端设备上。
 

存储带宽

内存带宽指的是GPU从其专用内存读取和写入数据的速率。若是咱们的游戏受到内存带宽的限制,这一般意味着咱们使用的纹理太大,GPU没法快速处理。
要检查内存带宽是否有问题,咱们能够执行如下操做:
  • 配置运行游戏并记录GPU时间。
  • Quality Settings中下降纹理质量。
  • 再次配置运行游戏并记录GPU时间。若是性能获得了改善,那么内存带宽可能就是问题所在了。
若是内存带宽是咱们的问题,咱们须要在游戏中减小纹理内存的使用。一样,对于每一款游戏最有效的技术也会有所不一样,可是咱们能够经过一些方式来优化咱们的纹理。
  • 纹理压缩是一种能够大大减小磁盘和内存中纹理大小的技术。若是咱们的游戏中内存带宽是一个问题,使用纹理压缩来减小内存中纹理的大小能够帮助提升性能。Unity中有不少不一样的纹理压缩格式和设置,每一个纹理均可以有单独的设置。通常来讲,只要有可能,就应该使用某种形式的纹理压缩;然而,尝试为不一样的纹理进行不一样的设置以达到最好的效果。Unity手册中关于不一样压缩格式和设置的文章。
  • Mipmaps是Unity中能够在远距离物体上使用的纹理的低分辨率版本。若是咱们的场景包含远离摄像机的对象,咱们可使用mipmaps来缓解内存带宽的问题。场景视图中的mipmaps绘制模式容许咱们查看场景中的哪些对象能够从mipmaps中受益,Unity手册中的这一页包含了更多关于为纹理启用Mipmaps的信息。
 
顶点处理
顶点处理是指GPU在网格中渲染每一个顶点时所必须作的工做。顶点处理的成本受到两个因素影响:必须渲染的顶点数量和必须在每一个顶点上执行的操做数量。
若是咱们的游戏达到了GPU性能边界,而且咱们已经肯定它不受填充率或内存带宽的限制,那么顶点处理极可能是问题的缘由。若是是这样,尝试减小GPU必须处进行的顶点处理的数量可能会带来性能的提升。
咱们能够考虑几种方法来帮助咱们减小顶点的数量或咱们在每一个顶点上执行的操做的数量:
  • 首先,咱们应该致力于减小没必要要的网格复杂性。若是咱们使用的网格具备在游戏中没法看到的细节,或者因为建立错误而具备太多顶点的低效率网格,这对GPU来讲是浪费工做。减小顶点处理成本的最简单的方法是在咱们的3D建模工具中建立顶点数较低的网格。
  • 咱们能够尝试一种叫作法线贴图的技术,在这种技术中纹理被用来在网格上建立更复杂的几何效果。尽管这种技术有一些GPU开销,但在许多状况下会带来性能的提升。Unity手册的这一页介绍了使用法线贴图来模拟复杂的几何网格
  • 若是咱们的游戏中的一个网格没有使用法线贴图,咱们一般能够在网格导入设置中禁用该网格的顶点切线。这减小了为每一个顶点发送到GPU的数据量。
  • 细节层次(LOD),是一种优化技术,其中原理摄像机的网格减小复杂性。这在不影响游戏视觉质量的状况下减小了GPU渲染顶点的数量。Unity手册中LOD页面包含了更多关于如何在游戏中设置LOD的信息。
  • 顶点着色器是告诉GPU如何绘制每一个顶点的着色器代码块。若是咱们的游戏受到顶点处理的限制,那么减小顶着色器的复杂性可能会有所帮助。
    • 若是咱们的游戏使用内置的着色器,咱们应该尽量使用最简单和最优化的着色器来得到咱们想要的视觉效果。例如,带有(mobile)的着色器是高度优化了的;咱们应该尝试使用它们,看看这是否可以在不影响游戏外观的状况下提升性能。
    • 若是咱们的项目使用定制的着色器,咱们应该尽量优化它们。优化着色器是一个复杂的主题,可是Unity手册的这一页这一页的着色器优化部分包含了优化着色器代码的有用信息。
 
总结
咱们已经学习了Unity中渲染是如何工做的,在渲染时会出现什么问题,以及如何在游戏中提升渲染性能。利用这些知识和咱们的分析工具,咱们能够修复与渲染相关的性能问题,并构建游戏架构,使它们拥有一个流畅高效的渲染管线。
下面的连接提供了关于本文主题的进一步详细信息:
相关文章
相关标签/搜索