u3d 创建meshmesh compression开多少

http://blog.csdn.net/candycat1992/article/details/
这一篇是在的一个的基础上总结扩展而得的~是一个非常棒的教程网站,包含了多媒体领域很多方面的资料,非常酷!除此之外,还参考了Unity Cookie中的一个教程。还有很多其他参考在下面的链接中。
这篇文章旨在简要地说明一下常见的各种优化策略。不过对每个基础有非常深入地讲解,需要的童鞋可以自行去相关资料。
还有一些我认为非常好的参考文章:
影响性能的因素
首先,我们得了解,影响游戏性能的因素哪些,才能对症下药。对于一个游戏来说,有两种主要的计算资源:CPU和GPU。它们会互相合作,来让我们的游戏可以在预期的帧率和分辨率下工作。CPU负责其中的帧率,GPU主要负责分辨率相关的一些东西。
总结起来,主要的性能瓶颈在于:
过多的Draw Calls
复杂的脚本或者物理模拟
过多的顶点
过多的逐顶点计算
像素(Fragment)处理
过多的fragment,overdraws
过多的逐像素计算
尺寸很大且未压缩的纹理
分辨率过高的framebuffer
对于CPU来说,限制它的主要是游戏中的Draw Calls。那么什么是Draw Call呢?如果你学过OpenGL,那么你一定还记得在每次绘图前,我们都需要先准备好顶点数据(位置、法线、颜色、纹理坐标等),然后调用一系列API把它们放到GPU可以访问到的指定位置,最后,我们需要调用_glDraw*命令,来告诉GPU,&嘿,我把东西都准备好了,你个懒家伙赶紧出来干活(渲染)吧!&。而调用_glDraw*命令的时候,就是一次Draw Call。那么为什么Draw Call会成为性能瓶颈呢(而且是CPU的瓶颈)?上面说到过,我们想要绘制图像时,就一定需要调用Draw Call。例如,一个场景里有水有树,我们渲染水的时候使用的是一个material以及一个shader,但渲染树的时候就需要一个完全不同的material和shader,那么就需要CPU重新准备顶点数据、重新设置shader,而这种工作实际是非常耗时的。如果场景中,每一个物体都使用不同的material、不同的纹理,那么就会产生太多Draw Call,影响帧率,游戏性能就会下降。当然,这里说得很简单,更详细的请自行谷歌。其他CPU的性能瓶颈还有物理、布料模拟、粒子模拟等,都是计算量很大的操作。
而对于GPU来说,它负责整个渲染流水线。它会从处理CPU传递过来的模型数据开始,进行Vertex Shader、Fragment Shader等一系列工作,最后输出屏幕上的每个像素。因此它的性能瓶颈可能和需要处理的顶点数目的、屏幕分辨率、显存等因素有关。总体包含了顶点和像素两方面的性能瓶颈。在像素处理中,最常见的性能瓶颈之一是overdraw。Overdraw指的是,我们可能对屏幕上的像素绘制了多次。
了解了上面基本的内容后,下面涉及到的优化技术有:
优化几何体
使用LOD(Level of detail)技术
使用遮挡剔除(Occlusion culling)技术
控制绘制顺序
警惕透明物体
减少实时光照
减少Draw Calls
减少纹理大小
首先是顶点优化的部分。
优化几何体
这一步主要是为了针对性能瓶颈中的&顶点处理&一项。这里的几何体就是指组成场景中对象的网格结构。
3D游戏制作都由模型制作开始。而在建模时,有一条我们需要记住:尽可能减少模型中三角形的数目,一些对于模型没有影响、或是肉眼非常难察觉到区别的顶点都要尽可能去掉。例如在下面左图中,正方体内部很多顶点都是不需要的,而把这个模型导入到Unity里就会是右面的情景:
在Game视图下,我们可以查看场景中的三角形数目和顶点数目:
可以看到一个简单的正方形就产生了这么多顶点,这是我们不希望看到的。
同时,尽可能重用顶点。在很多三维建模软件中,都有相应的优化选项,可以自动优化网格结构。最后优化后,一个正方体可能只剩下8个顶点:
它对应的顶点数和三角形数目如下:
等等!这里,你可能要问了,为什么顶点数是24,而不是8呢?美术朋友们经常会遇到这样的问题,就是建模软件里显示的模型顶点数和Unity中的不一样,通常Unity会多很多。谁才是对的呢?其实,它们是站在不同的角度上计算的,都有各自的道理,但我们真正应该关心的是Unity里的数目。
我们这里简单解释一下。三维软件里更多地是站在我们人类的角度理解顶点的,即我们看见的一个点就是一个。而Unity是站在GPU的角度上,去计算顶点数目的。而在GPU看来,看起来是一个的很有可能它要分开处理,从而就产生了额外的顶点。这种将顶点一分为多的原因,主要有两个:一个是UV splits,一个是Smoothing splits。而它们的本质其实都是因为对于GPU来说,顶点的每一个属性和顶点之间必须是一对一的关系。UV splits的产生,是因为建模时,一个顶点的UV坐标有多个。例如之前的立方体的例子,由于每个面都有共同的顶点,因此在不同面上,同一个顶点的UV坐标可能发生改变。这对于GPU来说,这是不可理解的,因此它必须把这个顶点拆分成两个具有不同UV坐标的定顶点,它才甘心。而Smoothing splits的产生也是类似的,不同的时,这次一个顶点可能会对应多个法线信息或切线信息。这通常是因为我们要决定一个边是一条Hard Edge还是Smooth Edge。Hard Edge通常是下面这样的效果(注意中间的折痕部分):
而如果观察它的顶点法线,就会发现,折痕处每个顶点其实包含了两个不同的法线。因此,对于GPU来说,它同样无法理解这样的事情,因此会把顶点一分为二。而相反,Smooth Edge则是下面的情况:
对于GPU来说,它本质上只关心有多少个顶点。因此,尽可能减少顶点的数目其实才是我们真正对需要关心的事情。因此,最后一条优化建议就是:移除不必要的Hard Edge以及纹理衔接,即避免Smoothing splits和UV&splits。
使用LOD(Level of detail)技术
LOD技术有点类似于Mipmap技术,不同的是,LOD是对模型建立了一个模型金字塔,根据摄像机距离对象的远近,选择使用不同精度的模型。它的好处是可以在适当的时候大量减少需要绘制的顶点数目。它的缺点同样是需要占用更多的内存,而且如果没有调整好距离的话,可能会造成模拟的突变。
在Unity中,可以通过来实现LOD技术:
通过上面的LOD Group面板,我们可以选择需要控制的模型以及距离设置。下面展示了油桶从一个完整网格到简化网格,最后完全被剔除的例子:
使用遮挡剔除(Occlusion culling)技术
遮挡剔除是用来消除躲在其他物件后面看不到的物件,这代表资源不会浪费在计算那些看不到的顶点上,进而提升性能。关于遮挡剔除,Unity Taiwan有一个系列文章大家可以看看(需FQ):
具体的内容大家可以自行查找。
现在我们来谈像素优化。
像素优化的重点在于减少overdraw。之前提过,overdraw指的就是一个像素被绘制了多次。关键在于控制绘制顺序。
Unity还提供了查看,在Scene视图的Render Mode-&Overdraw。当然这里的视图只是提供了查看物体遮挡的层数关系,并不是真正的最终屏幕绘制的overdraw。也就是说,可以理解为它显示的是如果没有使用任何深度检验时的overdraw。这种视图是通过把所有对象都渲染成一个透明的轮廓,通过查看透明颜色的累计程度,来判断物体的遮挡。
上图图,红色越是浓重的地方表示overdraw越严重,而且这里涉及的都是透明物体,这意味着性能将会受到很大影响。
控制绘制顺序
需要控制绘制顺序,主要原因是为了最大限度的避免overdraws,也就是同一个位置的像素可以需要被绘制多变。在PC上,资源无限,为了得到最准确的渲染结果,绘制顺序可能是从后往前绘制不透明物体,然后再绘制透明物体进行混合。但在移动平台上,这种会造成大量overdraw的方式显然是不适合的,我们应该尽量从前往后绘制。从前往后绘制之所以可以减少overdraw,都是因为深度检验的功劳。
在Unity中,那些Shader中被设置为&Geometry& 队列的对象总是从前往后绘制的,而其他固定队列(如&Transparent&&Overla&等)的物体,则都是从后往前绘制的。这意味这,我们可以尽量把物体的队列设置为&Geometry& 。
而且,我们还可以充分利用Unity的队列来控制绘制顺序。例如,对于天空盒子来说,它几乎覆盖了所有的像素,而且我们知道它永远会在所有物体的后面,因此它的队列可以设置为&Geometry+1&。这样,就可以保证不会因为它而造成overdraws。
时刻警惕透明物体
而对于透明对象,由于它本身的特性(可以看之前关于Alpha Test和Alpha Blending的)决定如果要得到正确的渲染效果,就必须从后往前渲染(这里不讨论使用深度的方法),而且抛弃了深度检验。这意味着,透明物体几乎一定会造成overdraws。如果我们不注意这一点,在一些机器上可能会造成严重的性能下面。例如,对于GUI对象来说,它们大多被设置成了半透明,如果屏幕中GUI占据的比例太多,而主摄像机又没有进行调整而是投影整个屏幕,那么GUI就会造成屏幕的大量overdraws。
因此,如果场景中大面积的透明对象,或者有很多层覆盖的多层透明对象(即便它们每个的面积可以都不大),或者是透明的粒子效果,在移动设备上也会造成大量的overdraws。这是应该尽量避免的。
对于上述GUI的这种情况,我们可以尽量减少窗口中GUI所占的面积。如果实在无能为力,我们可以把GUI绘制和三维场景的绘制交给不同的摄像机,而其中负责三维场景的摄像机的视角范围尽量不要和GUI重叠。对于其他情况,只能说,尽可能少用。当然这样会对游戏的美观度产生一定影响,因此我们可以在代码中对机器的性能进行判断,例如首先关闭所有的耗费性能的功能,如果发现这个机器表现非常良好,再尝试开启一些特效功能。
减少实时光照
实时光照对于移动平台是个非常昂贵的操作。如果只有一个平行光还好,但如果场景中包含了太多光源并且使用了很多多Passes的shader,那么很有可能会造成性能下降。而且在有些机器上,还要面临shader失效的风险。例如,一个场景里如果包含了三个逐像素的点光源,而且使用了逐像素的shader,那么很有可能将Draw Calls提高了三倍,同时也会增加overdraws。这是因为,对于逐像素的光源来说,被这些光源照亮的物体要被再渲染一次。更糟糕的是,无论是动态批处理还是动态批处理(其实文档中只提到了对动态批处理的影响,但不知道为什么实验结果对静态批处理也没有用),对于这种逐像素的pass都无法进行批处理,也就是说,它们会中断批处理。
例如,下面的场景中,四个物体都被标识成了&Static&,它们使用的shader都是自带的Bumped Diffuse。而所有的点光源都被标识成了&Important&,即是逐像素光。可以看到,运行后的Draw Calls是23,而非3。这是因为,只有&Forward Base&的Pass时发生了静态批处理(这里的动态批处理由于多Pass已经完全失效了),节省了一个Draw Calls,而后面的&Forward Add& Pass,每一次渲染都是一个单独的Draw Call(而且可以看到Tris和Verts数目也增加了):
这点正如中说的:The draw calls for &additional per-pixel lights& will not be batched。原因我不是很清楚,这里,但里面的意思说是对静态批处理没有影响,和我这里的结果不一样,知道原因的麻烦给我留言,非常感谢。我也在Unity论坛里提问里。
我们看到很多成功的移动游戏,它们的画面效果看起来好像包含了很多光源,但其实这都是骗人的。
使用Lightmaps
Lightmaps的很常见的一种优化策略。它主要用于场景中整体的光照效果。这种技术主要是提前把场景中的光照信息存储在一张光照纹理中,然后在运行时刻只需要根据纹理采样得到光照信息即可。
当然与之配合的还有Light Probes技术。风宇冲讲过,但是时间比较久远,但教程我相信网上有很多。
使用God Rays
场景中很多小型光源效果都是靠这种方法模拟的。它们一般并不是真的光源产生的,很多情况是通过透明纹理进行模拟。具体可以参见。
减少Draw Calls
批处理(Batching)
这方面的优化教程想必是最多的了。最常见的就是通过批处理(Batching)了。从名字上来理解,就是一块处理多个物体的意思。那么什么样的物体可以一起处理呢?答案就是使用同一个材质的物体。这是因此,对于使用同一个材质的物体,它们之间的不同仅仅在于顶点数据的差别,即使用的网格不同而已。我们可以把这些顶点数据合并在一起,再一起发送给GPU,就可以完成一次批处理。
Unity中有:一种是动态批处理,一种是静态批处理。对于动态批处理来说,好消息是一切处理都是自动的,不需要我们自己做任何操作,而且物体是可以移动的,但坏消息是,限制很多,可能一不小心我们就会破坏了这种机制,导致Unity无法批处理一些使用了相同材质的物体。对于静态批处理来说,好消息是自由度很高,限制很少,坏消息是可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。
首先来说动态批处理。Unity进行动态批处理的条件是,物体使用同一个材质并且满足一些特定条件。Unity总是在不知不觉中就为我们做了动态批处理。例如下面的场景:
这个场景共包含了4个物体,其中两个箱子使用了同一个材质。可以看到,它的Draw Calls现在是3,并且显示Save by batching是1,也就是说,Unity靠Batching为我们节省了1个Draw Call。下面,我们来把其中一个箱子的大小随便改动一下,看看会发生什么:
可以发现,Draw Calls变成了4,Save by batching的数目也变成了0。这是为什么呢?它们明明还是只使用了一个材质啊。原因就是前面提到的那些需要满足的其他条件。动态批处理虽然自动得令人感动,但它对模型的要求很多:
顶点属性的最大限制为900,而且未来有可能会变。不要依赖这个数据。
一般来说,那么所有对象都必须需要使用同一个缩放尺度(可以是(1, 1, 1)、(1, 2, 3)、(1.5, 1.4, 1.3)等等,但必须都一样)。但如果是非统一缩放(即每个维度的缩放尺度不一样,例如(1, 2, 1)),那么如果所有的物体都使用不同的非统一缩放也是可以批处理的。这个要求很怪异,为什么批处理会和缩放有关呢?这和Unity背后的技术有关系,有兴趣的可以自行谷歌,比如。
使用lightmap的物体不会批处理。多passes的shader会中断批处理。接受实时阴影的物体也不会批处理。
上述除了最常见的由于缩放导致破坏批处理的情况,还有就是顶点属性的限制。例如,在上面的场景中我们添加之前未优化后的箱子模型:
可以看到Draw Calls一下子变成了5。这是因为新添加的箱子模型中,包含了474个顶点,而它使用的顶点属性有位置、UV坐标、法线等信息,使用的总和超过了900。
动态批处理的条件这么多,一不小心它就不干了,因此Unity提供了另一个方法,静态批处理。接着上面的例子,我们保持修改后的缩放,但把四个物体的&Static Flag&勾选上:
点击Static后面的三角下拉框,我们会看到其实这一步设置了很多东西,这里我们想要的只是&Batching static&一项。这时我们再看Draw Calls,恩,还是没有变化。但是不要急,我们点击运行,变化出现了:
Draw Calls又回到了3,并且显示Save by batching是1。这就是得利于静态批处理。而且,如果我们在运行时刻查看模型的网格,会发现它们都变成了一个名为Combined Mesh (roo: scene)的东西。这个网格是Unity合并了所有标识为&Static&的物体的结果,在我们的例子里,就是四个物体:
你可以要问了,这四个对象明明不是都使用了一个材质,为什么可以合并成一个呢?如果你仔细观察上图的话,会发现里面标明了&4 submeshes&,也就是说,这个合并后的网格其实包含了4个子网格,也就是我们的四个对象。对于合并后后的网格,Unity会判断其中使用同一个材质的子网格,然后对它们进行批处理。
但是,我们再细心点可以发现,我们的箱子使用的其实是同一个网格,但合并后却变成了两个。而且,我们观察运行前后Stats窗口中的&VBO total&,它的大小由241.6KB变成了286.2KB,变大了!还记得静态批处理的缺点吗?就是可能会占用更多的内存。中是这样写的:
&Using static batching will require additional memory for storing the combined geometry. If several objects shared the same geometry before static batching, then a copy of geometry will be created for each object, either in the Editor or at runtime. This might not always be a good idea - sometimes you will have to sacrifice rendering performance by avoiding static batching for some objects to keep a smaller memory footprint. For example, marking trees as static in a dense forest level can have serious memory impact.&
也就是说,如果在静态批处理前有一些物体共享了相同的网格(例如这里的两个箱子),那么每一个物体都会有一个该网格的复制品,即一个网格会变成多个网格被发送给GPU。在上面的例子看来,就是VBO的大小明显增大了。如果这类使用同一网格的对象很多,那么这就是一个问题了,这种时候我们可能需要避免使用静态批处理,这意味着牺牲一定的渲染性能。例如,如果在一个使用了1000个重复树模型的森林中使用静态批处理,那么结果就会产生1000倍的内存,这会造成严重的内存影响。这种时候,解决方法要么我们可以忍受这种牺牲内存换取性能的方法,要么不要使用静态批处理,而使用动态批处理(前提是大家使用相同的缩放大小,或者大家都使用不同的非统一缩放大小),或者自己编写批处理的方法。当然,我认为最好的还是使用动态批处理来解决。
有一些小提示可以使用:
尽可能选择静态批处理,但得时刻小心对内存的消耗。
如果无法进行静态批处理,而要使用动态批处理的话,那么请小心上面提到的各种注意事项。例如:
尽可能让这样的物体少并且尽可能让这些物体包含少量的顶点属性。
不要使用统一缩放,或者都使用不同的非统一缩放。
对于游戏中的小道具,例如可以捡拾的金币等,可以使用动态批处理。
对于包含动画的这类物体,我们无法全部使用静态批处理,但其中如果有不动的部分,可以把这部分标识成&Static&。
一些讨论:
合并纹理(Atlas)
虽然批处理是个很好的方式,但很容易就打破它的规定。例如,场景中的物体都使用Diffuse材质,但它们可能会使用不同的纹理。因此,尽可能把多张小纹理合并到一张大纹理(Atlas)中是一个好主意。
利用网格的顶点数据
但有时,除了纹理不同外,还有对于不同的物体,它们在材质上还有一些微小的参数变化,例如颜色不同、某些浮点参数不同。但铁定律是,不管是动态批处理还是静态批处理,它们的前提都是要使用同一个材质。是同一个,而不是同一种,也就是说它们指向的材质必须是同一个实体。这意味着,只要我们调整了参数,就会影响到所有使用这个材质的对象。那么想要微小的调整怎么办呢?由于Unity中的规定非常死,那么我们只好想些&歪门邪道&,其中一种就是使用网格的顶点数据(最常见的就是顶点颜色数据)。
前面说过,经过批处理后的物体会被处理成一个VBO发送给GPU,VBO中的数据可以作为输入传递给Vertex Shader,因此我们可以巧妙地对VBO中的数据进行控制,从而达到不同效果的目的。一个例子是,还是之前的森林,所有的树使用了同一种材质,我们希望它们可以通过动态批处理来实现,但不同树的颜色可能不同。这时我么可以利用网格的顶点数据来调整。具体方法,可以参见后面会写的一篇文章。
但这种方法的缺点就是会需要更多的内存来存储这些用于调整参数用的顶点数据。没办法,永远没有绝对完美的方法。
减少纹理大小
之前提到过,使用Texture Atlas可以帮助减少Draw Calls,而这些纹理的大小同样是一个需要考虑的问题。在这之前要提到一个问题就是,所有纹理的长宽比最好是正方形,而且长度值最好是2的整数幂。这是因为有很多优化策略只有在这种时候才可以发挥最大效用。
Unity中查看纹理参数可以通过纹理的面板:
而调整参数可以通过纹理的Advance面板:
上面各种参数的说明可以参见。其中和优化相关的主要有&Generate Mip Maps&、&Max Size&和&Format&几个选项。
&Generate Mip Maps&会为同一张纹理创建出很多不同大小的小纹理,构成一个纹理金字塔。而在游戏中可以根据距离物体的远近,来动态选择使用哪一个纹理。这是因为,在距离物体很远的时候,就算我们使用了非常精细的纹理,但肉眼也是分辨不出来的,这种时候完全可以使用更小、更模糊的纹理来代替,而这大量可以节省访问的像素的数目。但它的缺点是,由于需要为每一个纹理建立一个图像金字塔,因此它会需要占用更多的内存。例如上面的例子,在勾选&Generate Mip Maps&前,内存占用是0.5M,而勾选了&Generate Mip Maps&后,就变成了0.7M。除了内存的占用以外,一些时候我们也不希望使用,例如GUI纹理等。我们还可以在面板中查看生成的Mip Maps:
中还提供了查看场景中物体的Mip Maps的使用情况。更确切的说是,展示了物体理想的纹理大小。其中红色表示这个物体可以使用更小的纹理,蓝色表示应该使用更大的纹理。
&Max Size&决定了纹理的长宽值,如果我们使用的纹理本身超过了这个最大值,Unity会对其进行缩小来满足这个条件。这里再重复一点,所有纹理的长宽比最好是正方形,而且长度值最好是2的整数幂。这是因为有很多优化策略只有在这种时候才可以发挥最大效用。
&Format&负责纹理使用的压缩模式。通常选择这种自动模式就可以了,Unity会负责根据不同的平台来选择合适的压缩模式。而对于GUI类型的纹理,我们可以根据对画质的要求来选择是否进行压缩,具体可以参见之前。
我们还可以根据不同的机器来选择使用不同分辨率的纹理,以便让游戏在某些老机器上也可以运行。
很多时候分辨率也是造成性能下降的原因,尤其是现在很多国内山寨机,除了分辨率高其他硬件简直一塌糊涂,而这恰恰中了游戏性能的两个瓶颈:过大的屏幕分辨率+糟糕的GPU。因此,我们可能需要对于特定机器进行分辨率的放缩。当然,这样会造成游戏效果的下降,但性能和画面之间永远是个需要权衡的话题。
在Unity中设置屏幕分辨率可以直接调用。实际使用中可能会遇到一些情况,讲了这种技术,可以去看看。
这篇文章是总结性质的,因此对每种技术都没有进行非常详细的解释。强烈建议大家阅读文章开头给出的各种链接,写得都很好。
阅读(...) 评论()一些Unity 优化建议 整理(为自己)
一些Unity 优化建议 整理(为自己)
发布时间: 12:51:16
编辑:www.fx114.net
本篇文章主要介绍了"一些Unity 优化建议 整理(为自己)",主要涉及到一些Unity 优化建议 整理(为自己)方面的内容,对于一些Unity 优化建议 整理(为自己)感兴趣的同学可以参考一下。
使用Profiler工具分析内存占用情况
System.ExecutableAndDlls:系统可执行程序和DLL,是只读的内存,用来执行所有的脚本和DLL引用。不同平台和不同硬件得到的值会不一样,可以通过修改Player
Setting的Stripping Level来调节大小。
Ricky:我试着修改了一下Stripping Level似乎没什么改变,感觉虽占用内存大但不会影响游戏运行。我们暂时忽略它吧(- -)!
GfxClientDevice:GFX(图形加速\图形加速器\显卡
(GraphicsForce Express))客户端设备。
Ricky:虽占用较大内存,但这也是必备项,没办法优化。继续忽略吧(- -)!!
ManagedHeap.UsedSize:托管堆使用大小。
Ricky:重点监控对象,不要让它超过20MB,否则可能会有性能问题!
ShaderLab:Unity自带的着色器语言工具相关资源。
Ricky:这个东西大家都比较熟悉了,忽略它吧。
SerializedFile:序列化文件,把显示中的Prefab、Atlas和metadata等资源加载进内存。
Ricky:重点监控对象,这里就是你要监控的哪些预设在序列化中在内存中占用大小,根据需求进行优化。
PersistentManager.Remapper:持久化数据重映射管理相关
Ricky:与持久化数据相关,比如AssetBundle之类的。注意监控相关的文件。
ManagedHeap.ReservedUnusedSize:托管堆预留不使用内存大小,只由Mono使用。
Ricky:无法优化。
许多贴图采用的Format格式是ARGB 32 bit所以保真度很高但占用的内存也很大。在不失真的前提下,适当压缩贴图,使用ARGB 16 bit就会减少一倍,如果继续Android采用RGBA Compressed ETC2 8 bits(iOS采用RGBA Compressed PVRTC 4 bits),又可以再减少一倍。把不需要透贴但有alpha通道的贴图,全都转换格式Android:RGB
Compressed ETC 4 bits,iOS:RGB Compressed PVRTC 4 bits。当加载一个新的Prefab或贴图,不及时回收,它就会永驻在内存中,就算切换场景也不会销毁。应该确定物体不再使用或长时间不使用就先把物体制空(null),然后调用Resources.UnloadUnusedAssets(),才能真正释放内存。有大量空白的图集贴图,可以用TexturePacker等工具进行优化或考虑合并到其他图集中。
AudioManager:音频管理器
Ricky:随着音频文件的增多而增大。
AudioClip:音效及声音文件
Ricky:重点优化对象,播放时长较长的音乐文件需要进行压缩成.mp3或.ogg格式,时长较短的音效文件可以使用.wav 或.aiff格式。
Cubemap:立方图纹理
Ricky:这个一般在天空盒中比较常见,我也不知道如何优化这个。。。
Mesh:模型网格
Ricky:主要检查是否有重复的资源,还有尽量减少点面数。
Mesh:场景中使用的网格模型
Ricky:注意网格模型的点面数,能合并的mesh尽量合并。
1)ManagedHeap.UsedSize: 移动游戏建议不要超过20MB.
2)SerializedFile: 通过异步加载(LoadFromCache、WWW等)的时候留下的序列化文件,可监视是否被卸载.
3)WebStream: 通过异步WWW下载的资源文件在内存中的解压版本,比SerializedFile大几倍或几十倍,不过我们现在项目中展示没有。
4)Texture2D: 重点检查是否有重复资源和超大Memory是否需要压缩等.
5)AnimationClip: 重点检查是否有重复资源.
6)Mesh: 重点检查是否有重复资源.
1.Device.Present:
1)GPU的presentdevice确实非常耗时,一般出现在使用了非常复杂的shader.
2)GPU运行的非常快,而由于Vsync的原因,使得它需要等待较长的时间.
3)同样是Vsync的原因,但其他线程非常耗时,所以导致该等待时间很长,比如:过量AssetBundle加载时容易出现该问题.
4)Shader.CreateGPUProgram:Shader在runtime阶段(非预加载)会出现卡顿(华为K3V2芯片).
5)StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 一般是由Debug.Log或类似API造成,游戏发布后需将Debug API进行屏蔽。
2.Overhead:
1)一般情况为Vsync所致.
2)通常出现在设备上.
3.GC.Collect:
1)代码分配内存过量(恶性的)
2)一定时间间隔由系统调用(良性的).
占用时间:
1)与现有Garbage size相关
2)与剩余内存使用颗粒相关(比如场景物件过多,利用率低的情况下,GC释放后需要做内存重排)
4.GarbageCollectAssetsProfile:
1)引擎在执行UnloadUnusedAssets操作(该操作是比较耗时的,建议在切场景的时候进行)。
2)尽可能地避免使用Unity内建GUI,避免GUI.Repaint过渡GCAllow.
3)if(other.tag == a.tag)改为pareTag(a.tag).因为other.tag为产生180B的GC Allow.
4)少用foreach,因为每次foreach为产生一个enumerator(约16B的内存分配),尽量改为for.
5)Lambda表达式,使用不当会产生内存泄漏.
5.尽量少用LINQ:
1)部分功能无法在某些平台使用.
2)会分配大量GC Allow.
6.控制StartCoroutine的次数:
1)开启一个Coroutine(协程),至少分配37B的内存.
2)Coroutine类的实例 -& 21B.
3)Enumerator -& 16B.
7.使用StringBuilder替代字符串直接连接.
8.缓存组件:
1)每次GetComponent均会分配一定的GC Allow.
2)每次Object.name都会分配39B的堆内存.
.框架设计层面。
一个相对中大型的游戏,系统非常的多。这时候合理的适时的释放内存有助于游戏的正常体验,甚至可以防止内存快速到达峰值,导致设备Crash。
目前主流平台机型可用内存:
Android平台:在客户端最低配置以上,均需满足以下内存消耗指标(PSS):
1)内存1G以下机型:最高PSS&=150MB
2)内存2G的机型:最高PSS&=200MB
iOS平台:在iPhone4S下运行,消耗内存(real mem)不大于150MB
1.场景切换时避开峰值。
当前一个场景还未释放的时候,切换到新的场景。这时候由于两个内存叠加很容易达到内存峰值。解决方案是,在屏幕中间遮盖一个Loading场景。在旧的释放完,并且新的初始化结束后,隐藏Loading场景,使之有效的避开内存大量叠加超过峰值。
2.GUI模块加入生命周期管理。
主角、强化、技能、商城、进化、背包、任务等等。通常一个游戏都少不了这些系统。但要是全部都打开,或者这个时候再点世界地图,外加一些逻辑数据内存的占用等等。你会发现,内存也很快就达到峰值。
这时候有效的管理系统模块生命周期就非常有必要。首先将模块进行划分:
1)经常打开 Cache_10;
2)偶尔打开 Cache_5;
3)只打开一次 Cache_0。
创建一个ModuleMananger 类,内部Render方法每分钟轮询一次。如果是“Cache_0”这个类型,一关闭就直接Destroy释放内存;“Cache_10”这个类型为10分钟后自动释放内存;& Cache_5&这种类型为5分钟后自动释放内存。每次打开模块,该模块就会重新计时。这样就可以有效合理的分配内存。
1、& 由于实时对战游戏的数据包数量巨大,早期版本的帧同步策略会导致比较明显的卡顿,通过进行数据包的合并与优化逐渐解决了卡顿问题;
2、& 频繁创建和销毁的小兵对象让CPU爆表了,大量的小兵如果采用实时内存的分配和回收,会产生大量的内存碎片和系统开销,解决方法之一就是采用高效的对象池进行优化,对每个内存对象的状态进行操作即可;
3、& 性能分析过程中,发现单人同屏和多人同屏时的开销都很大,通过视野裁剪技术,使得玩家视野外的不必要的特效和渲染可以全部关闭,极大降低了CPU、GPU和内存的开销;
4、& 在高中低三档机型上玩游戏时,分别加载不同层次的特效包,这也有助于降低CPU和内存的开销;性能分析过程中发现副本内wwise音频组件占了30%的CPU时间,果断抛弃之,采用Unity自带音频功能,优化很明显;
5、& 游戏内界面采用了UGUI的方式实现,但大量的实时UI变化使得副本内每帧会有230以上的drawcall,导致中低端机型感受到明显卡顿,最终采用UGUI+自研究UI的组合拳,重写了一套紧密结合游戏自身特性的UI来实现战斗血条和浮动文字的效果。
6、& &&资源使用总量是否在合理范围之内。
7、& &一个场景内的资源重复率。
8、& &资源对象拷贝的数量是否合理。
9、& 场景切换时保留的资源详情。
10、&&&&&&&&&&&&&网格、纹理、音频、动画、GameObject等资源是否超标。
11、&&&&&&&&&&&&&贴图:
12、&&&&&&&&&&&&&l&&控制贴图大小,尽量不要超过&;
13、&&&&&&&&&&&&&l&&尽量使用2的n次幂大小的贴图,否则GfxDriver里会有2份贴图;
14、&&&&&&&&&&&&&l&&尽量使用压缩格式减小贴图大小;
15、&&&&&&&&&&&&&l&&若干种贴图合并技术;
16、&&&&&&&&&&&&&l&&去除多余的alpha通道;
17、&&&&&&&&&&&&&l&&不同设备使用不同的纹理贴图,分层显示;
18、&&&&&&&&&&&&&&
19、&&&&&&&&&&&&&模型:
20、&&&&&&&&&&&&&l&&尽量控制模型的面数,小于1500会比较合适;
21、&&&&&&&&&&&&&l&&不同设备使用不同的模型面数;
22、&&&&&&&&&&&&&l&&尽量保持在30根骨骼内;
23、&&&&&&&&&&&&&l&&一个网格不要超过3个material;
24、&&&&&&&&&&&&&动画:
25、&&&&&&&&&&&&&l&&N种动画压缩方法;
26、&&&&&&&&&&&&&l&&尽量减少骨骼数量;
27、&&&&&&&&&&&&&声音:
28、&&&&&&&&&&&&&l&&采用压缩MP3&和&wav;
29、&&&&&&&&&&&&&资源方面的优化:
30、&&&&&&&&&&&&&l&&使用&Resource.Load&方法在需要的时候再读取资源;
31、&&&&&&&&&&&&&l&&各种资源在使用完成后,尽快用Resource.UnloadAsset和UnloadUnusedAsset卸载掉;
32、&&&&&&&&&&&&&l&&灵活运用AssetBundle的Load和Unload方法动态加载资源,避免主要场景内的初始化内存占用过高;(实现起来真的很难…)
33、&&&&&&&&&&&&&l&&采用www加载了AssetBundle后,要用www.Dispose&及时释放;
34、&&&&&&&&&&&&&l&&在关卡内谨慎使用DontDestroyOnLoad,被标注的资源会常驻内存;
35、&&&&&&&&&&&&&代码的优化:
36、&&&&&&&&&&&&&l&&尽量避免代码中的任何字符串连接,因为这会给GC带来太多垃圾;
37、&&&&&&&&&&&&&l&&用简单的“for”循环代替“foreach”循环;
38、&&&&&&&&&&&&&l&&为所有游戏内的动态物体使用内存对象池,可以减少系统开销和内存碎片,复用对象实例,构建自己的内存管理模式,减少Instantiate和Destory;
39、&&&&&&&&&&&&&l&&尽量不使用LINQ命令,因为它们一般会分配中间缓器,而这很容易生成垃圾内存;
40、&&&&&&&&&&&&&l&&将引用本地缓存到元件中会减少每次在一个游戏对象中使用 “GetComponent” 获取一个元件引用的需求;
41、&&&&&&&&&&&&&l&&减少角色控制器移动命令的调用。移动角色控制器会同步发生,每次调用都会耗损较大的性能;
42、&&&&&&&&&&&&&l&&最小化碰撞检测请求(例如raycasts和sphere checks),尽量从每次检查中获得更多信息;
43、&&&&&&&&&&&&&l&&AI逻辑通常会生成大量物理查询,建议让AI更新循环设置低于图像更新循环,以减少CPU负荷;
44、&&&&&&&&&&&&&l&&要尽量减少Unity回调函数,哪怕是空函数也不要留着;(例如空的Update、FixedUpdate函数)
45、&&&&&&&&&&&&&l&&尽量少使用FindObjectsOfType函数,这个函数非常慢,尽量少用且一定不要在Update里调用;
46、&&&&&&&&&&&&&l&&千万一定要控制mono堆内存的大小;
47、&&&&&&&&&&&&&&
48、&&&&&&&&&&&&&unity3D 对于移动平台的支持无可厚非,但是也有时候用Unity3D 开发出来的应用、游戏在移动终端上的运行有着明显的效率问题,比如卡、画质等各种问题。自己在做游戏开发的时候偶有所得。对于主要影响性能的因素做个总结。
49、&&&&&&&&&&&&&
50、&&&&&&&&&&&&&主要因素有:
51、&&&&&&&&&&&&&&&&&&&&&1.&&&&&&Savedby batching 值过大&&&---- & 这个值主要是针对Mesh的批处理,这个值越高,应用就越卡&&&
52、&&&&&&&&&&&&&&&&&&&&&2.&&&&&Drawcall值过大 ---- &&&Drawcall 值过大,所需要的 GPU 的处理性能较高,从而导致CPU的计算时间过长,于是就卡了
53、&&&&&&&&&&&&&&&&&&&&&3.&&&&&点、面过多&&&&&&&&&&&----& 点、面过多,GPU 根据不同面的效果展开计算,并且CPU计算的数据也多,所以效果出来了,但是卡巴斯基
54、&&&&&&&&&&&&&由于 Saved by batching 和 Drawcall 值过大所引起的卡的问题我所做的优化方式有:
55、&&&&&&&&&&&&&&&&&&&&&1.&&&&对于模型 :Mesh 合并,有个不错的插件(DrawCallMinimizer&&&---&&&直接上AssetStore 下载即可,免费的,而且有文档,很容易上手)
56、&&&&&&&&&&&&&&&&&&&&&2.&&&&对于UI&&:&&尽量避免使用Unity3D自带的 GUI 换用 NGUI或者EZGUI;因为这两个UI插件对于UI中的图片处理是将UI图片放置在一个 Atlas中,一个 Atlas 对应一个Drawcall
57、&&&&&&&&&&&&&&&&&&&&&3.&&&对于灯光:&可以使用 Unity3D 自带的&&Lightmapping插件来烘焙场景中的灯光效果到物体材质上&
58、&&&&&&&&&&&&&&&&&&&&&4.&&对于场景: 可以使用 Unity3D 自带的 OcclusionCulling 插件把静止不动的场景元素烘焙出来
59、&&&&&&&&&&&&&&&&&&&&&4.&&&对于特效:尽量把材质纹理合并
60、&&&&&&&&&&&&&对于Unity3D 在移动终端上支持的Drawcall 数到底多少,主要是跟机子性能有关的,当然也不是说值小性能就一定没问题(本人亲测,也有17就卡的,主要是模型材质纹理过大所引起的),目前我做的是70左右的,还OK,挺正常的
61、&&&&&&&&&&&&&&
62、&&&&&&&&&&&&&由于点、面过多所导致的性能问题,最好用简模,用四面体来做复杂的模型,但是面、点也别太多,至于Unity3D 到底支持多少点、面的说法各异,我也搞不懂,总之少些肯定OK
63、&&&&&&&&&&&&&&
64、&&&&&&&&&&&&&&
65、&&&&&&&&&&&&&&
66、&&&&&&&&&&&&&检测方式:
67、&&&&&&&&&&&&&一,Unity3D 渲染统计窗口
68、&&&&&&&&&&&&&Game视窗的Stats去查看渲染统计的信息:
69、&&&&&&&&&&&&&1、FPS
70、&&&&&&&&&&&&&fps其实就是 framesper second,也就是每一秒游戏执行的帧数,这个数值越小,说明游戏越卡。
71、&&&&&&&&&&&&&&
72、&&&&&&&&&&&&&2、Draw calls
73、&&&&&&&&&&&&&batching之后渲染mesh的数量,和当前渲染到的网格的材质球数量有关。
74、&&&&&&&&&&&&&&
75、&&&&&&&&&&&&&3、Saved&by&batching&
76、&&&&&&&&&&&&&渲染的批处理数量,这是引擎将多个对象的绘制进行合并从而减少GPU的开销;
77、&&&&&&&&&&&&&很多GUI插件的一个好处就是合并多个对象的渲染,从而降低DrawCalls ,保证游戏帧数。
78、&&&&&&&&&&&&&&
79、&&&&&&&&&&&&&4、Tris 当前绘制的三角面数
80、&&&&&&&&&&&&&&
81、&&&&&&&&&&&&&5、Verts 当前绘制的顶点数
82、&&&&&&&&&&&&&&
83、&&&&&&&&&&&&&6、Used Textures 当前帧用于渲染的图片占用内存大小
84、&&&&&&&&&&&&&&
85、&&&&&&&&&&&&&7、Render Textures 渲染的图片占用内存大小,也就是当然渲染的物体的材质上的纹理总内存占用
86、&&&&&&&&&&&&&&
87、&&&&&&&&&&&&&8、VRAM usage 显存的使用情况,VRAM总大小取决于你的显卡的显存
88、&&&&&&&&&&&&&&
89、&&&&&&&&&&&&&9、VBO Total 渲染过程中上载到图形卡的网格的数量,这里注意一点就是缩放的物体可能需要额外的开销。
90、&&&&&&&&&&&&&&
91、&&&&&&&&&&&&&10、VisibleSkinned Meshes 蒙皮网格的渲染数量
92、&&&&&&&&&&&&&&
93、&&&&&&&&&&&&&11、Animations 播放动画的数量
94、&&&&&&&&&&&&&注意事项:
95、&&&&&&&&&&&&&1,运行时尽量减少 Tris 和 Draw Calls
96、&&&&&&&&&&&&&预览的时候,可点开 Stats,查看图形渲染的开销情况。特别注意 Tris 和 Draw Calls 这两个参数。
97、&&&&&&&&&&&&&一般来说,要做到:
98、&&&&&&&&&&&&&Tris 保持在 7.5k 以下,有待考证。
99、&&&&&&&&&&&&&Draw Calls 保持在 20 以下,有待考证。
100、&&&&&&&&&&2,FPS,每一秒游戏执行的帧数,这个数值越小,说明游戏越卡。
101、&&&&&&&&&&3,Render Textures 渲染的图片占用内存大小。
102、&&&&&&&&&&4,VRAM usage 显存的使用情况,VRAM总大小取决于你的显卡的显存。
103、&&&&&&&&&&&
104、&&&&&&&&&&二,代码优化
105、&&&&&&&&&&1. 尽量避免每帧处理
106、&&&&&&&&&&比如:
107、&&&&&&&&&&function Update() {DoSomeThing(); }
108、&&&&&&&&&&可改为每5帧处理一次:
109、&&&&&&&&&&function Update() { if(Time.frameCount% 5 == 0) { DoSomeThing(); } }
110、&&&&&&&&&&2. 定时重复处理用InvokeRepeating 函数实现
111、&&&&&&&&&&比如,启动0.5秒后每隔1秒执行一次 DoSomeThing 函数:
112、&&&&&&&&&&&
113、&&&&&&&&&&function Start() {InvokeRepeating(&DoSomeThing&, 0.5, 1.0); }
114、&&&&&&&&&&&
115、&&&&&&&&&&3. 优化 Update,FixedUpdate, LateUpdate 等每帧处理的函数
116、&&&&&&&&&&函数里面的变量尽量在头部声明。
117、&&&&&&&&&&比如:
118、&&&&&&&&&&function Update() { var pos:Vector3 = transform. }
119、&&&&&&&&&&可改为
120、&&&&&&&&&&private var pos: Vector3;function Update(){ pos = transform. }
121、&&&&&&&&&&&
122、&&&&&&&&&&4. 主动回收垃圾
123、&&&&&&&&&&给某个 GameObject 绑上以下的代码:
124、&&&&&&&&&&function Update() {if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }
125、&&&&&&&&&&&
126、&&&&&&&&&&5. 优化数学计算
127、&&&&&&&&&&比如,如果可以避免使用浮点型(float),尽量使用整形(int),尽量少用复杂的数学函数比如 Sin 和 Cos 等等
128、&&&&&&&&&&&
129、&&&&&&&&&&6,减少固定增量时间
130、&&&&&&&&&&将固定增量时间值设定在0.04-0.067区间(即,每秒15-25帧)。您可以通过Edit-&Project Settings-&Time来改变这个值。这样做降低了FixedUpdate函数被调用的频率以及物理引擎执行碰撞检测与刚体更新的频率。如果您使用了较低的固定增量时间,并且在主角身上使用了刚体部件,那么您可以启用插值办法来平滑刚体组件。
131、&&&&&&&&&&7,减少GetComponent的调用
132、&&&&&&&&&&使用 GetComponent或内置组件访问器会产生明显的开销。您可以通过一次获取组件的引用来避免开销,并将该引用分配给一个变量(有时称为&缓存&的引用)。例如,如果您使用如下的代码:
133、&&&&&&&&&&function Update () {
134、&&&&&&&&&&transform.Translate(0, 1, 0);
135、&&&&&&&&&&&
136、&&&&&&&&&&}
137、&&&&&&&&&&通过下面的更改您将获得更好的性能:
138、&&&&&&&&&&&
139、&&&&&&&&&&var myTransform : T
140、&&&&&&&&&&function Awake () {
141、&&&&&&&&&&myTransform =
142、&&&&&&&&&&}
143、&&&&&&&&&&function Update () {
144、&&&&&&&&&&myTransform.Translate(0, 1, 0);
145、&&&&&&&&&&}
146、&&&&&&&&&&&
147、&&&&&&&&&&8,避免分配内存
148、&&&&&&&&&&您应该避免分配新对象,除非你真的需要,因为他们不再在使用时,会增加垃圾回收系统的开销。您可以经常重复使用数组和其他对象,而不是分配新的数组或对象。这样做好处则是尽量减少垃圾的回收工作。同时,在某些可能的情况下,您也可以使用结构(struct)来代替类(class)。这是因为,结构变量主要存放在栈区而非堆区。因为栈的分配较快,并且不调用垃圾回收操作,所以当结构变量比较小时可以提升程序的运行性能。但是当结构体较大时,虽然它仍可避免分配/回收的开销,而它由于&传值&操作也会导致单独的开销,实际上它可能比等效对象类的效率还要低。
149、&&&&&&&&&&&
150、&&&&&&&&&&9,使用iOS脚本调用优化功能
151、&&&&&&&&&&UnityEngine 命名空间中的函数的大多数是在 C/c + +中实现的。从Mono的脚本调用 C/C++函数也存在着一定的性能开销。您可以使用iOS脚本调用优化功能(菜单:Edit-&Project Settings-&Player)让每帧节省1-4毫秒。此设置的选项有:
152、&&&&&&&&&&Slow and Safe – Mono内部默认的处理异常的调用
153、&&&&&&&&&&&
154、&&&&&&&&&&Fast and Exceptions Unsupported–一个快速执行的Mono内部调用。不过,它并不支持异常,因此应谨慎使用。它对于不需要显式地处理异常(也不需要对异常进行处理)的应用程序来说,是一个理想的候选项。
155、&&&&&&&&&&&
156、&&&&&&&&&&10,
157、&&&&&&&&&&优化垃圾回收
158、&&&&&&&&&&&
159、&&&&&&&&&&如上文所述,您应该尽量避免分配操作。但是,考虑到它们是不能完全杜绝的,所以我们提供两种方法来让您尽量减少它们在游戏运行时的使用:
160、&&&&&&&&&&如果堆比较小,则进行快速而频繁的垃圾回收
161、&&&&&&&&&&这一策略比较适合运行时间较长的游戏,其中帧率是否平滑过渡是主要的考虑因素。像这样的游戏通常会频繁地分配小块内存,但这些小块内存只是暂时地被使用。如果在iOS系统上使用该策略,那么一个典型的堆大小是大约 200 KB,这样在iPhone 3G设备上,垃圾回收操作将耗时大约 5毫秒。如果堆大小增加到1 MB时,该回收操作将耗时大约 7ms。因此,在普通帧的间隔期进行垃圾回收有时候是一个不错的选择。通常,这种做法会让回收操作执行的更加频繁(有些回收操作并不是严格必须进行的),但它们可以快速处理并且对游戏的影响很小:
162、&&&&&&&&&&if (Time.frameCount % 30 == 0)
163、&&&&&&&&&&{
164、&&&&&&&&&&System.GC.Collect();
165、&&&&&&&&&&}
166、&&&&&&&&&&&
167、&&&&&&&&&&但是,您应该小心地使用这种技术,并且通过检查Profiler来确保这种操作确实可以降低您游戏的垃圾回收时间
168、&&&&&&&&&&如果堆比较大,则进行缓慢且不频繁的垃圾回收
169、&&&&&&&&&&这一策略适合于那些内存分配 (和回收)相对不频繁,并且可以在游戏停顿期间进行处理的游戏。如果堆足够大,但还没有大到被系统关掉的话,这种方法是比较适用的。但是,Mono运行时会尽可能地避免堆的自动扩大。因此,您需要通过在启动过程中预分配一些空间来手动扩展堆(ie,你实例化一个纯粹影响内存管理器分配的&无用&对象):
170、&&&&&&&&&&&
171、&&&&&&&&&&function Start() {
172、&&&&&&&&&&&
173、&&&&&&&&&&var tmp = newSystem.Object[1024];
174、&&&&&&&&&&&
175、&&&&&&&&&&// make allocations in smallerblocks to avoid them to be treated in a special way, which is designed forlarge blocks
176、&&&&&&&&&&&
177、&&&&&&&&&&for (var i : int = 0; i &1024; i++)
178、&&&&&&&&&&&
179、&&&&&&&&&&tmp[i] = new byte[1024];
180、&&&&&&&&&&&
181、&&&&&&&&&&// release reference
182、&&&&&&&&&&&
183、&&&&&&&&&&tmp =
184、&&&&&&&&&&&
185、&&&&&&&&&&}
186、&&&&&&&&&&&
187、&&&&&&&&&&游戏中的暂停是用来对堆内存进行回收,而一个足够大的堆应该不会在游戏的暂停与暂停之间被完全占满。所以,当这种游戏暂停发生时,您可以显式请求一次垃圾回收:
188、&&&&&&&&&&&
189、&&&&&&&&&&System.GC.Collect();
190、&&&&&&&&&&&
191、&&&&&&&&&&另外,您应该谨慎地使用这一策略并时刻关注Profiler的统计结果,而不是假定它已经达到了您想要的效果。
192、&&&&&&&&&&&
193、&&&&&&&&&&三,模型
194、&&&&&&&&&&1,压缩 Mesh
195、&&&&&&&&&&导入 3D 模型之后,在不影响显示效果的前提下,最好打开 Mesh Compression。
196、&&&&&&&&&&Off, Low, Medium, High 这几个选项,可酌情选取。
197、&&&&&&&&&&2,避免大量使用 Unity 自带的 Sphere 等内建 Mesh
198、&&&&&&&&&&Unity 内建的 Mesh,多边形的数量比较大,如果物体不要求特别圆滑,可导入其他的简单3D模型代替。
199、&&&&&&&&&&&
200、&&&&&&&&&&1不是每个主流手机都支持的技术(就是如果可以不用就不用或有备选方案)
201、&&&&&&&&&&屏幕特效
202、&&&&&&&&&&动态的pixel光照计算(如法线)
203、&&&&&&&&&&实时的阴影
204、&&&&&&&&&&&
205、&&&&&&&&&&2优化建议
206、&&&&&&&&&&2.1渲染
207、&&&&&&&&&&1.不使用或少使用动态光照,使用light mapping和light probes(光照探头)
208、&&&&&&&&&&2.不使用法线贴图(或者只在主角身上使用),静态物体尽量将法线渲染到贴图
209、&&&&&&&&&&3.不适用稠密的粒子,尽量使用UV动画
210、&&&&&&&&&&4.不使用fog,使用渐变的面片(参考shadowgun)
211、&&&&&&&&&&5.不要使用alpha–test(如那些cutout shader),使用alpha-blend代替
212、&&&&&&&&&&6.使用尽量少的material,使用尽量少的pass和render次数,如反射、阴影这些操作
213、&&&&&&&&&&7.如有必要,使用Per-LayerCull Distances,
214、&&&&&&&&&&8.只使用mobile组里面的那些预置shader
215、&&&&&&&&&&9.使用occlusionculling
216、&&&&&&&&&&11.远处的物体绘制在skybox上
217、&&&&&&&&&&12.使用drawcallbatching:
218、&&&&&&&&&&&&&&&&&&对于相邻动态物体:如果使用相同的shader,将texture合并
219、&&&&&&&&&&&&&&&&&&对于静态物体,batching要求很高,详见
220、&&&&&&&&&&&
221、&&&&&&&&&&规格上限
222、&&&&&&&&&&1.&&&&&&每个模型只使用一个skinnedmesh renderer
223、&&&&&&&&&&2.&&&&&&每个mesh不要超过3个material
224、&&&&&&&&&&3.&&&&&&骨骼数量不要超过30
225、&&&&&&&&&&4.&&&&&&面数在1500以内将得到好的效率
226、&&&&&&&&&&2.2物理
227、&&&&&&&&&&1.真实的物理(刚体)很消耗,不要轻易使用,尽量使用自己的代码模仿假的物理
228、&&&&&&&&&&2.对于投射物不要使用真实物理的碰撞和刚体,用自己的代码处理
229、&&&&&&&&&&3.不要使用meshcollider
230、&&&&&&&&&&4.在edit-&projectsetting-&time中调大FixedTimestep(真实物理的帧率)来减少cpu损耗
231、&&&&&&&&&&2.3脚本编写
232、&&&&&&&&&&1.尽量不要动态的instantiate和destroyobject,使用object pool
233、&&&&&&&&&&2.尽量不要再update函数中做复杂计算,如有需要,可以隔N帧计算一次
234、&&&&&&&&&&3.不要动态的产生字符串,如Debug.Log(&boo&+ &hoo&),尽量预先创建好这些字符串资源
235、&&&&&&&&&&4.cache一些东西,在update里面尽量避免search,如GameObject.FindWithTag(&&)、GetComponent这样的调用,可以在start中预先存起来
236、&&&&&&&&&&5.尽量减少函数调用栈,用x= (x & 0 ? x : -x);代替x = Mathf.Abs(x)
237、&&&&&&&&&&6.下面的代码是几个gc“噩梦”
238、&&&&&&&&&&&String的相加操作,会频繁申请内存并释放,导致gc频繁,使用
239、&&&&&&&&&&&&&functionConcatExample(intArray: int[]) {
240、&&&&&&&&&&&&&&&&&&&&&&&&&varline = intArray[0].ToString();
241、&&&&&&&&&&&
242、&&&&&&&&&&&&&&&&&&&&&&&&&for(i = 1; i & intArray.L i++) {
243、&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&line+= &, & + intArray[i].ToString();
244、&&&&&&&&&&&&&&&&&&&&&&&&&}
245、&&&&&&&&&&&
246、&&&&&&&&&&&&&&&&&&&&&&&&&
247、&&&&&&&&&&}
248、&&&&&&&&&&在函数中动态new array,最好将一个array、传进函数里修改
249、&&&&&&&&&&functionRandomList(numElements: int) {
250、&&&&&&&&&&&&&&&&&&&&varresult = new float[numElements];
251、&&&&&&&&&&&
252、&&&&&&&&&&&&&&&&&&&&for(i = 0; i & numE i++) {
253、&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&result[i]= Random.
254、&&&&&&&&&&&&&&&&&&&&}
255、&&&&&&&&&&&
256、&&&&&&&&&&&&&&&&&&&&
257、&&&&&&&&&&}
258、&&&&&&&&&&&
259、&&&&&&&&&&2.4 shader编写
260、&&&&&&&&&&1.数据类型
261、&&&&&&&&&&&fixed&/&lowp&-for colors, lighting information and normals,
262、&&&&&&&&&&half&/&mediump&-for texture UV coordinates,
263、&&&&&&&&&&float&/&highp&-avoid in pixel shaders, fine to use in vertex shader for position calculations.
264、&&&&&&&&&&2.少使用的函数:pow,sin,cos等
265、&&&&&&&&&&2.4 GUI
266、&&&&&&&&&&1.不要使用内置的onGUii函数处理gui,使用其他方案,如NGUI
267、&&&&&&&&&&&
268、&&&&&&&&&&3.格式
269、&&&&&&&&&&1.贴图压缩格式:ios上尽量使用PVRTC,android上使用ETC
270、&&&&&&&&&&最简单的优化建议:
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.为什么需要针对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..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 :/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:
/Documentation/Components/RenderTech-ForwardRendering.html
e.使用相关剔除数量直接减少Draw Call数量,下文有相关提及。
5.优化几何模型
最基本的两个优化准则:
a.不要有不必要的三角面。
b.UV贴图中的接缝和硬边越少越好。
需要注意的是,图形硬件需要处理顶点数并跟硬件报告说的并不一样。不是硬件说能渲染几个点就是几个点。模型处理应用通展示的是几何顶点数量。例如,一个由一些不同顶点构成的模型。在显卡中,一些集合顶点将会被分离(split)成两个或者更多逻辑顶点用作渲染。如果有法线、UV坐标、顶点色的话,这个顶点必须会被分离。所以在游戏中处理的实际数量显然要多很多。
6.关于光照
若不用光肯定是最快的。移动端优化可以采用用光照贴图(Lightmapping)去烘培一个静态的贴图,以代替每次的光照计算,在U3D中只需要非常短的时间则能生成。这个方法能大大提高效率,而且有着更好的表现效果(平滑过渡处理,还有附加阴影等)。
在移动设备上和低端电脑上尽量不要在场景中用真光,用光照贴图。这个方法大大节省了CPU和GPU的计算,CPU得到了更少的DRAWCALL,GPU则需要更少顶点处理和像素栅格化。
Lightmapping : /Documentation/Manual/Lightmapping.html
7.对GPU的优化——图片压缩和多重纹理格式
Compressed Textures(图片压缩):
/Documentation/Components/class-Texture2D.html
图片压缩将降低你的图片大小(更快地加载更小的内存跨度(footprint)),而且大大提高渲染表现。压缩贴图比起未压缩的32位RGBA贴图占用内存带宽少得多。
之前U3D会议还听说过一个优化,贴图尽量都用一个大小的格式(512 * 512 , 1024 * 1024),这样在内存之中能得到更好的排序,而不会有内存之间空隙。这个是否真假没得到过测试。
MIPMA(多重纹理格式):
/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 :
/Documentation/Manual/LevelOfDetail.html
Per-Layer Cull Distances :
/Documentation/ScriptReference/Camera-layerCullDistances.html
Occlusion Culling :
/Documentation/Manual/OcclusionCulling.html
9.关于Realtime Shadows(实时阴影)
实时阴影技术非常棒,但消耗大量计算。为GPU和CPU都带来了昂贵的负担,细节的话参考下面:
/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优化的小提示:
/Documentation/Components/SL-ShaderPerformance.html
11.另外的相关优化:
a.对Draw Call Batching的优化
/Documentation/Manual/DrawCallBatching.html
b.对Rendering Statistics Window的说明和提示:
/Documentation/Manual/RenderingStatistics.html
c.角色模型的优化建议
用单个蒙皮渲染、尽量少用材质、少用骨骼节点、移动设备上角色多边形保持在300~1500内(当然还要看具体的需求)、PC平台上内(当然还要看具体的需求)。
/Documentation/Manual/ModelingOptimizedCharacters.html
U3D的渲染是有顺序的,U3D的渲染顺序是由我们控制的,控制好U3D的渲染顺序,你才能控制好DrawCall
一个DrawCall,表示U3D使用这个材质/纹理,来进行一次渲染,那么这次渲染假设有3个对象,那么当3个对象都使用这一个材质/纹理的 时候,就会产生一次DrawCall,可以理解为一次将纹理输送到屏幕上的过程,(实际上引擎大多会使用如双缓冲,缓存这类的手段来优化这个过程,但在这 里我们只需要这样子认识就可以了),假设3个对象使用不同的材质/纹理,那么无疑会产生3个DrawCall
接下来我们的3个对象使用2个材质,A和B使用材质1,C使用材质2,这时候来看,应该是有2个DrawCall,或者3个DrawCall。 应该是2个DrawCall啊,为什么会有3个DrawCall???而且是有时候2个,有时候3个。我们按照上面的DrawCall分析流程来分析一 下:
1.渲染A,使用材质1&
2.渲染B,使用材质1&
3.渲染C,使用材质2
在这种情况下是2个DrawCall,在下面这种情况下,则是3个DrawCall
1.渲染A,使用材质1&
2.渲染C,使用材质2&
3.渲染B,使用材质1
因为我们没有控制好渲染顺序(或者说没有去特意控制),所以导致了额外的DrawCall,因为A和B不是一次性渲染完的,而是被C打断了,所以导致材质1被分为两次渲染
那么是什么在控制这个渲染顺序呢?首先在多个相机的情况下,U3D会根据相机的深度顺序进行渲染,在每个相机中,它会根据你距离相机的距离,由远到近进行渲染,在UI相机中,还会根据你UI对象的深度进行渲染
那么我们要做的就是,对要渲染的对象进行一次规划,正确地排列好它们,规则是,按照Z轴或者深度,对空间进行划分,然后确定好每个对象的Z轴和深度,让使用同一个材质的东西,尽量保持在这个空间内,不要让其他材质的对象进入这个空间,否则就会打断这个空间的渲染顺序
在这个基础上,更细的规则有:
场景中的东西,我们使用Z轴来进行空间的划分,例如背景层,特效层1,人物层,特效层2&
NGUI中的东西,我们统一使用Depth来进行空间的划分&
人物模型,当人物模型只是用一个材质,DrawCall只有1,但是用了2个以上的材质,DrawCall就会暴增(或许对材质的RenderQueue 进行规划也可以使DrawCall只有2个,但这个要拆分好才行),3D人物处于复杂3D场景中的时候,我们的空间规则难免被破坏,这只能在设计的时候尽 量去避免这种情况了&
使用了多个材质的特效,在动画的过程中,往往会引起DrawCall的波动,在视觉效果可以接受的范围内,可以将特效也进行空间划分,假设这个特效是2D显示,那么可以使用Z轴来划分空间
一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!
二、互相尊重,对自己的言论和行为负责。
本文标题:
本页链接:}

我要回帖

更多关于 u3d mesh filter 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信