Unity3D 最简单的优化建议
1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU。
2.如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unlit目录下的。它们更高效。
3.尽可能共用材质。
4.将不需要移动的物体设为Static,让引擎可以进行其批处理。
5.尽可能不用灯光。
6.动态灯光更加不要了。
7.尝试用压缩贴图格式,或用16位代替32位。
8.如果不需要别用雾效(fog)
9.尝试用OcclusionCulling,在房间过道多遮挡物体多的场景非常有用。若不当反而会增加负担。
10.用天空盒去“褪去”远处的物体。
11.shader中用贴图混合的方式去代替多重通道计算。
12.shader中注意float/half/fixed的使用。
13.shader中不要用复杂的计算pow,sin,cos,tan,log等。
14.shader中越少Fragment越好。
15.注意是否有多余的动画脚本,模型自动导入到U3D会有动画脚本,大量的话会严重影响消耗CPU计算。
16.注意碰撞体的碰撞层,不必要的碰撞检测请舍去。
1.使用assetbundle,实现资源分离和共享,将内存控制到200m之内,同时也可以实现资源的在线更新
2.顶点数对渲染无论是cpu还是gpu都是压力最大的贡献者,降低顶点数到8万以下,fps稳定到了30帧左右
3.只使用一盏动态光,不是用阴影,不使用光照探头
粒子系统是cpu上的大头
4.剪裁粒子系统
5.合并同时出现的粒子系统
6.自己实现轻量级的粒子系统
animator也是一个效率奇差的地方
7.把不需要跟骨骼动画和动作过渡的地方全部使用animation,控制骨骼数量在30根以下
8.animator出视野不更新
9.删除无意义的animator
10.animator的初始化很耗时(粒子上能不能尽量不用animator)
11.除主角外都不要跟骨骼运动apply root motion
12.绝对禁止掉那些不带刚体带包围盒的物体(static collider )运动
NUGI的代码效率很差,基本上runtime的时候对cpu的贡献和render不相上下
13每帧递归的计算finalalpha改为只有初始化和变动时计算
14去掉法线计算
15不要每帧计算viewsize 和windowsize
16filldrawcall时构建顶点缓存使用array.copy
17.代码剪裁:使用strip level ,使用.net2.0 subset
18.尽量减少smooth group
19.给美术定一个严格的经过科学验证的美术标准,并在U3D里面配以相应的检查工具
1.为什么需要针对CPU(中央处理器)与GPU(图形处理器)优化?
CPU和GPU都有各自的计算和传输瓶颈,不同的CPU或GPU他们的性能都不一样,所以你的游戏需要为你目标用户的CPU与GPU能力进行针对开发。
2.CPU与GPU的限制
GPU一般具有填充率(Fillrate)和内存带宽(Memory Bandwidth)的限制,如果你的游戏在低质量表现的情况下会快很多,那么,你很可能需要限制你在GPU的填充率。
CPU一般被所需要渲染物体的个数限制,CPU给GPU发送渲染物体命令叫做DrawCalls。一般来说DrawCalls数量是需要控制的,在能表现效果的前提下越少越好。通常来说,电脑平台上DrawCalls几千个之内,移动平台上DrawCalls几百个之内。这样就差不多了。当然以上并不是绝对的,仅作一个参考。
往往渲染(Rendering)并不是一个问题,无论是在GPU和CPU上。很可能是你的脚本代码效率的问题,用Profiler查看下。
关于Profiler介绍:http://docs.unity3d.com/Documentation/Manual/Profiler.html
需要注意的是:
在GPU中显示的RenderTexture.SetActive()占用率很高,是因为你同时打开了编辑窗口的原因,而不是U3D的BUG。
3.关于顶点数量和顶点计算
CPU和GPU对顶点的计算处理都很多。GPU中渲染的顶点数取决于GPU性能和SHADER的复杂程度,一般来说,每帧之内,在PC上几百万顶点内,在移动平台上不超过10万顶点。
CPU中的计算主要是在蒙皮骨骼计算,布料模拟,顶点动画,粒子模拟等。GPU则在各种顶点变换、光照、贴图混合等。
【个人认为,具体还是看各位的项目需求,假设你项目的是3d游戏。你游戏需要兼容低配置的硬件、流畅运行、控制硬件发热的话,还要达到一定效果(LIGHTMAP+雾效),那么顶点数必定不能高。此时同屏2W顶点我认为是个比较合适的数目,DRAWCALL最好低于70。另,控制发热请控制最高上限的帧率,流畅的话,帧率其实不需要太高的。】
4.针对CPU的优化——减少DRAW CALL 的数量
为了渲染物体到显示器上,CPU需要做一些工作,如区分哪个东西需要渲染、区分开物体是否受光照影响、使用哪个SHADER并且为SHADER传参、发送绘图命令告诉显示驱动,然后发送命令告诉显卡删除等这些。
假设你有一个上千三角面的模型却用上千个三角型模型来代替,在GPU上花费是差不多的,但是在CPU上则是极其不一样,消耗会大很多很多。为了让CPU更少的工作,需要减少可见物的数目:
a.合并相近的模型,手动在模型编辑器中合并或者使用UNITY的Draw call批处理达到相同效果(Draw call batching)。具体方法和注意事项查看以下链接:
Draw call batching : http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html
b.在项目中使用更少的材质(material),将几个分开的贴图合成一个较大的图集等方式处理。
如果你需要通过脚本来控制单个材质属性,需要注意改变Renderer.material将会造成一份材质的拷贝。因此,你应该使用Renderer.sharedMaterial来保证材质的共享状态。
有一个合并模型材质不错的插件叫Mesh Baker,大家可以考虑试下。
c.尽量少用一些渲染步骤,例如reflections,shadows,per-pixel light 等。
d.Draw call batching的合并物体,会使每个物体(合并后的物体)至少有几百个三角面。
假设合并的两个物体(手动合并)但不共享材质,不会有性能表现上的提升。多材质的物体相当于两个物体不用一个贴图。所以,为了提升CPU的性能,你应该确保这些物体使用同样的贴图。
另外,用灯光将会取消(break)引擎的DRAW CALL BATCH,至于为什么,查看以下:
Forward Rendering Path Details:
http://docs.unity3d.com/Documentation/Components/RenderTech-ForwardRendering.html
e.使用相关剔除数量直接减少Draw Call数量,下文有相关提及。
5.优化几何模型
最基本的两个优化准则:
a.不要有不必要的三角面。
b.UV贴图中的接缝和硬边越少越好。
需要注意的是,图形硬件需要处理顶点数并跟硬件报告说的并不一样。不是硬件说能渲染几个点就是几个点。模型处理应用通展示的是几何顶点数量。例如,一个由一些不同顶点构成的模型。在显卡中,一些集合顶点将会被分离(split)成两个或者更多逻辑顶点用作渲染。如果有法线、UV坐标、顶点色的话,这个顶点必须会被分离。所以在游戏中处理的实际数量显然要多很多。
6.关于光照
若不用光肯定是最快的。移动端优化可以采用用光照贴图(Lightmapping)去烘培一个静态的贴图,以代替每次的光照计算,在U3D中只需要非常短的时间则能生成。这个方法能大大提高效率,而且有着更好的表现效果(平滑过渡处理,还有附加阴影等)。
在移动设备上和低端电脑上尽量不要在场景中用真光,用光照贴图。这个方法大大节省了CPU和GPU的计算,CPU得到了更少的DRAWCALL,GPU则需要更少顶点处理和像素栅格化。
Lightmapping : http://docs.unity3d.com/Documentation/Manual/Lightmapping.html
7.对GPU的优化——图片压缩和多重纹理格式
Compressed Textures(图片压缩):
http://docs.unity3d.com/Documentation/Components/class-Texture2D.html
图片压缩将降低你的图片大小(更快地加载更小的内存跨度(footprint)),而且大大提高渲染表现。压缩贴图比起未压缩的32位RGBA贴图占用内存带宽少得多。
之前U3D会议还听说过一个优化,贴图尽量都用一个大小的格式(512 * 512 , 1024 * 1024),这样在内存之中能得到更好的排序,而不会有内存之间空隙。这个是否真假没得到过测试。
MIPMAps(多重纹理格式):
http://docs.unity3d.com/Documentation/Components/class-Texture2D.html
跟网页上的略缩图原理一样,在3D游戏中我们为游戏的贴图生成多重纹理贴图,远处显示较小的物体用小的贴图,显示比较大的物体用精细的贴图。这样能更加有效的减少传输给GPU中的数据。
8.LOD 、 Per-Layer Cull Distances 、 Occlusion Culling
LOD (Level Of Detail) 是很常用的3D游戏技术了,其功能理解起来则是相当于多重纹理贴图。在以在屏幕中显示模型大小的比例来判断使用高或低层次的模型来减少对GPU的传输数据,和减少GPU所需要的顶点计算。
摄像机分层距离剔除(Per-Layer Cull Distances):为小物体标识层次,然后根据其距离主摄像机的距离判断是否需要显示。
遮挡剔除(Occlusion Culling)其实就是当某个物体在摄像机前被另外一个物体完全挡住的情况,挡住就不发送给GPU渲染,从而直接降低DRAW CALL。不过有些时候在CPU中计算其是否被挡住则会很耗计算,反而得不偿失。
以下是这几个优化技术的相关使用和介绍:
Level Of Detail :
http://docs.unity3d.com/Documentation/Manual/LevelOfDetail.html
Per-Layer Cull Distances :
http://docs.unity3d.com/Documentation/ScriptReference/Camera-layerCullDistances.html
Occlusion Culling :
http://docs.unity3d.com/Documentation/Manual/OcclusionCulling.html
9.关于Realtime Shadows(实时阴影)
实时阴影技术非常棒,但消耗大量计算。为GPU和CPU都带来了昂贵的负担,细节的话参考下面:
http://docs.unity3d.com/Documentation/Manual/Shadows.html
10.对GPU优化:采用高效的shader
a.需要注意的是有些(built-in)Shader是有mobile版本的,这些大大提高了顶点处理的性能。当然也会有一些限制。
b.自己写的shader请注意复杂操作符计算,类似pow,exp,log,cos,sin,tan等都是很耗时的计算,最多只用一次在每个像素点的计算。不推荐你自己写normalize,dot,inversesqart操作符,内置的肯定比你写的好。
c.需要警醒的是alpha test,这个非常耗时。
d.浮点类型运算:精度越低的浮点计算越快。
在CG/HLSL中--
float :32位浮点格式,适合顶点变换运算,但比较慢。
half:16位浮点格式,适合贴图和UV坐标计算,是highp类型计算的两倍。
fixed: 10位浮点格式,适合颜色,光照,和其他。是highp格式计算的四倍。
写Shader优化的小提示:
http://docs.unity3d.com/Documentation/Components/SL-ShaderPerformance.html
11.Unity中一个简单的图片优化:
为什么我的游戏占用了那么多的存储空间?
为什么我的手机发热跟个火山一样?
为什么我的游戏加载那么慢?
为什么我的游戏画面已经调到了最低特效,它还是这么卡?
我该怎样缩小我的游戏所需要的存储空间?
噢不要慌,这些都是我们在游戏发布前夕会遇到的很常见的问题。
我们来设想一个这样的情形:你刚刚发布了一款新游戏,恰好,你最大的竞争对手打算跟你扳扳手腕(最麻烦的地方就是他对你有着非常大的不满)。
你的游戏大小50MB,而他的仅仅需要20MB。
一周之后,你发现他的游戏凭着十来万的下载量在应用市场上混的是风生水起,再看看你的只有少的可怜的几百下载量(即使你认为你的游戏有着更好的游戏体验与游戏玩法)。
记住我的这句话:手游市场是一个疯狂的、竞争十分激烈的地方。如果你不控制游戏的大小,你很可能被立即打的体无完肤。没有哪个用户喜欢等待,也不喜欢浪费太多的流量(对于这种情况,我更希望将RIP添加进你的游戏当中)。
等等,你还没有必要因此而垂头丧气。RIP并不总是意味着REST IN PEACE,有时它也代表着RETURN IF POSSIBLE。
所以,让我们来制定一个翻盘计划。
我同意你的观点,但是我怎么才能缩减游戏的大小呢?
有很多东西值得我们去关注,但是首先让我们打开Editor Log(编辑记录), 看一看到底是什么占了大量的空间。
如果你不知道怎么打开EditorLog,请参阅如下网页:
http://docs.unity3d.com/Manual/LogFiles.html#sthash.XUDBcSzL.dpuf
在Unity官方用户手册中,有如下的一段话:
“日志提供哪些资源占用空间,并且按比例将资源一一列出。一般来说,像纹理、音乐、视频这样的文件需要占用大量的存储空间,而对于脚本、关卡、着色器(shader)这样的东西来说,所需的空间非常少。”
由下图我们可以很容易地理解上面的话:
该数据是从我们一款已开发完成的2D游戏当中真实采集而来。正如上图所示,Textures/Graphics(纹理图像)占用了游戏当中最多的空间。
因此,GraphicsCompression(图像压缩)应当成为缩减大小的重中之重。
OK,这确实很明显,但是我应该对它做些什么呢?
很幸运的是,现在有一个名叫Texture Compression(纹理压缩)的概念
那么TextureCompression到底是什么意思呢?
维基百科中对其有着如下定义:
●“Texture Compression是一种特殊的图像压缩算法,这种算法是专门为在3D计算机图像渲染系统当中进行纹理地图的存储而设计的。与传统图像压缩算法所不同的是,texture Compression算法最适应随机存取。”
这些都是什么鬼?别担心,我以前也跟你一样的感受:)
一些易于理解的定义:
●常用的如PNG与JPG之类的图像压缩格式(这也是我们在游戏当中经常使用的格式)不可以直接被GPU所解码。因此在把他们拷贝至GPU内存之前,首先需要解压。而解压这些纹理需要时间,加载时间也随之变长了。
●有一个更好的选择是选用Hardware Accelerated Formats(硬件加速格式)。这种格式确实有损耗,但其优势在于这种格式是专门为GPU所设计的。
●这样一来便意味着不需要在拷贝之前进行解压,玩家的加载时间也因此降低,说不定性能还会进一步得到提升,而这些都要归功于Hardware Optimizations(硬件优化)。
●当前有很多种压缩格式,如ETC1, ASTC, DXT1, DXT5, PVRTC, ATC等等。这种将普通压缩格式转变为硬件加速格式的过程,我们称之为Texture compression(纹理压缩)。现在是不是很容易就明白了?:D
●我找到了一个有助于你理解纹理压缩与其细节重要性的网页(在下面贴出来了),有兴趣再去看一看吧:
http://renderingpipeline.com/2012/07/texture-compression/
基本上硬件加速格式有如下好处:提升性能,延长电池寿命,减少设备发热量,减少带宽需求,更少的能量消耗。
毫无疑问的是这对你我来说都很重要。同时也希望你的对手已经对这轮反攻做好了准备,嘿嘿:)
啊,原来纹理压缩就是答案啊,那我该用哪种压缩方式呢?
●呃,这要取决于你比较关注哪一类设备。并不是所有的设备都支持所有的硬件压缩格式。
●让我们先看一看下表,了解一下不同设备所支持的压缩格式。
有两种类型的硬件加速格式,分别是standard与proprietary。
Standard格式如下:
ETC1 |
装有OpenGL ES 2.0及以上版本的所有安卓设备可以支持,但是不支持alpha通道。 |
ETC2 |
需要OpenGL ES 3.0及以上版本。 |
ASTC |
比ETC1与ETC2效果更好,需要Android Extension Pack。 |
ATC |
需要Adreno GPU。 |
PVRTC |
需要PowerVR GPU。 |
DXT1 |
S3 DXT1纹理压缩。需要设备具有Nvidia Tegra平台。 |
S3TC |
S3纹理压缩,与DXT类似,需要设备具有Nvidia Tegra平台。 |
Proprietary格式如下:
让我们先举个例子,这样就可以更容易理解压缩以及它是如何实际运行的。就拿我经常用的UI来看吧。
我的UI就和下图类似:
我会只关注背景图像。在选择背景图像上,我会把图像弄成下面这个样子(在inspector中打开)。
这是默认的尺寸(未压缩,设置为真彩):
我们可以看见这个图片的大小是8M。
Inspector中显示的这个图片的设置是这样的:
现在将TextureType设置成Advanced,GenerateMip Maps取消勾选,勾选上Override for Android(这一操作会允许自定义压缩),再把Format选择成RGBA Compressed DXT5。
再回头看一看刚才的图片变成什么样了:
图像的大小从8M变成了2M。
这是不是看起来挺厉害的?下面再试一试ETC Compression。
将格式选择为RGBCompressed ETC 4 bits:
图片又变成了1M,够酷的吧!
Notes 1.大小的压缩不仅依赖于格式,还依赖于图像的类型 2.更小的大小并不一定意味着最优化(我将在后文当中添加一些光) 3.如果你使用mipmaps,那么在存储的时候会比单个图像大三分之一左右(我建议在做UI相关图形的时候不要用mipmaps) 4.大部分的压缩格式要求文件的分辨率数值为2的次幂(比如我刚才在例子中使用的2048*1024),这样才可以有效地进行压缩。 |
如果你对减少文件大小很感兴趣,可以看一看这个:
●http://docs.unity3d.com/Manual/ReducingFilesize.html
图像存储大小的计算规则是宽*长*像素深度(bpp),像素深度指的是每个像素所占的字节。
这样一来你就可以用这种方法来对你的目标设备进行压缩格式的选取了。
单设备支持怎么能行?我想让所有的设备都支持我的游戏!!
●很好,我们没理由放弃哪怕只有1%的市场份额!
●幸运的是,我们不必太过于担心iOS系统的设备,因为PVRTC这种纹理压缩格式可以在所有装有iOS系统的设备上使用,比如iPhone,iPod Touch,还有iPad。
●因此,对于iOS来说只需要压缩成PVRTC格式就行了。
那么对于安卓设备怎么办呢?
不同的安卓设备,它们的GPU都不一样。
在Unity用户手册当中有着这样的描述:
●“虽然安卓不支持DXT/PVRTC/ATC纹理,如果特定的设备不支持这些方法的话,Unity会将这些纹理在运行时解压至RGB(A)格式。这样一来会影响GPU的渲染速度。”
举个例子来说明一下,如果压缩成了DXT5格式,然后在一个不支持的设备上运行,那么纹理就不会解压成RGBA 32位,并且这些文件(已解压的)会和没解压的一起存储在内存当中。
在这种情况下,不仅浪费了解压纹理的时间,还由于多存储了一个已解压的文件而浪费了空间。这毫无疑问会在渲染性能上起到一个非常消极的作用。
因此该问题的解决方法就是使用ETC1格式(这是一种标准格式)来替代它们,所有的GPU都支持这种格式。这难道不是件非常容易的事情?
但是有一点需要注意一下。这并不是每个地方都没问题,就比如ETC1不支持alpha通道。很多游戏的纹理与2D sprite都是用alpha的,现在该怎么做呢?
●Unity同行给了我们两种方案:
1)可以为DXT/PVRTC/ACT这些不同的格式生成不同的安装包(译者:如炉石传说移动版),然后在安卓市场当中按不同的设备来发布合适的安装包。 或 2)转换成RGBA16位,有时候这样可以在需求alpha的地方很好地协调游戏大小,画质,渲染速度三者之间的关系。在alpha不需要的地方,可以与ETC1建立连接并且使用它。 |
就我个人而言,我更倾向于第二种方法,但是一切还是要以实际情况进行考虑。
征服市场
“是的,你现在可以开始行动了!非常明确的是,现在的游戏画面有着足够的能量来镇住你的这款游戏。图形的优化是必须的,除此之外,别无出路。”
“关于画质与游戏大小的权衡也差不多就这样了,这还是主要取决于你如何机智地进行图像优化。”
12.另外的相关优化:
a.对Draw Call Batching的优化
http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html
b.对Rendering Statistics Window的说明和提示:
http://docs.unity3d.com/Documentation/Manual/RenderingStatistics.html
c.角色模型的优化建议
用单个蒙皮渲染、尽量少用材质、少用骨骼节点、移动设备上角色多边形保持在300~1500内(当然还要看具体的需求)、PC平台上1500~4000内(当然还要看具体的需求)。
http://docs.unity3d.com/Documentation/Manual/ModelingOptimizedCharacters.html