前言

观看 bilibili @Metaverse大衍神君 视频记录笔记,是本来作者留下的笔记经整理也有自己不太懂的地方扩展延伸,如有错误还请海涵指正!

环境搭建

使用如下命令拉起测试项目并使用 Unity 打开

git clone https://github.com/lwwhb/Unity2022_SUNTAIL_Stylized_Fantasy_Village_Optimization

个人使用的 2022.1.16 版本打开,会出现一个 com.unity.render-pipelines.universal 的包错误,个人解决方案是在 PackageManage 里删除这个包后,Unity 会自动提示升级材质,点击后会自动安装新版的 URP 包,也会自动配置好渲染管线

{% note warning%} 注意这里我将项目设置为了 URP(SRP) 渲染管线会导致下面无法查看场景 Mipmap 选项,在视频教程中的内置渲染管线(built-in)可以查看 {% endnote %}

打开项目的 Autoconnect Profiler 并在场景加入 DebugCanvas 方便移动设备的调试

关闭移动平台的 VSync Count 选项,以发挥最大性能

{% note primary %}

VSync Count:每帧之间应传递的垂直同步数量 Unity 通过将平台的默认目标帧速率除以 VSyncCount 的值来计算目标帧速率

{% endnote %}

未优化时的性能指标

  • 生成的 Android APK大小 550M
  • 三角形平均面熟 150-200 万,峰值 230 万
  • 渲染批次 1500-1800 次
  • SetPassCall 200 次以上
  • 小米11 ultra平均 FPS :10 FPS,iPhone XS Max 15FPS
  • 内存 小米 11 ultra 1.5 GB, iPhone XS Max 1 GB
  • 纹理资源小米 11 ultra 670M, iPhone XS Max 530M
  • Mesh资源小米 11 ultra 423M,iPhone XS Max 423M
  • 音效资源小米 11 ultra 76M,iPhone XS Max 76M

静态优化

导入 - 创建 - 运行 - 分发 - 加载

Import

内部导入如:prefab、animation、timeline、rendertexture 等 外部导入如:模型网格 Mesh、纹理、音乐音效、字体、动画视频等

合理的资源导入设置、规格在不同平台下表现不同

在此使用 Unity UPR 性能测试解决方案,查看我们导入的资源存在哪些问题,正常注册、创建项目、下载 Asset Checker

以个人 Mac 为例:

cd 到刚下载的路径下终端输入:./assetcheck --h 可以查看到相关帮助信息

之后配置联系到项目:./assetcheck --project=<项目路径> --projectId=<项目ID>

{% note info %}

项目 ID:在创建的 UPR 项目下点击,可以查看到相关项目包名和 Project ID(项目 ID)

{% endnote %}

这样稍等一会,我们就可以在 UPR 项目中查看到我们的项目报告以便分析

下面分析项目报告

Audio

  • 启用 Force to Mono

将双声道相同的音频在不丢失内容的情况下,可强制改为单声道以节约内存

{% note primary %}

Compression Format

PCM: 它是一种无损、未压缩的编解码器和格式,在Unity中播放也几乎不需要CPU处理,因为它不需要解压缩,所以在构建项目的时候大小大幅增加,这特别是对移动开发不太友好,

一个附带说明是,Unity 似乎正在对一些音频文件进行自己的无损压缩,在选择 PCM 格式下,音频还是会有不同程度的压缩(大概 65% - 100%,但几乎没有影响到质量)

ADPCM: ADPCM 的算法在 Unity 中提供了固定的压缩比,这导致文件比原始 PCM 音频小约3.5倍

优势:与 PCM 一样,解压缩或解码此编解码器的成本非常轻,与 PCM 相比只是多一点,以换取大幅减小的文件大小 劣势:缺点是压缩方法本身,ADPCM 压缩音频数据的方式偶尔会导致原始文件中没有的失真或“额外噪音”

Vorbis: Vorbis是一种完全开放的、非专有的、免专利和免版税的通用音频格式。这是一个非常高效的音频编解码器,提供高质量的有损压缩,在 Unity 中还有一个质量滑块,这使我们能够决定要降低多少音频质量

MP3: 用于有损压缩的高效音频编解码器,与 Vorbis 相似。它可以将音频压缩到非常小的尺寸,而不会造成太大的质量损失,但有一个主要区别:MP3 不能无缝循环。如果想让游戏中的声音循环,如音乐或氛围,最好不要选择此压缩格式

在 IOS 平台上可能会对 MP3 格式有特殊优化,一般来说,建议尽可能选择 Vorbis 而不是MP3,因为 Vorbis 以相同的比特率提供更高效的编码,这意味着将以相同的文件大小获得更高的质量{% endnote %}

  • 优化采样率

Sample Rate Settings:定义了单位时间内从连续信号中提取并组成离散信号的采样个数

  • 加载类型

Decompress On Load:

会将音频数据解压缩并解码到内存的原始大小,使其能够以最少的 CPU 使用率按需播放。这样做的权衡是,音频数据将占用内存中的最大空间,这就是为什么它只适用于经常播放并需要在很短的时间内准备好的小型音频文件,如脚步声、UI 声音、枪声和其他武器声音等

Compressed In Memory:

会将音频数据以压缩状态存储在内存中,但在播放时需要 CPU 进行解压缩和解码。这样做的优点是,存储介质没有读取时间和内存使用量小,每次播放时使用多一点 CPU 来解压和解码。对于不经常播放的声音,如对象交互声音、随机氛围声音和其他许多声音,建议使用此设置

Streaming:

当音频数据实时从存储介质中读取时,会进行解码和解压缩。这具有最低的内存使用率,但最高的 CPU 使用率。这将相当慢,因为 CPU 必须首先从存储介质中读取,这比内存慢。因此,此选项仅建议在每个场景的一个或两个音频文件上使用。大多数情况下,此设置将用于音乐或氛围文件,因为这些资产很大,不希望它们占用可用于其他东西的宝贵内存资源

{% note info %}

为什么 PCM 总是最终格式? 通常播放编码文件(无论是音频还是视频)的应用程序需要将该文件解码为目标设备可以在标准端口上输出的格式,大多数音频设备都在等待 PCM 数据。解码器很可能会集成到软件中,或者在极少数情况下,您将有实际的硬件或芯片接管解码过程来缓解 CPU

{% endnote %}

  • 删除 Audio Source

对于用不到的音视频文件,应采用删除释放内存而不是简单的将音量设置为 0

FBX

DCC 中模型导出

  • Unity 支持多种标准和专有模型文件格式(DCC)Unity 内部使用 .fbx 文件格式作为其导入链。最佳做法尽可能使用 .fbx 文件格式,并且不应在生产中使用专有文件格式

  • 优化原始导入模型文件,删除不需要的数据

    • 统一单位
    • 导出的网格必须是多边形拓扑网格,不能是贝塞尔曲线、样条曲线、NURBS、NURMS、细分曲面等
    • 烘培 Deformers, 在导出之前,确保变形体被烘培到网格模型上,如骨骼形变烘培到蒙皮权重上
    • 不建议模型使用到的纹理随模型导出
    • 如果你需要导入 blend shape normals,必须要指定光滑组 smooth groups
    • DCC 导出面板设置, 不建议携带场景信息导出,如不建议导出摄像机、灯光、材质等信息,因为这些的信息与 Unity 内默认都不同。除非你自己为某 DCC 做过自定义导出插件

原始模型文件对性能的影响点

  • 最小化面数,不要使用微三角形,分布尽量均匀
  • 合理的网络拓扑和平滑组
  • 尽量少的使用材质个数
  • 尽可能少的使用蒙皮网格
  • 尽可能少的骨骼数量
  • FK 与 IK 节点没分离,IK 节点没删除

模型优化

  • 尽可能的将网格合并到一起
  • 尽可能使用共享材质
  • 不要使用网格碰撞体
  • 不必要不要开启网格读写
  • 使用合理的 LOD 级别
  • Skin Weights 受骨骼影响个过多
  • 合理压缩网格
  • 不需要 rigs 和 BlendShapes 尽量关闭
  • 如果可能,禁用法线或切线
  • 多套模型

资源检查报告

  • 其中两项建议与模型动画有关,而测试项目中所有模型资源都不涉及动画,可以将 Rig 标签下的 Animation Type 设置为 None,并关闭 Animation 标签下的 Import Animations 选项,设置 Materials 标签中的 Material Creation Mode为 None

  • 开启 Project Settings —> Player —>Optimization下的 Vertex Compression (定点通道压缩,会降低精度)与 Optimize Mesh Data (删除未使用的网格组建)选项

纹理

Texture types:

  • Default

    用于所有纹理的最常见纹理类型,它提供对纹理导入的大多数属性的访问

  • Normal map

    将颜色通道转换为适合实时正常映射的格式

  • Editor GUI and Legacy GUI

    在任何 HUD 或 GUI 控件上使用纹理,对于此纹理类型,纹理形状属性始终设置为2D

  • Cursor

    使用纹理作为自定义光标,此纹理类型的纹理形状属性始终设置为 2D

  • Sprite (2D and UI)

    通常作用于 2D 游戏和 UI 中,对于此纹理类型纹理形状属性始终设置为 2D

  • Cookie

    使用内置渲染管道中用于 cookie 的基本参数设置纹理,使用此纹理类型,Unity 根据选定的光类型自动更新纹理形状属性

    • 定向和聚光光源的 cookies 总是 2D 纹理
    • 点光源 cookies 必须是 cubemaps(立方体类型)

    {% note info %} cubemaps:六个正方形纹理的集合,可以表示环境中的反射或几何图形后面绘制的天空框。六个正方形构成了围绕物体的虚构立方体的面;每个面代表沿着世界轴方向(向上、向下、向左、向右、向前和向后)的视图 {% endnote %}

  • Lightmap

    此选项允许编码为特定格式(RGBM 或 dLDR,取决于平台)和后处理,纹理数据的步骤(推拉膨胀通道)。对于此纹理类型,纹理形状属性始终设置为 2D。

  • Single Channel

    如果在纹理中只需要一个通道,使用此纹理类型,还可以更改纹理形状属性以定义纹理形状

Texture Shape:

  • 2D 最常用的 2D 纹理,默认选项
  • Cube 一般用于天空和与反射探针,默认支持 Default、Normal、Single Channel 几种类型纹理,可以通过 Assets > Create > Legacy > Cubemap 生成,也可以通过 C# 代码 Camera.RenderToCubemap 在脚本中生成
  • 2D Array 纹理数组,可以极大提高大量相同大小和格式的纹理访问效率,但需要特定平台支持,可以通过引擎 SystemInfo.supports2DArrayTextures 接口运行时查看是否支持
  • 3D 通过纹理位图方式存储或传递一些3D结构化数据,一般用于体积仿真,如雾效、噪声、体积数据、距离场、动画数据等信息,可以外部导入,也可运行时程序化创建

Alpha Source:

默认选择 Input Texture Alpha 就好,如果确定不使用原图中的 Alpha 通道,可以选择 None,另外 From Gray Scale 我们一般不会选用

Alpha Is Transparency:

指定 Alpha 通道是否开启半透明,如果位图像素不关心是否要半透明可以不开启此选项。这样 Alpha 信息只需要占 1bit 以节省内存

Ignore Png file gamma:

是否忽略 png 文件中的 gamma 属性,这个选项是否忽略取决于 png 文件中设置不同 gamma 属性导致的显示不正常,一般原图制作流程没有特殊设置,这个选项一般默认就好

Read / Write

开启此选项会导致内存量增加一倍,默认我们都是不开启,除非你的脚本逻辑中需要动态读写该纹理时需要打开此选项


纹理的大小都是 2 的幂次方,如果纹理不符合则是自动化为最近的 2 的幂次方大小,这可能会造成纹理浪费,不符合 Unity 压缩格式,纹理大小也直接影响内存和显存大小

选择合适纹理大小应尽量遵循以下经验:

  • 不同平台、不同硬件配置选择不同的纹理大小,Unity下可以采用 bundle 变体设置多套资源、通过 Mipmap 限制不同平台加载不同 level 层级的贴图

  • 根据纹理用途的不同选择不同的纹理加载方式,如流式纹理加载 Texture Streaming、稀疏纹理 Sparse Texture、虚拟纹理 VirtualTexture 等方式

  • 不能让美术人员通过增加纹理大小的方式增加细节,可以选择细节贴图 DetailMap 或增加高反差保留的方式

  • 在不降低视觉效果的情况下尽量减小贴图大小,最好的方式是纹理映射的每一个纹素的大小正好符合屏幕上显示像素的大小,如果纹理小了会造成欠采样,纹理显示模糊,如果纹理大了会造成过采样,纹理显示噪点。这一点做到完美平衡很难保障,可以充分利用 Unity 编辑器下 SceneView -> DrawMode -> Mipmap 来查看在游戏摄像机视角下哪些纹理过采样,哪些纹理欠采样,进而来调整纹理大小

纹理颜色空间:

默认大多数图像处理工具都会使用 sRGB 颜色空间处理和导出纹理。但如果你的纹理不是用作颜色信息的话,那就不要使用 sRGB 空间,如金属度贴图、粗糙度贴图、高度图或者法线贴图等。一旦这些纹理使用 sRGB 空间会造成视觉表现错误

{% note primary%} 什么是颜色(位)的深度? 图像的颜色(位)深度描述了使用多少位数据来存储每个像素的颜色值。图像的位深度越大,可以使用的颜色就越多(但不一定存在)这些位通常分布在多个通道上,以产生最终的颜色

{% endnote %}

DepthDistributionColors
8-bit (RGB)R3 G3 B228 * 8 * 8
16-bit (RGBA)R3 G3 B2 A828 * 8 * 8 + alpha
16-bit (RGB)R5 G6 B5216 * 16 * 16
24-bit (RGBA)R5 G6 B5 A8216 * 16 * 16 + alpha
24-bit (RGB)R8 G8 B8224 * 24 * 24
32-bit (RGB)R8 G8 B8 A8232 * 32 * 32

纹理压缩:

https://docs.unity3d.com/Manual/class-TextureImporterOverride.html

纹理压缩是指图像压缩算法,保持贴图视觉质量的同时,尽量减小纹理数据的大小。默认情况下我们的纹理原始格式采用 PNG 或 TGA 这类通用文件格式,但与专用图像格式相比他们访问和采样速度都比较慢,无法通用 GPU 硬件加速,同时纹理数据量大,占用内存较高。所以在渲染中我们会采用一些硬件支持的纹理压缩格式,如 ASTC 、ETC、ETC2、DXT 等

纹理图集:

纹理图集是一系列小纹理图像的集合

  • 优点:

    一是采用共同纹理图集的多个静态网格资源可以进行静态合批处理,减少DrawCall 调用次数

    二是纹理图集可以减少碎纹理过多,因为他们打包在一个图集里,通过压缩可以更有效的利用压缩,降低纹理的内存成本和冗余数据

  • 缺点

    美术需要合理规划模型,并且要求模型有相同的材质着色器,或需要制作通道图去区分不同材质,制作和修改成本较高

纹理过滤:

  • Nearest Point Filtering:临近点采样过滤最简单、计算量最小的纹理过滤形式,但在近距离观察时,纹理会呈现块状

  • Bilinear Filtering:双线性采样过滤会对临近纹素采样并插值化处理,对纹理像素进行着色。双线性过滤会让像素看上去平滑渐变,但近距离观察时,纹理会变得模糊

  • Trilinear Filtering:三线性过滤除与双线性过滤相同部分外,还增加了Mipmap等级之间的采样差值混合,用来平滑过度消除Mipmap之间的明显变化

  • Anisotropic Filtering:各向异性过滤可以改善纹理在倾斜角度下的视觉效果,跟适合用于地表纹理

纹理 Mipmap:

逐级减低分辨率来保存纹理副本。相当于生成了纹理 LOD,渲染纹理时,将根据像素在屏幕中占据的纹理空间大小选择合适的 Mipmap 级别进行采样

{% note info %} Generate Mip Maps

什么时候不需要生成 MipMaps?

  1. 2D场景
  2. 固定视角,摄像机无法缩放远近
  • Border Mip Maps 默认不开启,只有当纹理的是 Light Cookies 类型时,开启此选项来避免 colors bleeding 现象导致颜色渗透到较低级别的 Mip Level 纹理边缘上

  • MipMap Filtering

    • Box 最简单,随尺寸减小,Mipmap 纹理变得平滑模糊
    • Kaiser,避免平滑模糊的锐化过滤算法
  • Mip Maps Preserve Coverage,只有需要纹理在开启 mipmap 后也需要做 Alpha Coverage 时开启。默认不开启

  • Fadeout MipMaps 纹理 Mipmap 随 Mip 层级淡化为灰色,一般不开启,只有在雾效较大时开启不影响视觉效果

{% endnote %}

{% note primary%} 选择合适纹理过滤的最佳经验

  • 使用双线性过滤平衡性能和视觉质量

  • 有选择地使用三线性过滤,因为与双线性过滤相比,它需要更多的内存带宽

  • 使用双线性和 2x 各向异性过滤,而不是三线性和 1x 各向异性过滤,因为这样做不仅视觉效果更好,而且性能也更高

  • 保持较低的各向异性级别。仅对关键游戏资源使用高于 2 的级别 {% endnote %}

  • 优点:

    GPU 不需要在远距离上对对象进行全分辨率纹理采样,因此可以提高纹理采样性能

    同时也解决了远距离下的过采样导致的噪点问题,提高的纹理渲染质量

  • 缺点:

    由于 Mipmap 纹理要生成低分辨率副本,会造成额外的内存开销(可使用 MipmapStreaming 来优化)

其他可能有问题的纹理类型:

  • 纹理图集大小设置不合理,图集利用率低
  • 大量只有颜色差异的图片
  • UI 背景贴图而不采用 9 宫格缩放的图
  • 纯色图没有使用 Single Channel
  • 不合理的半透明 UI,占据大量屏幕区域,造成 Overdraw 开销
  • 大量 2D 序列帧动画,而且图片大,还不打图集
  • 不合理的通道图利用方案
  • 大量渐变色贴图,没有采用 1 像素过渡图,也不采用 Single Channel,粒子特效中较为常见

动画

info : Animation Compression In Unity

Rig 标签:

Animation Type

  • None :无动画
  • Legacy :Unity 3.0 之前的旧版动画,为了向后兼容,该系统仍然可用。使用遗留动画的主要原因是继续处理旧项目,而无需为 Mecanim 更新它,但不建议在新项目使用它
  • Generic :通用骨骼框架
  • Humanoid: 人形骨骼框架

{% note info %} Unity 有一个名为 Mecanim 的丰富而复杂的动画系统,它提供:简单的工作流程和人形角色动画设置;动画重新定位并将动画从一个角色模型应用到另一个角色模型的能力。 {% endnote %}

选择原则

  • 无动画选择 None
  • 非人形动画选择 Generic
  • 人形动画
    • 人形动画需要 Kinematices 或 Animation Retargeting 功能,或者没有有自定义骨骼对象时选择 Humanoid Rig
    • 其他都选择 Generic Rig,在骨骼数差不多的情况, Genericc Rig 会比 Humanoid Rig 省 30% 甚至更多的 CPU 的时间

Skin Weights

默认 4 根骨头,但对于一些不重要的动画对象可以减少到 1 根,节省计算量

在 ProjetSettings 中的 Quality 选项中可对不同平台设备做不同优化

Optimize Bones

建议开启,在导入时自动剔除没有蒙皮顶点的骨骼

Optimize Game Objects

在 Avatar 和 Animatior 组件中删除导入游戏角色对象的变换层级结构,而使用 Unity 动画内部结构骨骼,消减骨骼 transform 带来的性能开销。可以提高角色动画性能, 但有些情况下会造成角色动画错误,这个选项可以尝试开启但要看表现效果而定。注意如果你的角色是可以换装的,在导入时不要开启此选项,但在换装后在运行时在代码中通过调用 AnimatorUtility.OptimizeTransformHierarchy 接口仍然可以达到此选项效果

Animation 标签:

Back Animation 只在模型是一些 DCC 软件制作导出的才适用

Resmple Curves

将动画曲线重新采样为四元数数值,并为动画每帧生成一个新的四元数关键帧,仅当导入动画文件包含欧拉曲线时才会显示此选项

Anim.Compression

  • Off: 不压缩, 质量最高,内存消耗最大
  • Keyframe Reduction :减少冗余关键帧,减小动画文件大小和内存大小
  • Keyframe Reduction and Compression: 减小关键帧的同时对关键帧存储数据进行压缩,只影响文件大小
  • Optimal:仅适用于 Generic 与 Humanoide 动画类型,Unity 决定如何进行压缩

{% note primary %} 当选择后三种压缩格式时,都会有 Rotation Error、Position Erro r和 Scale Error,Unity 通过以上三个压缩内容的值来控制删除多少数据(默认是 0.5,表面上从 0 到 100,但可以远远超出这个百分比),这代表了源资产可接受的变化量。允许的变化越大,压缩就越激进,性能提升就越大 {% endnote %}

Animation Custom Properties

导入用户自定义属性,一般对应 DCC 工具中的 extraUserProperties 字段中定义的数据

动画曲线数据信息:

  • Curves Pos: 位置曲线
  • Quaternion: 四元数曲线 Resample Curves 开启会有
  • Euler: 欧拉曲线
  • Scale: 缩放曲线
  • Muscles: 肌肉曲线,Humanoid 类型下会有
  • Generic: 一般属性动画曲线,如颜色,材质等
  • PPtr:精灵动画曲线,一般 2D 系统下会有
  • Curves Total: 曲线总数
  • Constant: 优化为常数的曲线
  • Dense: 使用了密集数据(线性插值后的离散值)存储
  • Stream: 使用了流式数据(插值的时间和切线数据)存储

在动画导入选项尝试一些更改后,可在此选项中查看优化后的信息

动画文件导入设置优化后信息查看原则:

  1. 一看效果差异(与原始制作动画差异是否明显)
  2. 二看曲线数量(总曲线数量与各种曲线数显,常量曲线比重大更好)
  3. 三看动画文件大小(以移动平台为例,动画文件在 几百k 或更少为合理,查过 1M 以上的动画文件考虑是否进行了合理优化)

Chapter 1 End ✅ and keep updated ⚙️