复习Unity优化技巧

复习Unity优化技巧

一、引言

又要找工作了,现在的公司项目组被砍了,很不幸,我也被砍了。那么久需要复习下,准备下阶段找工作了。于是,决定弄个文章来记录下。被砍是因为政策原因。并没有做什么违法的事情,跟公司性质有关系。

新的格局

不太想做游戏开发了,这一年多搞了Linux跟各种游戏开发软件结合,如Unity、Cocos、Electron,希望未来能找一个开发游戏但偏Linux方向的。

有推荐的吗?

欢迎大家在评论区告诉我。谢谢。

二、优化

按照Unity的官方文档来吧。不懂的加备注。

第一章、优化图形性能

1、分析:

A、GPU通常受填充率或者内存带宽制约。
填充率

是指图形处理单元在每秒内所渲染的像素数量,单位是MPixel/s(百万像素每秒)或者GPixel/s(十亿像素每秒)。像素的填充率等于显示核心的渲染管线数量×核心频率。

渲染管线

渲染管线(Rendering Pipeline)其实就是GPU渲染流程。你可以这样理解渲染,在一个三维坐标系下,给定一个视点(即摄相机),给定三维物体、光源以及照明模式、纹理等信息,如何绘制一幅呈现在视点画面中的二维图像的过程。

尝试解决办法:降低显示分辨率并运行游戏。如果显示分辨率降低后游戏运行更快,表明 GPU 填充率可能是限制因素。

B、CPU 通常受到需要渲染的批次数的限制。

尝试解决办法:检查 Rendering Statistics 窗口中的“batches”。渲染的批次越多,CPU 成本越高。

C 、不太常见的瓶颈。
a、GPU 有太多顶点需要处理。

可接受的能确保良好性能的顶点数量取决于 GPU 和顶点着色器的复杂程度。一般来说,移动端应不超过 100,000 个顶点。另一方面,即使有数百万个顶点,PC 也能管理到位,不过最好还是通过优化尽可能减少此数量。

b、CPU 有太多顶点需要处理。

这些顶点可能位于蒙皮网格、布料模拟、粒子或其他游戏对象和网格中。如上所述,通常较好的做法是在不影响游戏质量的情况下尽可能降低此数量。

c. 其他问题。

如果渲染在 GPU 或 CPU 方面不是问题,则可能在其他地方存在问题,例如在脚本或物理系统中。

2、优化

A、GPU:优化模型几何体

优化模型几何体有两个基本规则:

  • 除非必要,否则不要使用三角形(尽量减少模型的三角面数和顶点数)

  • 尽可能降低 UV 贴图接缝和硬边(双倍顶点)的数量
    GPU优化:

  • 模型优化,尽量减少模型的三角面数和顶点数。

  • 保持尽量少的材质数目,便于Unity进行批处理。

  • 使用纹理图集(一张大贴图中包含多个子贴图)来替代一系列单独的小贴图。

  • 使用代码操作材质时,尽量使用renderer.shareMaterial代替renderer.Material,因为后者的每一次改动都会创建一个新的材质。

  • 使用Mip Map(Mip Map在贴图的Import Setting中设置),但同时会增加内存。

  • 使用LOD,遮挡剔除(Occlusion culling)等技术。

  • 光照优化,尽量使用烘培灯光,预先烘培好场景的Lightmap。控制灯光的数量并且谨慎使用产生实时阴影的光。

B、GPU:光照性能

速度最快的方案是始终创建根本不需要计算的光照。要做到这一点,使用光照贴图只需一次“烘焙”静态光照,而无需每帧计算。生成光照贴图环境的过程只比在 Unity 场景中放置光源稍久一点,但是:

  • 运行速度要快得多(每像素 2 个光源的情况下,速度快 2–3 倍)
  • 视觉效果要好得多,因为可以烘焙全局光照,使光照贴图显得更平滑
    在许多情况下,可运用简单的技巧,无需添加多个额外的光照。例如,无需添加直接照入摄像机的光源来提供__边缘光照__效果,而是直接在着色器中添加专用的 Rim Lighting 计算(请参阅表面着色器示例以了解如何执行此操作)。

前向渲染中的光照

对于所有像素,动态光照会为每个受影响的像素增加渲染工作,可能导致对象在多个 pass 中被渲染。避免在性能较弱的设备(如移动端或低端 PC GPU)上使用多个__像素光照__来照射单个对象,应使用光照贴图实现静态对象的光照,而不是每帧计算其光照。每顶点动态光照可能会为顶点变换增加显著的工作量,因此尽量避免多个光源照射单个对象的情况。

避免组合距离足够远而需要受到不同像素光照影响的网格。使用像素光照时,每个网格必须渲染多次,因为只要发生像素光照就要进行渲染。如果组合两个相距很远的网格,则会增加组合对象的有效大小。照射该组合对象任何部分的所有像素光照在渲染期间都要考虑在内,因此需要创建的渲染 pass 的数量可能增加。通常情况下,为渲染组合对象而必须创建的 pass 数为每个单独对象的 pass 数之和,因此进行网格组合并不会获得任何好处。

在渲染过程中,Unity 会查找网格周围的所有光源,并计算出哪些光源对网格的影响最大。使用 Quality 窗口上的设置可修改多少个光源用于像素光照以及多少个用于顶点光照。每个光源根据它与网格的距离以及它的光照强度来计算其重要性;纯粹从游戏背景而言,有些光源比另一些光源更重要。鉴于此原因,每个光源都有 Render Mode 设置,可设置为 Important 或 Not Important__;标记为 Not Important__ 的光源具有较低的渲染开销。

示例:假设有一个驾驶游戏,玩家的汽车在黑暗中行驶,前照灯已打开。前照灯可能是游戏中视觉上最重要的光源,因此它们的 Render Mode 应设置为 Important。游戏中可能还有其他不太重要的光源,比如其他汽车的尾灯或远处的灯柱,这些光源不能通过像素光照来大幅改善视觉效果。这种情况下,可放心地将这些光源的 Render Mode 设置为 Not Important,从而避免将渲染能力浪费在无用之处。

通过优化每像素光照可以节省 CPU 和 GPU 工作量:CPU 的绘制调用将减少,而 GPU 要处理的顶点将减少,同时为所有其他对象渲染栅格化的像素也将减少。

C、GPU:纹理压缩和 Mipmap

使用压缩纹理可减小纹理的大小。这种做法可加快加载时间、减小内存占用并显著提高渲染性能。与未压缩的 32 位 RGBA 纹理所需的内存带宽相比,压缩纹理使用的内存带宽要小得多。

纹理 Mipmap
对于 3D 场景中使用的纹理,应始终启用 Generate mipmaps 选项。Mipmap 纹理使 GPU 能够为较小的三角形使用较低分辨率的纹理。这一点类似于纹理压缩可以帮助限制 GPU 渲染时传输的纹理数据量。

此规则的唯一例外是当已知纹理像素将 1:1 映射到渲染的屏幕像素时(与 UI 元素或在 2D 游戏中一样)。

D、GPU:LOD(细节级别)和每层剔除距离

剔除对象涉及使对象不可见。这是减轻 CPU 和 GPU 负载的有效方法。

在许多游戏中,在不影响玩家体验的情况下快速有效地执行此操作的方法是,相对于大对象,更激进地剔除小对象。例如,可让远处的小岩石和碎片不可见,而大型建筑物仍然保持可见。

有多种方式实现此目标:

  • 使用细节级别系统
  • 手动设置摄像机上的每层剔除距离
  • 将小对象放入单独一层,并使用 Camera.layerCullDistances 脚本函数设置每层剔除距离
E、GPU:实时阴影:

实时阴影很不错,但它们对性能有很大影响,同时会增加 CPU 的绘制调用次数和 GPU 的处理量。

F、GPU:编写高性能着色器的技巧

不同的平台具有截然不同的性能;与低端移动端 GPU 相比,高端 PC GPU 在图形和着色器方面的处理能力要高得多。即使在单一平台上也是如此;快速的 GPU 比慢速的集成 GPU 快几十倍。

移动平台和低端 PC 上的 GPU 性能可能远低于开发机器上的 GPU 性能。建议手动优化着色器以减少计算和纹理读取,从而在低端 GPU 机器上获得良好的性能。例如,某些内置的 Unity 着色器具有速度快得多但存在一些限制或近似处理的“移动端”等效项。

以下是移动端和低端 PC 显卡的一些指导原则:

复杂的数学运算
超越数学函数(例如 pow、exp、log、cos、 sin、tan)都很消耗资源,所以尽量避免使用它们。如果可能,请尽量考虑使用查找纹理作为复杂数学计算的替代方法。

避免编写自己的运算(如 normalize、dot、inversesqrt)。Unity 的内置选项确保驱动程序可以生成好得多的代码。请记住,Alpha 测试 (discard) 运算通常会使片元着色器变慢。

浮点精度
虽然浮点变量的精度(float 与 half 与 fixed) 在桌面平台 GPU 上很大程度上会被忽略,但在移动端 GPU 上 对于获得良好性能非常重要。

有关着色器性能的更多详细信息,请参阅着色器性能页面。

E、CPU:优化方向:

为了在屏幕上渲染对象,CPU 需要做很多处理工作:确定哪些光源影响该对象,设置着色器和着色器参数,向图形驱动程序发送绘制命令,而图形驱动程序随后将准备发送到显卡的命令。

所有这种基于“每个对象”的 CPU 使用率都是非常消耗资源的,所以如果有很多可见对象,影响就会累加起来。例如,如果有一千个三角形,如果它们都在一个网格中,而不是每个三角形在一个网格中(这种情况下加起来就有 1000 个网格),则 CPU 处理起来就比较容易。两种方案的 GPU 成本非常相似,但 CPU 完成渲染一千个对象(而不是一个)的工作要高得多。减少可见对象数量。要减少 CPU 需要执行的工作量,请执行以下操作:

  • 通过手动方式或使用 Unity 的绘制调用批处理将近处对象组合在一起。
  • 通过将单独的纹理放入更大的纹理图集,在对象中使用更少的材质。
  • 减少可能导致对象多次渲染的因素(例如反射、阴影和每像素光照)。

将对象组合在一起,使每个网格至少有几百个三角形,并使整个网格只使用一种材质。请注意,组合两个不共享材质的对象根本不会提高性能。需要多种材质的最常见原因是两个网格不共享相同的纹理;为了优化 CPU 性能,请确保组合的所有对象共享相同的纹理。

F、用于提高游戏运行速度的简单核对表
  • 在针对 PC 平台进行构建时,保持顶点数量低于 200K 和 3M/帧(具体值取决于目标 GPU)。
  • 如果要使用内置着色器,请从 Mobile 或 Unlit 类别中选取。这些类别也适用于非移动平台,但它们 - - 是更复杂着色器的简化和近似版本。
  • 保持每个场景使用较少的不同材质,并尽可能在不同对象之间共享材质。
  • 在非移动对象上设置 Static 属性以便允许内部优化,如静态批处理。
  • 只有一个(最好是方向性的)pixel light 影响几何体(而不是有多个)。
  • 烘焙光照而不是使用动态光照。
  • 尽可能使用压缩纹理格式,并使用 16 位纹理而非 32 位纹理。
  • 尽可能避免使用雾效。
  • 如果复杂的静态场景具有大量遮挡,使用遮挡剔除减少可见几何体数量和绘制调用次数。设计关卡 - - 时注意遮挡剔除。
  • 使用天空盒“伪造”远处的几何体。
  • 使用像素着色器或纹理组合器来混合多个纹理而不是使用多 pass 方法。
  • 尽可能使用 half 精度变量。
  • 最大限度减少在像素着色器中使用复杂的数学运算,例如 pow、sin 和 cos。
  • 每个片元使用更少的纹理。

第二章、绘制调用批处理

1、基本概念

A、UV

完整的说,其实应该是UVW(因为XYZ已经用过了,所以另选三个字母表示)。U和V分别是图片在显示器水平、垂直方向上的坐标,取值一般都是0~1,也 就是(水平方向的第U个像素/图片宽度,垂直方向的第V个像素/图片高度)。那W呢?贴图是二维的,何来三个坐标?嗯嗯,W的方向垂直于显示器表面,一般 用于程序贴图或者某些3D贴图技术(记住,确实有三维贴图这种概念!),对于游戏而言不常用到,所以一般我们就简称UV了。

B、光栅化

光栅化就是将一个图元转变为一个二维图像的过程,光栅化会根据三角形顶点的位置,来确定需要多少个像素点才能构成这个三角形。总的来说就是将几何信息转换成一个个的栅格组成的图像的过程,把顶点投影到屏幕空间进行渲染。

C、渲染管道、主要步骤

渲染管道是显示器上为了显示出图像而经过的一系列的必要操作,将几何物体从一个坐标系中变换到另一个坐标系中去。
主要步骤:本地坐标->视图坐标->光照->裁剪->投影->视图变换->光栅化

2、分析

要在屏幕上绘制游戏对象,引擎必须向图形 API(例如 OpenGL 或 Direct3D)发出绘制调用。绘制调用通常为资源密集型操作,图形 API 为每次绘制调用执行大量工作,从而导致 CPU 端的性能开销。此开销的主要原因是绘制调用之间的状态变化(例如切换到不同材质),而这种情况会导致图形驱动程序中执行资源密集型验证和转换步骤。

Unity 使用两种方法来应对此情况:

动态批处理:对于足够小的网格,此方法会在 CPU 上转换网格的顶点,将许多相似顶点组合在一起,并一次性绘制它们。
静态批处理:将静态(不移动)游戏对象组合成大网格,并以较快的速度渲染它们。
与手动合并游戏对象相比,内置批处理有几个好处;最值得注意的是,仍然可以单独剔除游戏对象。但是,也有一些缺点;静态批处理会导致内存和存储开销,动态批处理会产生一些 CPU 开销。

3、批处理的材质设置

  • 只有共享相同材质的游戏对象才可一起接受批处理。因此,如果想要实现良好批处理,应在尽可能多的不同游戏对象之间共享材质。
  • 如果两种相同材质仅在纹理上不同,可将这些纹理组合成单个大纹理。此过程通常称为纹理镶嵌。一旦纹理位于相同图集中,即可使用单个材质。
  • 如果需要从脚本访问共享材质属性,必须注意,修改 Renderer.material 将创建该材质的副本。应改用 Renderer.sharedMaterial 来保留共享的材质。
  • 阴影投射物即使材质不同,通常也可以在渲染时接受批处理。Unity 中的阴影投射物即使具有不同材质也可以使用动态批处理,只要阴影 pass 所需材质中的值相同即可。例如,许多板条箱可能使用具有不同纹理的材质,但是由于渲染纹理的阴影投射物不相关,所以在此情况下,它们可以一起接受批处理。

4、动态批处理(网格)

如果移动的游戏对象共享相同材质并满足其他条件,则 Unity 可自动在同一绘制调用中批处理这些游戏对象。动态批处理是自动完成的,无需您进行任何额外工作。

  • 批处理动态游戏对象在每个顶点都有一定开销,因此批处理仅会应用于总共包含不超过 900 个顶点属性且不超过 300 个顶点的网格。
    如果着色器使用顶点位置、法线和单个 UV,最多可以批处理 300 个顶点,而如果着色器使用顶点位置、法线、UV0、UV1 和切线,则只能批处理 180 个顶点。
    注意:将来可能会更改属性数量限制。
    这个地方为什么是300和180,按照900个顶点属性计算,900/(顶点位置、法线和单个 UV)=300。同样的,900/(顶点位置、法线、UV0、UV1 和切线)=180。

  • 如果游戏对象在变换中包含镜像,则不会对这些对象进行批处理(例如,具有 +1 缩放的游戏对象 A 和具有 –1 缩放的游戏对象 B 无法一起接受批处理)。即分别拥有缩放尺度(1,1,1)和(2,2,2)的两个物体将不会进行批处理。统一缩放尺度的物体不会与非统一缩放尺度的物体进行批处理。使用缩放尺度(1,1,1)和 (1,2,1)的两个物体将不会进行批处理,但是使用缩放尺度(1,2,1)和(1,3,1)的两个物体将可以进行批处理。
    DrawCall分析流程来分析一下:
    1、渲染A,使用材质1
    2、渲染B,使用材质1
    3、渲染C,使用材质2
      在这种情况下是2个DrawCall,在下面这种情况下,则是3个DrawCall
    1、渲染A,使用材质1
    2、渲染C,使用材质2
    3、渲染B,使用材质1

  • 即使游戏对象基本相同,使用不同的材质实例也会导致游戏对象不能一起接受批处理。例外情况是阴影投射物渲染。

  • 带有光照贴图的游戏对象具有其他渲染器参数:光照贴图索引和光照贴图偏移/缩放。通常,动态光照贴图的游戏对象应指向要批处理的完全相同的光照贴图位置。

  • 多 pass 着色器会中断批处理。

    • 几乎所有的 Unity 着色器都支持前向渲染中的多个光照,有效地为它们执行额外 pass。“其他每像素光照”的绘制调用不进行批处理。
    • 旧版延迟(光照 pre-pass)渲染路径会禁用动态批处理,因为它必须绘制两次游戏对象。

因为动态批处理的工作原理是将所有游戏对象顶点转换到 CPU 上的世界空间,所以仅在该工作小于进行绘制调用的情况下,才有优势。绘制调用的资源需求取决于许多因素,主要是使用的图形 API。例如,对于游戏主机或诸如 Apple Metal 之类的现代 API,绘制调用的开销通常低得多,通常动态批处理根本没有优势。

5、动态批处理(粒子系统、线渲染器、轨迹渲染器)

动态批处理在用于具有 Unity 动态生成的几何体的组件时,其工作方式与用于网格时不同。

  • 对于每个兼容的渲染器类型,Unity 将所有可批处理的内容构建为 1 个大型顶点缓冲区。
  • 渲染器设置材质状态以用于批处理。
  • Unity 将顶点缓冲区绑定到图形设备。
  • 对于批处理中的每个渲染器,Unity 将偏移更新到顶点缓冲区中,然后提交新的绘制调用。

在衡量图形设备调用的成本时,渲染组件时的最慢部分是材质状态的设置。相比之下,将不同偏移处的绘制调用提交到共享顶点缓冲区中的速度非常快。

这种方法与 Unity 在使用静态批处理时提交绘制调用的方式非常相似。

6、静态批处理

使用静态批处理,引擎可减少任何大小的几何体的绘制调用,但前提是它共享相同材质并且不移动。这种处理方式通常比动态批处理更高效(它不会在 CPU 上转换顶点),但是使用更多内存。

为了利用静态批处理,您需要显式指定某些游戏对象是静态对象且不会在游戏中移动、旋转或缩放。为此,请使用 Inspector 中的 Static 复选框,将游戏对象标记为静态。

在内部,静态批处理的工作原理是将静态游戏对象转换到世界空间并为它们构建一个共享的顶点和索引缓冲区。如果已启用 Optimized Mesh Data__(在 Player__ 设置中),则 Unity 会在构建顶点缓冲区时删除任何着色器变体未使用的任何顶点元素。为了执行此操作,系统会进行一些特殊的关键字检查;例如,如果 Unity 未检测到 LIGHTMAP_ON 关键字,则会从批处理中删除光照贴图 UV。然后,针对同一批次中的可见游戏对象,Unity 会执行一系列简单的绘制调用,每次调用之间几乎没有状态变化。在技术上,Unity 不会减少 API 绘制调用,而是减少它们之间的状态变化(这正是消耗大量资源的部分)。在大多数平台上,批处理限制为 64k 个顶点和 64k 个索引(OpenGLES 上为 48k 个索引,在 macOS 上为 32k 个索引)。

7、提示

当前,仅对网格渲染器、轨迹渲染器、线渲染器、粒子系统和精灵渲染器进行批处理。这意味着不会对蒙皮网格、布料和其他类型的渲染组件进行批处理
渲染器仅与其他相同类型的渲染器一起接受批处理。

半透明着色器通常要求游戏对象按照从后到前的顺序进行渲染,从而实现透明性。Unity 首先按此顺序对游戏对象排序,然后尝试对它们进行批处理,但是因为必须严格满足顺序,所以这通常意味着可以实现比不透明游戏对象更少的批处理。

手动组合彼此接近的游戏对象可以是绘制调用批处理的极好替代方法。例如,一个带有大量抽屉的静态橱柜通常只需在 3D 建模应用程序中或者使用 Mesh.CombineMeshes 来组合成一个网格。

第三章、角色建模的性能优化

1、使用单个带蒙皮的网格渲染器

对于每个角色,仅使用单个蒙皮网格渲染器。Unity 使用可见性剔除和包围体更新来优化动画,这些优化仅在您将单个动画组件和单个蒙皮网格渲染器组合使用时才会生效。若使用两个蒙皮网格,模型的渲染时间大概会是单个网格的两倍,并且这样做很少能带来实际的意义。

2、使用尽可能少的材质

您同样应该尽可能减少每个网格上的材质数量。只有在需要为角色的不同的部分使用不同着色器(例如,眼睛的特殊着色器)时,您才应该考虑使用多种材质。其余大多数情况下,每个角色有两到三种材质就应该足够了。

3、使用尽可能少的骨骼

典型的桌面游戏中的骨骼层级视图大概拥有 15 到 60 根骨骼。骨骼的数量越少,性能就越好。在使用大约 30 根骨骼的情况下,桌面平台上可以获得极佳质量,移动平台上也能获得比较好的质量。理想情况下,移动设备游戏的骨骼数量应保持在 30 根以下,桌面游戏的骨骼数量也不要超出 30 根太多。

4、多边形数量

您应该使用的多边形数量取决于您需要达到的质量,以及您的目标平台。对于移动设备而言,每个网格 300 到 1500 个多边形就可以取得良好的效果,而对于桌面平台,理想的数量范围大约为 1500 到 4000。如果游戏经常会同屏内出现大量角色,那么您可能需要减少每个网格的多边形数量。

5、正向和反向动力学保持分离

在导入动画时,模型的反向动力学 (IK) 节点将被烘焙为正向动力学 (FK),因此 Unity 根本不需要 IK 节点。但是,如果它们留在了模型中,那么即使它们不影响动画,也会让 CPU 产生额外开销。您可以根据自己的偏好,删除 Unity 或建模工具中的冗余 IK 节点。理想情况下,应在建模期间保留单独的 IK 和 FK 层级视图,以便在必要时更轻松地删除 IK 节点。

A、反向动力学(IK)

反向动力学,就是子骨骼节点带动父骨骼节点运动。一般在模型制作的时候删除。导入Unity如何删除暂时没有找到方法。

第四章、最佳实践指南

1、性能分析

-[UnityAppController startUnity]
|- [UnityInitApplicationGraphics]
|- [UnityLoadApplication]

UnityInitApplicationGraphics 执行大量内部工作,例如设置图形设备和初始化 Unity 的大量内部系统。此外,它还初始化资源系统 (Resources system)。为此,它必须加载资源系统包含的所有文件的索引。
每个“Resources”文件夹中的每个资源文件 (1)(注意: 这仅适用于项目“Assets”文件夹中名为“Resources”的文件夹,以及这些“Resources”文件夹中的所有子文件夹。)都作为资源系统的数据。因此,初始化资源系统所需的时间与“Resources”文件夹中的文件数量呈线性关系。

UnityLoadApplication 包含加载并初始化项目的第一个场景的方法。它包括反序列化并实例化显示第一个场景所需的所有数据,例如编译着色器、上传纹理和实例化游戏对象。此外,第一个场景中的所有 MonoBehaviour 都在此时执行 Awake 回调。
这就意味着,如果在项目的第一个场景中的 Awake 回调中存在任何执行时间很长的代码,那么该代码可能会导致项目的初始启动时间的延长。解决此问题的方法是删除这些运行速度慢的代码,或者在应用程序生命周期的其他地方执行该代码。

2、内存分析

MemoryProfiler

3、协程

DelayedCallManager

4、资源审核

A、代码审核(AssetPostprocessor )

Unity Editor 中的 AssetPostprocessor 类可用于在 Unity 项目上强制执行某些最低标准。导入资源时将回调此类。要使用此类,应继承 AssetPostprocessor 并实现一个或多个 OnPreprocess 方法。重要的方法包括:

  • OnPreprocessTexture
  • OnPreprocessModel
  • OnPreprocessAnimation
  • OnPreprocessAudio
B、通用资源规则
a、纹理
(1)禁用 read/write enabled 标志

Read/Write enabled 标志使纹理在内存中保留两次:一次保存在 GPU 中,一次保存在 CPU 可寻址内存中(1)(注意: 这是因为大多数平台上从 GPU 内存回读的速度极慢。将纹理从 GPU 内存读入临时缓冲区以供 CPU 代码(例如 Texture.GetPixel)使用将是非常低效的)。在 Unity 中,默认情况下禁用此设置,但可能会无意中将其打开。

只有在着色器之外操作纹理数据时(例如使用 Texture.GetPixel 和 Texture.SetPixel API 时)才需要 Read/Write Enabled,否则应尽可能避免使用它。

(2)尽可能禁用 Mipmap

如果对象相对于摄像机具有相对不变的 Z 深度,则可禁用 Mipmap,这样将大约节省加载纹理所需的内存的三分之一。如果对象的 Z 深度会发生变更,则禁用 Mipmap 可能导致 GPU 上的纹理采样性能变差。

通常情况下,这对于 UI 纹理以及在屏幕上以恒定大小显示的其他纹理非常有用。
注意:这里为什么禁用Mipmap是大约节省三分之一。这是因为:Mipmap的原理是把一张贴图按照2的倍数进行缩小。直到1X1。也即使说,对于一张10241024的32位png(大小是4M,即102410244)来说,后续的大小是:
512
512=1
256256=1/4
128
128=1/16
6464=1/64
32
32=1/256
1616=1/1024
8
8=1/4098
44=1/40984
22=1/409816
11=1/409864

这样算下来,约等于1.33333,儿1.33333/4也约等于1/3。因此有节约三分之一的说话。

(3)压缩所有纹理

使用适合项目目标平台的纹理压缩格式对于节省内存至关重要。
如果所选的纹理压缩格式不适合目标平台,Unity 会在加载纹理时解压缩纹理,这将消耗 CPU 时间和额外的内存。此问题在 Android 设备上最常见,因为此类平台通常因芯片组不同而支持截然不同的纹理压缩格式。

(4)实施合理的纹理大小限制

虽然很简单,但也很容易忘记调整纹理大小或无意中更改纹理大小导入设置。应确定不同类型纹理的合理最大值,并通过代码强制执行这些限制规则。
对于许多移动应用程序,2048x2048 或 1024x1024 足以满足纹理图集的要求,而 512x512 足以满足应用于 3D 模型的纹理的要求。

ETC2 主要是对于NPOT却是4的倍数的贴图有较大压缩,比如一个1920X1080RGB的Loading图,ETC压缩下不管用大小5.9M,ETC2下压缩为1M

ETC2支持透明度的,ETC1不支持,现在基本上所有的手机都支持ETC2了。

b、模型
(1)禁用 Read/Write enabled 标志

模型的 Read/Write enabled 标志与纹理的上述相应标志具有相同的工作原理。但是,模型在默认情况下会启用该标志。
如果项目在运行时通过脚本修改网格 (Mesh),或者如果网格用作 MeshCollider 组件的基础,则 Unity 会要求启用此标志。如果模型未在 MeshCollider 中使用并且未被脚本操纵,请禁用此标志以节省一半模型内存。

(2)在非角色模型上禁用骨架

认情况下,Unity 会为非角色模型导入通用骨架。如果模型在运行时实例化,则会导致添加 Animator 组件。如果模型没有通过动画系统进行动画处理,则会给动画系统增加不必要的开销,因为每帧都必须运行一次所有激活的 Animator。
在非动画模型上禁用骨架可以避免自动添加 Animator 组件,并防止可能无意中向场景添加不需要的 Animator。

(3)在动画模型上启用 Optimize Game Objects 选项

Optimize Game Objects 选项对动画模型有着显著的性能影响。禁用该选项后,Unity 会在每次实例化模型时创建一个大型变换层级视图来镜像模型的骨骼结构。此变换层级视图的更新成本很高,尤其是在附加了其他组件(如粒子系统或碰撞体)的情况下。它还限制了 Unity 通过多线程执行网格蒙皮和骨骼动画计算的能力。
如果需要暴露模型骨骼结构上的特定位置(例如暴露模型的双手以便动态附加武器模型),则可在 Extra Transforms 列表中将这些位置专门列入白名单。

(4)尽可能使用网格压缩

启用网格压缩可减少用于表示模型数据不同通道的浮点数的位数。这样做可能导致精确度的轻微损失,在用于最终项目之前,美术师应评估这种不精确性的影响。

在给定压缩级别中使用的具体位数在 ModelImporterMeshCompression 脚本参考中有详细说明。

请注意,可对不同的通道使用不同级别的压缩,因此项目可选择仅压缩切线和法线,同时保持 UV 和顶点位置不压缩。

(5)注意网格渲染器设置

将网格渲染器添加到预制件或游戏对象时,请注意组件上的设置。默认情况下,Unity 会启用阴影投射和接收、光照探针采样、反射探针采样和运动矢量计算。

如果项目不需要这些功能中的一个或多个,请确保通过自动脚本关闭它们。添加网格渲染器的任何运行时代码也都需要处理这些设置。

对于 2D 游戏,在启用阴影选项的情况下意外地将网格渲染器添加到场景会为渲染循环添加完整的阴影 pass。通常情况下,这是对性能的浪费。

c、音频
(1)适合平台的压缩设置

应为音频启用与可用硬件匹配的压缩格式。所有 iOS 设备都包含硬件 MP3 解压器,而许多 Android 设备本身支持 Vorbis。

此外,应将未压缩的音频文件导入 Unity。Unity 在构建项目时总是会重新压缩音频。无需导入压缩的音频再重新压缩,这只会降低最终音频剪辑的质量。

(2)将音频剪辑强制设置为单声道

很少有移动设备实际配备立体声扬声器。在移动平台项目中,将导入的音频剪辑强制设置为单声道会使其内存消耗减半。此设置也适用于没有立体声效果的任何音频,例如大多数 UI 声音效果。

(3)将音频剪辑强制设置为单声道

尽量降低音频文件的比特率,以进一步节省内存消耗和构建的项目大小,但这种情况需要咨询音频设计师。

(4)怎么选音频

Load Type的各个选项:

  • Compressed In Memory – 音频剪辑将存储在RAM中,播放时将解压缩,播放时不需要额外的存储
  • Streaming –音频永久存在设备上(硬盘或闪存上) ,播放流媒体方式. 不需要RAM进行存储或播放。
  • Decompress On Load – 未压缩的音频将存储在RAM中。这个选项需要的内存最多,但是播放它不会像其他选项那样需要太多的CPU电源。

长音频播放消耗大量内存,如果播放时不想在内存中进行解压,有两个选择:

  • Load Type选“Streaming”, Compression Format 选”Vorbis",使用最少的内存,但需要更多的CPU电量和硬盘I/O操作;
  • Load Type选“Compressed In Memory”, Compression Format 选”Vorbis",磁盘I/O操作被替换成内存的消耗,请注意,要调整“Quaility”滑块以减小压缩剪辑的大小,以交换音质,一般推荐70%左右。

其他情况的选择方式:

  • 对于经常播放的和短的音频剪辑,使用“Decompress On Load”和“PCM或ADPCM"压缩格式。当选择PCM时,不需要解压缩,如果音频剪辑很短,它将很快加载。你也可以使用ADPCM。它需要解压,但解压比Vorbis快得多。
  • 对于经常播放,中等大小的音频剪辑使用”Compressed In Memory“和”ADPCM“压缩格式,比原始PCM小3.5倍,解压算法的CPU消耗量不会像vorbis消耗那么多CPU。
  • 对于很少播放并且长度比较短的声音剪辑,使用”Compressed In Memory"和”ADPCM“压缩格式,比原始PCM小3.5倍,解压算法的CPU消耗量不会像vorbis消耗那么多CPU。
  • 对于很少播放中等大小的音频,使用”Compressed In Memory“ 和Vorbis压缩格式。这个音频可能太长,无法使用adpcm存储,播放太少,因此解压缩所需的额外CPU电量不会太多。

5托管堆

a、了解托管堆

许多 Unity 开发者面临的另一个常见问题是托管堆的意外扩展。在 Unity 中,托管堆的扩展比收缩容易得多。此外,Unity 的垃圾收集策略往往会使内存碎片化,因此可能阻止大型堆的收缩。

b、托管堆的工作原理及其扩展原因

“托管堆”是由项目脚本运行时(Mono 或 IL2CPP)的内存管理器自动管理的一段内存。必须在托管堆上分配托管代码中创建的所有对象(2)(__注意:__严格来说,必须在托管堆上分配所有非 null 引用类型对象和所有装箱值类型对象)。
在这里插入图片描述
在上图中,白框表示分配给托管堆的内存量,而其中的彩色框表示存储在托管堆的内存空间中的数据值。当需要更多值时,将从托管堆中分配更多空间。
垃圾回收器定期运行(3)(__注意:__具体运行时间视平台而定)。这时将扫描堆上的所有对象,将任何不再引用的对象标记为删除。然后会删除未引用的对象,从而释放内存。

至关重要的是,Unity 的垃圾收集(使用 Boehm GC 算法)是非分代的,也是非压缩的。“非分代”意味着 GC 在

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值