Unity游戏中的图形渲染优化


http://www.ceeger.com/forum/read.php?tid=594397&fid=2
技术文章翻译 原文链接  
Unity官方教程中的一篇文章,虽然讲得不深,但是比较系统全面,对寻找问题,优化性能还是有帮助的,分享给大家把。英语渣,翻译有啥不对的地方,欢迎指正。  


简介  

在这篇文章中,我们将学习:在Unity渲染一帧画面背后发生的事;在渲染过程中可能发生的 一些性能问题以及如何去解决这些和渲染相关的性能问题。在阅读这篇文章之前,我们要知道,没有一种通用的方法来提高渲染性能。渲染性能和很多因素相关,并取决于游戏运行的硬件和操作系统。我们要通过研究、尝试和严格分析实验结果来解决性能问题。这篇文件包含了一些常规的渲染性能问题和解决它们的方法,以及进阶阅读的链接。可能你遇到的问题并没有在这篇文件中讲到,但是它仍然能够帮助我们理解问题,给我们一些解决问题的知识和思路。  


渲染的简介  

在开始之前,我们先简单了解一下当Unity在渲染一帧图像时发生了什么。理解事件流和术语有助于我们理解,研究和解决性能问题。 
注意:在这篇文章中,我们使用术语"object"表示一个游戏中可能被渲染的对象。任何一个有Render脚本的GameObject将被称为一个object。
 
简单来说,渲染有下面几个步骤:  
  • 中央处理器器(CPU),决定哪些对象需要渲染以及要怎么渲染。
  • CPU发送指令给图像处理器(GPU)。
  • GPU根据CPU指令进行渲染。
我们接下来将更加详细的介绍每一步发生的事。但在那之前,让我们熟悉一下我们用到的词语以及CPU和GPU在渲染过程中扮演的不同角色。 有一个描述渲染的常用词语叫渲染管道(the rendering pipeline)。这是一个很形象的词,高效的渲染就是让信息像水一样流动起来。  
渲染一帧,CPU需要完成以下工作:  
  • CPU检测场景中的每一个物件,决定它是否需要被渲染。一个object只有达成某些特定的条件才能被渲染。例如,object的包围盒的一部分必须在摄像机的视锥体中。一个object被裁剪后也不会被渲染。
  • CPU收集每一个需要渲染的的object的信息,并将它们按顺序生成名为 draw calls 的命令。一个 draw call 包含了一个模型和如何渲染这个模型的信息。例如,使用哪一张贴图。在特定的情况下,使用同一种设置的objects将被合并成一个draw call。将不同的objects合并成一个draw call 被叫做合批(batching)。 CPU为每一个draw call创建个一个名为batch的数据包,数据包可能还会包含一些除了drawcalls的额外数据,但是这些一般和性能问题关系不大,所以在这里暂不讨论。
为每一个drawcall,CPU将做如下工作:  
  • CPU将发送一个命令给GPU,告诉GPU改变某些被称为渲染状态( render state )的变量。这个命令变为称为( SetPass call)。一个SetPass call告诉GPU,下一个模型将使用什么设置来渲染。只有当下一个模型所需要的render state和前一个模型不应的时候,SetPass Call才会被发送。
  • CPU发送draw call给GPU。GPU使用最近的一次SetPass call设置的渲染状态,渲染draw call中的模型。
  • 在某些情况下,一个 batch 可能有多个 pass。pass是shader代码中的一个片段。一个新的pass需要改变渲染状态。对batch中的每一个pass,CPU需要发送一个新的SetPass call和draw call。
同时,GPU进行如下工作:  
  • GPU按顺序处理CPU发送过来的任务。
  • 如果当前任务是SetPass call,GPU更新render state。
  • 如果当期任务是Draw call,GPU渲染模型。这一步在shader code中分为不同的阶段。由于比较复杂,这篇文件中不更深入的讲解。但是我们有必要知道的是,shader代码中名为vertex shader的部分告诉GPU处理模型的顶点,名为fragment shader的部分告诉GPU处理像素。
  • 一直重复上面的操作直到所有的CPU请求被处理。
现在我们直到了Unity渲染一帧具体进行了哪些操作,现在可以来看一下在渲染过程中可能会出现哪些问题。  


渲染问题类型  

最重要的是:只有当CPU和GPU的任务全部完成,才能完成一帧的渲染。只要任何一个任务花费了过长的时间,将会拖慢这一帧的渲染。 
渲染问题有两个基本的原因。第一,低效率的管线。当渲染管线中的一步或者多步需要花费较长时间,破坏了渲染数据流的流畅运行。通常被称之为渲染瓶颈。第二,是给渲染管线传递了大量数据。即使高效的管线在一帧中处理的数据也有上限。 
当因为CPU任务耗时较长导致降帧,我们称之为CPU问题(CPU bound).当因为GPU任务耗时较长,我们称之为GPU问题(GPU bound)。
 


查找渲染问题  

在作出任何改变前,使用Profiling工具来分析性能问题的原因是十分重要。不同的问题需要不同的解决方法。衡量每次改动带来的性能提升也很重要。性能优化是一个需要衡量得失的行为,因为你对某一个性能的优化可能带来其他地方的性能消耗。 
我们将使用两个工具来发现和修正我们的渲染性能问题:Profiler和Frame Debugger.两个工具都是Unity自带的。
 


Profiler  

在Profiler窗口中,我们可以看到游戏性能的实时数据,包括内存使用,渲染管线和脚本性能。 如果你不熟悉Profiler,可以查看Unity手册这篇教程  


The Frame Debugger  

通过The Frame Debugger,我们可以知道一帧是怎么一步步渲染的。可以看到每一个draw call,draw call的着色器数据,发送给GPU事件顺序。这些信息可以帮助我们了解我们的游戏是怎么渲染的,我们可以在哪些地方优化他们。 
如果你不熟悉Frame Debugger,可以查看手册教程
 


查找渲染问题的原因  

在进行渲染优化之前,我们需要确定的是确实是由于渲染导致游戏变慢。如果是你的脚本过于复杂到时游戏满,进行渲染优化是没有任何意义的。如果你不确定你的性能问题是不是和渲染有关,可以查阅这篇教程。 
一旦我们确定是由于渲染导致的性能问题,我们还要确定到底是CPU阻塞还是GPU阻塞。不同的问题需要不同的解决方案,所以在动手之前确定还问题是非常重要的。如果你不确定到底是CPU问题还是GPU问题可以查看这篇教程 
如果我们已经确定所有事情,那么可以继续了。
 


CPU阻塞  

简单来说,CPU承担的渲染问题主要可以分为三类:  
  • 决定要渲染什么
  • 为GPU准备渲染命令
  • 将命令发送给GPU
这些大类里面包含了很多小任务,这些任务可能在不同的线程内执行。线程运行任务同时执行;如果一个线程执行一个任务,另外一个线程执行另外一个完全独立的任务,这样可以提高工作效率。这种渲染任务被分配到不同线程的做法,我们称之为多线程渲染。  
在Unity渲染过程中,有三种不同的线程:主线程、渲染线程、工作线程。主线程承担游戏的大部分CPU任务,包括一些渲染任务。渲染线程专门用来给GPU发送渲染指令。工作线程主要用来剔除或模型蒙皮。哪一种任务由什么线程承担取决于游戏设置和硬件。例如,如果我们的CPU有多核,那么就会创建多个工作线程。因此,在目标硬件上分析游戏性能是非常必要的,因为我们的游戏在不同的硬件上性能表现可能会有很大差别。  
因为多线程非常复杂而且硬件相关。我们在优化之前需要知道是什么任务导致我们CPU阻塞。如果是因为剔除操作,我们优化发送渲染命令的时间是没有用的。因为他们属于不同的线程。  
注意:并不是所有平台都支持多线程渲染。在写这篇文章的时候,WebGL就不支持这个特性。如果某平台不支持多线程渲染,所有的CPU任务将在一个线程内处理。如果在这个平台上发生CPU阻塞,那么优化任何一项CPU任务都会提高CPU性能。如果你的游戏属于这个平台,那么应该阅读下面的所有章节,来旋转最适合你游戏的优化方案。  


图像工作(Graphics jobs)  

Graphics jobs选项在Player Settings中,用来决定Unity是否使用工作线程来承担渲染任务。如果不勾选此项,这些工作将由主线程或渲染线程承担。如果平台支持这个选项,它将会带来较大的性能提升。如果我们希望开启这个特性,我们应该对比开启和不开启的情况的性能表现。  


找出导致性能问题的任务  

我们可以通过Profiler窗口来定位什么任务导致了CPU阻塞。这篇[url=" style=]文章[/url]告诉我们如何定位问题所在。 
如果我们已经知道了什么任务导致CPU阻塞,让我们看看一些常见的问题和解决方法。
 


发送命令给GPU  

发送渲染命令给GPU流程可能是导致CPU阻塞的一个常见原因。这项任务通常在很多平台上由渲染线程承担,但在一些特定平台(如PS4)也可能被工作线程承担。  
在发送渲染命令过程中,消耗最大的是SetPass call。如果你的CPU阻塞是因为发送渲染命令导致的,降低SetPass call的数量将是提高性能的最好方法。  
我们可以在Profiler窗口中的Rendering profiler栏中看到SetPass calls和batches数量。SetPass calls的上限由硬件决定;高配的PC肯定比移动端能够承受更高的SetPass Call数量。  
SetPass Calls的数量和与之相关的batches数量和很多因素相关,我们将更详细的讨论这些问题。不过通常是这样的:  
  • 降低batches数量或者让更多的objects共用材质球,会降低SetPass Calls数量。
  • 降低SetPass Calls数量,在绝大多数情况下,会提升CPU性能。
如果降低Batches数量并没有降低SetPass calls数量。它仍然能够带来性能提升,尽管可能渲染的模型数量一样,但是合并成一个batch比多个batch更加高效。 
概括的来说有三个方法降低batches和SetPass calls的数量。我们将分别进行详细说明:
 
  • 降低渲染objects的数量,可以同时降低batches和SetPass calls。
  • 降低每个object渲染次数将有可能降低SetPass calls。
  • 将需要渲染的object合并成更少的batches
不同的技术使用不同的游戏,因此我们应该考虑所有的操作,决定哪一个适合我们自己并进行尝试。  


降低需要渲染的objects的数量  

降低需要渲染的objects的数量是降低batches和SetPass calls最简单的方法。下面是一些我们可以使用的技术。  
  • 直接减少场景中的激活objects是一个有效的办法,例如,我们渲染了一群各种各样的人物,我们可以尝试更少的角色,如果场景看起来还是不错,性能也得到了提升。这是一个比使用复杂技术更快捷的解决方案。
  • 我们可以通过摄像机的远截面(Far Clip Plane)来降低摄像机的渲染距离。这个参数的意思是离摄像机多远的objects将不再被渲染。如果我们想被摄像机剔除的objects不被发现,可以是用雾效进行遮挡。
  • 通过距离来隐藏objects,有更细粒度的方法。就是使用摄像机的 Layer Cull Distance属性。它针对不同layers的物体,可以设置不同的剔除距离。我们可以把很多细小的近景装饰细节的剔除距离设置的比较近,而一些地表元素的剔除距离设置得较远。
  • 我们可以使用一个名为遮挡剔除(occlusion culling)的技术,来隐藏那些被其他objects遮挡的objects。例如,如果我们的场景中有一个的建筑物,我们可以通过遮挡剔除来讲建筑后面的物件剔除,不再渲染他们。Unity的遮挡剔除并不适用于所有场景。它会导致一些额外的CPU消耗并且设置起来比较复杂。但是在某些场景中,它可以显著提升性能。这篇文章非常好的讨论了这个话题。处理使用Unity的遮挡剔除,我们可以实施我们自己的遮挡剔除,手动禁用那些不会被玩家看到的objects,例如我们的场景中有一些在过场动画中用到的objects,我们应该在动画开始前和结束后禁用他们。在游戏中使用我们自己的知识永远比让Unity自动动态工作来得有效。


降低objects的渲染次数  

实时光照,阴影,反射让游戏变得更写实,但是他们的性能消耗非常大。使用这些特效会让objects渲染多次,会极大的影响性能。  
游戏渲染路径(Rendering path)的选择也影响这些特性对性能的消耗。Rendering path是一个在场景渲染是计算执行顺序的术语。不同的Rendering Path主要就是处理实时光照、阴影和反射的处理方式不一样。一般说来,如果我们的游戏有大量的实时光照、阴影和反射并允许在高端硬件上,延迟渲染(Deferred Rendering)是更好的选择。如果我们的游戏允许在低端硬件,并且并没有用到上面的特性,就应该旋转正向渲染(Forward Rendering)。然而这时一个很复杂的问题,如果我们想使用实时光照、阴影和反射,我们最好深入研究并尝试。这篇文章提供了更多关于渲染路径的信息。这篇教程讲到了Unity中光照的问题。  
不管我们选择什么渲染路径,实时光照、阴影和反射的使用都会影响游戏的性能,直到怎么优化他们是十分重要的。  
  • Unity中的动态光照很复杂,这里就不讨论了,这篇教程可以帮助你很好的了解它。通用这篇Unity手册也讲到了灯光优化。
  • 动态光照非常耗性能。如我们的场景中有很多不移动的objects,例如地图,我们可以使用烘焙(baking)预习计算场景中的光照。这篇教程介绍了这项技术。
  • 如果我们希望在游戏中使用实时光照。这将是可以提升性能的部分。这篇手册中介绍了可以在质量设置(Quality Settings)中可以调整的阴影参数和这些参数带来的效果和性能上的影响。例如,我们可以使用Shadow Distanc属性来确定只有里的很近的物体才投射阴影。
  • 反射探针(Reflection probes)可以实现真实反射,但是却非常耗性能。如果性能对我们很重要,应该尽量减少探针的使用,并尽量优化它们。这篇手册讲到了如何优化反射探针。


合并Objects  

当多个objects满足一定条件时,它们将被合并成一个批次。为了合批,objects必须:  
  • 使用同一个材质球
  • 使用同样的材质球设置(贴图,shader和shader参数)
将满足条件的objects合批可以提高性能,但是就想所有优化技术一样,我们应该仔细分析合批的消耗和性能提升。 这里有一些合批的不同技术:  
  • 静态合批(Static batching) 将那些不会移动的满足条件的objects合并成一个批次。一个较好的例子就是将一堆相同物件例如石头合并在一起。由于静态合批会带来额外的内存消耗,所以在使用过程中,应该分析得失。
  • 动态合批(Dynamic batching)是另外一个合批技术。它并不关心objects是否移动。但是有一些其他限制。在这篇文章中可以找到这些限制和用法说明。动态合批会导致一些CPU开销,我们应该谨慎的使用这项技术,并多实验,分析它的实际效果。
  • 将UI元素和批比较复杂,因为它会影响我们的UI布局。可以查看这个视频(youtube)这篇文件来了解更多。
  • GPU instancing使用将大量相同的objects高效合批的技术。它的使用有很多限制,并且不支持全平台。但是如果我们的游戏需要同时渲染大量相同的模型,这些技术将非常有用。这篇手册介绍了GPU instancing是什么、怎么使用它,以及什么情况下它会给我们的游戏带来性能提升。
  • 图集(Texture atlasing),将多张小纹理打包成一张大尺寸纹理。它经常用于2D游戏和UI界面,不过同样也可以用于3D游戏。如果我们使用这项技术,我们可以确保多个objects使用同一张贴图,进而满足合批条件。Unity内置了一个图集工具Sprite Packer。
  • 我们还可以通过Unity编辑器或者代码,将那些使用相同材质球和贴图的模型进行手动合并。如果我们使用这种方式合并模型,我们需要知道的是,光照,阴影和剔除都会按照一个模型进行处理。也就是说它带来的性能提升可能会因为它不再被剔除带来的消耗抵消掉。如果想使用这项技术,可以查看这个函数Mesh.CombineMeshesUnity’s Standard Assets package中有一个名为CombineChildren的例子。
  • 在代码中我们需要很谨慎的使用Render.material。这将会将材质球复制一份并返回新拷贝的引用。如果这个object原来被合批,那么这个操作将会破坏合批操作。因为材质球的引用不再指向同一个了。如果我们想处理被合批的object的材质球,我们应该使用Renderer.sharedMaterial。


剔除,排序和合批  

剔除,收集需要渲染的objects的数据,将这些数据进行排序并打包和生成GPU命令都会引起CPU阻塞。这些任务可能在主线程或独立的工作线程上执行。依游戏设置和目标硬件而定。  
  • 剔除本身并不是非常耗效率,但是为了性能还是应该减少不必要的剔除。这是一个乘法算法,也就是说性能消耗等于 objects个数乘以摄像机个数。所以我们应该将那些用不到的摄像机和渲染对象禁用。
  • 合批可以显著提高向GPU发送命令的速度,但是有时候它也会带来一些意想不到的开销。如果合批导致了CPU阻塞,我们需要降低手动合批和自动合批的数量。


蒙皮模型(Skinned meshes)  

SkinnedMeshRenderers 当我们使用骨骼动画技术来控制模型的动作和变形的时候,我们会用到SkinnedMeshRenderers。它常常被用在会动的角色身上。根据硬件和设置的不同,渲染skinned mesh的任务通常由主线程或单独的工作线程承担。  
渲染skinned meshes是一个开销较大的操作。如果在Profiler窗口中发现,渲染skinned meshes导致了CPU阻塞,我们可以通过以下几点来优化:  
  • 首先我们要确定这个object是否需要SkinnedMeshRenderer。因为有可能我们的模型并不需要有动作,但是被错误的绑定了SkinnedMeshRenderer组件。如果是这种情况,我们使用MeshRender组件来代替SkinnedMeshRenderer。在模型导入设置中,如果我们不勾选import animations选择,导入时Unity会使用MeshRender代替SkinnedMeshRenderer。
  • 如果我们只需要在某些时候让模型有动作。(例如,只是在开始的时候或者是离摄像机一定距离的时候),我可以选择低细节的模型或者将SkinnedMeshRender替换为MeshRender。在SkinnedMeshRenderer组件中有一个BakeMesh函数,可以创建某个合适动作下的模型。这在切换不同的模型和渲染组件的时候非常有用。
  • 这篇手册提出了针对有蒙皮的有动作的角色的优化方案。这篇手册提到了哪些设置可以提高性能。另外每一个模型顶点都会带来开销。所以给模型减少顶点也是必须做的优化。
  • 在某些平台上,skinning可以在GPU上处理。如果我们的GPU还有性能空间,可以尝试开启这个选择。这个选择在PlayerSetting窗口中。


主线程的操作和渲染无关  

我们需要知道的时候很多CPU任务和渲染没有关系,也就是说如果发生在主线程发生CPU阻塞,我们可以通过优化那些和渲染无关的部分,来降低CPU消耗。  
举个例子,我们的游戏中,脚本和渲染都消耗了大量的CPU时间,但是我们在不降低画质的前提下做了大量优化工作,性能仍然不达标,这样我们可以尝试优化我们的脚本来提高性能。  


GPU阻塞  

第一件事就是找出到底是什么导致了GPU瓶颈。GPU性能通常被填充率(fill rate)限制。在移动设备上尤其如此。另外还有存储带宽和低昂点处理。让我们逐一研究它们并学习是什么导致问题,怎么发现他并解决它。  


填充率(Fill rate)  

填充率是指每秒钟GPU渲染到屏幕上的像素的数量。如果我们游戏的帧率被填充率限制,说明我们每帧需要在渲染的像素超过了GPU的能力。  
要检查是否是填充率导致了GPU阻塞非常简单:  
  • 在Profile窗口中记录GPU时间
  • 在PlayerSettings中降低游戏的分辨率
  • 再次打开Profile窗口查看,如果性能提高,就有可能是填充率的问题。
如果是填充率过高的问题,这里有一些方法可以帮助我们解决它。  


片段着色器(Fragment shader)  

片段着色器是Shader中的一部分。它用来告诉GPU如何渲染一个像素。GPU在渲染每一个像素的时候都会执行它,因此如果片段着色器代码非常低效就很容易产生性能问题。复杂的片段着色器代码是导致填充率问题的重要原因。  
  • 如果我们用的是内置Shader,我们应该在效果允许的情况下使最简单的和优化过的Shader。例如The mobile shaders that ship with Unity就是经过高度优化过的。我们可以尝试使用它们,并分析它们是否对性能和效果有影响。这些Shader为移动端设计。但是它们可以用在很多项目。只要效果可以接受,我们可以在非移动平台使用移动平台的Shader来提高性能,完全没问题。
  • 如果object使用的是Unity的Standard Shader,Unity是通过材质球的设置来编译这个Shader的,只有被使用的特性才会被编译。也就是说移除某些特性例如Detail maps将会减少片段着色器的复杂度,这将会对性能有很大提升。因此我们应该尝试材质球选项,并进行测试,判断他们对性能和效果的影响。
  • 如果我们的项目用的是自定义的着色器。我们应该尽量优化他们。优化Shader是一个很复杂话题,可以查看着两篇文章文章1文章2


Overdraw  

Overdraw表示一个像素被渲染多次的术语。当一个object被渲染在其他object上面时,Overdraw就会发生。它也是导致填充率过高的一个因素。为了理解Overdraw,我们需要先了解Unity在场景中渲染objects的顺序。一个object的渲染顺序(draw order)通常由这个object的shader中的渲染序列决定(render queue)。处于不同渲染队列中的物体,有不同的排序方式。例如,对于Geometry队列中的对象为了减少overdraw,从前往后排序。但在Transparent队列中的对象为了视觉效果,采用的是从后往前排序。从后往前排序会导致overdraw达到最大。Overdraw是一个很复杂的问题,所以这里没有一个通用方法解决所有overdraw问题。但是降低叠加objects的数量是关键。查找这个问题最好的方式是在Unity编辑器的场景视图中,有一个DrawMode模式,通过它可以让我们查看场景中的overdraw情况,并决定可以对哪些地方进行优化。导致overdraw过高的罪魁祸首通常是透明材质,没有经过优化的粒子和互相堆叠的UI控件,因此我们要对这些元素尝试优化或者删减。这篇文章主要讲UI,不过也涉及到了Overdraw,可以参考一下。  


Image effects  

使用后处理(ImageEffects)也是导致填充率过高的一个重要因素,特别是当我们使用多个Image Effect的时候。如果我们的游戏确定要使用Image Effect,并且遇到了填充率问题。我们应该尝试使用不同的设置或者使用更优化的Image Effect版本(例如用Bloom(Optimized)替换Bloom))。如果在同一个摄像机上使用了多个Image Effect,将会导致有多个Shader passes。在这种情况下,将多个着色器代码合并到一个Pass中将会显著提高性能。如果优化后仍然有填充率问题,我们可以考虑禁用Image Effect,特别是在一些低端设备上。  


存储带宽(Memory bandwidth)  

存储带宽是指GPU读取和写入专有内存的速度。如果我们的游戏被存储带宽限制,通常表明我们使用的贴图尺寸超过了GPU的处理能力。 快速检测是否是存储带宽问题的方法:  
  • Profile查看GPU时间
  • 在QualitySettings中降低贴图质量(Texture Quality)
  • 再次在Profile中查看GPU时间,如果性能显著提高,说明存储带宽可能有问题
如果存储带宽是主要问题,我们应该降低贴图的内存使用。同样,降低贴图内存的技术在各个平台上由较大不同,但是我们有一些方法可以优化我们的贴图。  
  • 图片压缩(Texture compression) 可以帮助我们在硬盘和内存中大大减少贴图大小。如果渲染带宽是主要问题,使用贴图压缩降低贴图尺寸对提高性能有很大帮助。Unity内置了多种不同纹理压缩格式和设置。每一张图片都可以使用不同的设置。通用的一个规则是,我们应该尽可能使用多种纹理格式来满足不同需求,不断的尝试是找到最佳设置的方法。这里包含了一些关于压缩格式和设置的信息。
  • Mipmaps 对较远的objects使用低分辨率的贴图。如果我们的场景中有大量远离摄像机的objects,我们可以使用mipmaps来缓解存储带宽压力。编辑器场景视图中The Mipmaps Draw Mode模式下,我们可以查看场景中哪些objects使用mipmaps。这篇手册提供了更多关于贴图mipmaps的信息。


顶点处理(Vertex processing)  

顶点处理是指GPU渲染模型中的每个顶点需要做的工作。顶点处理的消耗主要和两个因素相关:渲染顶点的数量和每个顶点需要进行的操作数量。  
如果我们的游戏有GPU阻塞,但是又确定不是填充率和存储带宽导致的,那就有可能是顶点处理导致了问题。在这种情况下尝试降低GPU处理的顶点数量。  
这里有一些帮助我们降低顶点数量或者是降低对每个顶点的操作数的方法。  
  • 首先,我们应该降低一些不不要的模型复杂度。如果一个模型的某些细节在游戏中根本看不出来或者在制作过程中错误的用了过多的顶点,那么就是浪费了GPU的工作量。这是最简单的降低顶点处理的方法就是通过3D美术软件重新制作低顶点数的模型。
  • 我们可以使用法线贴图技术,通过它我们可以让模型看起来有更多的几何细节。尽管这项技术可能会对GPU带来额外的性能开销。但在很多情况下,总的来说性能会得到优化。这篇文章讲到了如何在模型上使用法线贴图。
  • 如果确定我们的模型不使用法线贴图。我们可以在import settings设置中关闭模型的顶点的切线信息。这将减少传递给GPU的顶点数据的大小。
  • 细节层级(Level of detail LOD),当objects离摄像机较远时,使用较低复杂度的模型。这项技术非常有效,并且它不会影响游戏的视觉效果。这篇手册提供LOD的更多信息。
  • 顶点着色器(Vertex shaders)告诉GPU如何渲染每一个顶点。如果游戏受限于顶点处理,可以降低顶点着色器的复杂度。 
    如果使用的是内置Shader,在不影响表现效果的情况下我们应该使用最简单的或者是优化过的Shader。例如The mobile shaders that ship with Unity就是经过高度优化过的。我们可以尝试使用它们,并分析它们是否对性能和效果有影响。 
    如果我们的项目用的是自定义的着色器。我们应该尽量优化他们。优化Shader是一个很复杂话题,可以查看着两篇文章文章1文章2


总结  

我们学习了Unity中渲染是如何进行的,在渲染过程中会产生哪些问题并如何优化他们。使用这些知识和分析工具,我们可以解决因为渲染导致的性能问题并创建一个有着平顺高效的渲染管线的游戏。  
下面的连接提供了这篇文章讨论的内容的更多信息。  


资料  

Unity Learn: A guide to optimizing Unity UI 
Unity Knowledge Base: Why is my static batching breaking or otherwise not working as expected? 
Fabian Giesen: A trip through the graphics pipeline 
Simon Schreibt: Render hell 
Gamasutra: How to choose between Forward or Deferred rendering paths in Unity 
Gamasutra: Batching independently moving GameObjects into a single mesh to reduce draw calls 
FlameBait Games: Optimizing SkinnedMeshRenderers for Unity 5 
Pencil Square Games: Reducing draw calls (also named SetPass calls) in Unity 5

猜你喜欢

转载自blog.csdn.net/sun1018974080/article/details/79558620