Unity渲染优化教程
下面给大家分享的是Unity开发中的渲染优化,有不了解的渲染优化方法的都可以看看。
用Profiler工具得到最恐怖的数据:
CPU: Camera.Render : 85-90% Physical.Simulate:10% Memory: Total Objects In Scene : 90000左右 Textures : 1369/42.9MB Meshes: 12038 / 241.2MB Materials:22650 / 21.0MB GPU: Draw Calls : 22000 Triangles : 2.0M Vertexs: 2.4M Used Textures :108 / 16.8M FPS:7-8
看过上面的恐怖数据我们来看一个公式:
从NVIDIA公司的一篇分析3D引擎批次优化的讲稿中我们得到一个大概的FPS和DrawCall之间的公式:
25K * GHz(CPU)*PERCENT/FPS = DrawCall
GHz代表CPU的计算能力,PERCENT代表DrawCall耗费CPU计算性能的比例,FPS代表你所想要达到的帧率,则可以计算出DrawCall数量。
正文:
1.首先我们得知道到底是什么限制了运行的帧率?
2.因此我们从渲染的角度可以做出以下优化:
a. 批处理优化DrawCall数量 b. 减少模型的顶点数量 c. 减少模型的片元数量 d. 光照优化 e. 降低Shader的计算复杂度 f. 节省内存带宽
批处理优化DrawCall数量:
Unity的批处理分两种,一种是动态批处理,一种是静态批处理。
动态批处理,每一帧都会对合一批处理得模型进行网格合并,然后把数据提交GPU,使用同一个材质进行渲染,由于是每帧都会执行一次网格合并,因此动态批处理过的模型是可以自由移动的。反之静态批处理的模型是不可以自由移动的。
但是动态批处理有一些限制原因,比如多Pass的Shader会导致批处理的中断,再有就是顶点属性不能超过900个,也就是顶点不能超过300个。使用光照纹理的物体要保证它们指向光照纹理中的同一个位置才能被批处理。当然在了解原理的情况下,应该是可以自己编写相应的摆脱这个300个顶点的限制问题的。
静态批处理会在运行的开始阶段把设置为static的模型网格合并到一个新的网格中,因此在运行时,参与静态批处理得物体也就不能够移动了。再有一个缺点就是静态批处理会占用更多的内存空间,因为如果在静态批处理钱一些物体共享了网格,那么在运行后内存中每一个物体都会对应一个该网格的复制品,因此将一网格变成多个网格进行了一次DrawCall提交,导致内存空间的浪费。但是静态批处理中Shader中的Base Pass还是会被批处理,后续的处理其他非平行光的Pass则不会被批处理。
不论是动态批处理,还是静态批处理,都要求模型之间要使用相同的材质才能实现批处理,但是对与不同的纹理、颜色的模型,我们可以选择将它们的纹理合并到一张更大的纹理中,使用不同的UV坐标来采样纹理即可。
减少顶点数量:
a.建模时就要注意模型的三角面的数量,对于一些对模型没有影响或着不影响视觉效果的顶点,都要尽可能的清除掉。再有就是去掉不必要的棱角边和纹理的衔接,因为这样可以避免产生额外的顶点。 b.使用Unity提供的LOD Group组件,实现不同距距离对模型不同精度的展现,当然这样我们就得准备多个精度的模型。 c.使用Occlusion Culling(遮挡剔除)剔除那些在物体后面看不见的物件,这样就可以不用浪费更多的资源在在那些看不到的点行,以提升性能。但是我们应该讲它与相机设置中的Distance区分开来,Frustum Culling(视椎体剔除)是剔除不在相机视野范围之内的物体。
减少片元数量:
控制绘制顺序,写过Shader的人都知道Shader中有一个标签就是控制 Shader的渲染顺序的,决定了它在渲染时参与绘制的顺序。比如渲染队列为Opaque(不透明的)的物体,总是从前往后绘制的,由于深度测试(ZTEST)的存在,后面绘制的物体的可能就通不过深度测试而无法渲染,因此就避免很多的overdraw。而渲染队列为Transparent等透明队列的物体是从后往前渲染的就必定会存在overdraw的情况,因此我们也应该尽量避免使用透明队列。
减少实时光阴和阴影:
如果场景中有过多的光源,并且使用了多个Pass的Shader那么就有可能会造成性能下降,首先是多Pass的Shader可能影响到动态批处理的中断,导致DrawCall数量的提升,再就是如果如果使用了逐像素光照的光源,还使用了逐像素渲染的 Shader那么即便是静态批处理的模型也会因为多Pass的Shader而造成DrawCall数量的上升。因此我们可以通过烘焙生成lightmap,然后对纹理进行采样来模拟真实的效果。但是烘焙也只能对场景中的静态物体。所以在场景中不需要移动的物体设置为Static之后进行烘焙,然后对于动态物体再开启实时光影,综合降低性能消耗。
节省带宽:
1.降低纹理大小:这里我们建议纹理的长宽值最好是2的整数幂,因为很多优化只有这个时候才能发挥到最大值。我们还可以使用多级渐远纹理和纹理压缩技术来保证纹理像素不会过高也不会太低,因为纹理没必要精细到肉眼无法分辨,也没必要在视野很远的地方的纹理还采用了很高像素的纹理,这样我们就可以合理避免访问大量像素的计算。 2.尽可能的使用压缩纹理,并使用32bit的纹理。
降低Shader计算复杂度:
1.Shader LOD技术,在Shader 中设置LOD值之后,在渲染时,只有低于LOD设定值才会被渲染。在Edit->ProjectSettings->Quality下面有一个属性值Maximum LOD Level 默认为0 ,不进行限制,比如输入400,则Shader中LOD值高于400的统统不参与渲染。 2.在Shader代码片段中,我们应该尽可能把计算放在顶点着色器中,因为顶点的数量会比像素数量少很多,但是顶点着色器中某些渲染可能比片元着色器中渲染的效果要差一些。注意使用适当的数值类型,比如没必要使用float来存储uv,用half就足够,用fixed来替代float存储颜色值等。我们通常也可以采用一种掏巧的方式利用一个float4类型存储两个纹理的uv值,或是别的用法。尽量避免使用分支语句和循环语句,避免使用一些复杂的数学运算,比如Sin,Pow,Tan,Log等。
其他建议:
1.如果你是使用内置的着色器的话,从Mobile和Unlit类别中进行选择,他们是其他复杂着色器的近似版本。 2.最好只有一个定向的像素灯光,其他采用烘焙贴图。 3.场景中尽可能避免使用Fog雾。 4.如何场景远处包含几何物体,使用天空盒代替模型。
因此我将对我们的模型进行以下的优化:
1.就是在加载完模型之后,对不需要点击的模型利用程序进行网格合并,降低DrawCall数量。 2.考虑对模型进行光照烘焙,取代掉场景中的灯光,减少灯光上的资源消耗。 3.对模型的贴图进行细查,查贴图的尺寸,对可压缩贴图进行合理的压缩。 4.降低模型中棱角的存在(双倍顶点数量)。 5.对于需要参与点击的模型和人物碰撞的模型以及无需碰撞的模型进行细分,降低MeshCollider带来的消耗。 6.对于场景中用的几种Shader(Diffuse、Transparent)进行重写。