Unity 优化性能

优化图形性能

渲染 Statistics 窗口

Game 视图包括一个统计窗口,显示您的应用程序在运行模式下的实时渲染信息。要打开此窗口,请单击右上角的 Stats 按钮。该窗口会在 Game 视图右上角叠加显示。它包含的统计信息对于优化性能很有用。显示的具体统计信息根据构建目标而有所不同。
image

Audio:表示音频的数据

  • Level:声音强度。单位是分贝(db)。
  • DSP load:数字信号处理器的负载。播放的声音越多,声音的采集率越高,声音效果越复杂,该变量数值都会增大,应尽量避免数值过大。
  • Clipping:音频的裁剪情况。当音频信号超过设备支持的最大范围时,该音频信号会被裁剪,裁剪后,该音频会出现一定程度失真现象,应该避免该项数据过大。
  • Stream load:音频流的负载情况。音频的流式加载是指以持有的方式从音频源中获取音频数据,而不是一次性加载全部数据。流式加载的主要优势是可以实时地处理和播放音频,无需等待全部数据加载完成。应尽量避免数据过大。

Graphic:表示图像的数据

  • FPS:每秒执行的帧数。游戏画面、视频画面都是由一张张静态的画面连续播放而成的,一帧就是一张静态的画面。60FPS很流畅、45FPS比较流畅、30FPS会感觉卡顿。
  • CPU:
  • Main:主线程处理一帧所用时间,主线程主要负责游戏逻辑的更新。应尽量避免数据过大。
  • Render:渲染线程处理一帧所用时间,渲染线程负责显示游戏画面。应尽量避免数据过大。
  • Batches:一帧内处理绘制调用(Draw Call)的批次总数。该数字包括静态和动态批次。应尽量避免数据过大。
  • Saved by batching:一帧内有多少绘制调用(Draw Call)被合并到批次。为确保良好的批处理,应尽可能在不同游戏对象之间共享材质。应尽量避免数据过大。
  • Tris:摄像机视椎体范围内三角面的个数。应尽量避免数据过大。
  • Verts:摄像机视椎体范围内网格顶点的个数。应尽量避免数据过大。
  • Screen:屏幕的分辨率及其使用的内存量。应尽量避免数据过大。
  • SetPass Call:一帧中切换用于渲染游戏对象的着色器通道的次数。一个着色器可能包含多个着色器通道,每个通道以不同的方式渲染场景中的游戏对象。每个 pass 都需要 Unity 绑定一个新的着色器,这可能会带来 CPU 开销。应尽量避免数据过大。
  • Shadow casters:一帧中投射阴影的游戏对象的数量。应尽量避免数据过大。
  • Visible skinned meshes:相机中有多少个可见的蒙皮网格数量。应尽量避免数据过大。
  • Animations:场景中有多少个Animator组件正在播放动画。播放动画会消耗性能,应尽量避免数据过大。

绘制调用批处理

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

首先启用静态/动态批处理:
image

静态批处理

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

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

动态批处理

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

  • 批处理动态游戏对象在每个顶点都有一定开销,因此批处理仅会应用于总共包含不超过 900 个顶点属性且不超过 300 个顶点的网格。
  • 如果游戏对象在变换中包含镜像,则不会对这些对象进行批处理(例如,具有 +1 缩放的游戏对象 A 和具有 –1 缩放的游戏对象 B 无法一起接受批处理)。
  • 即使游戏对象基本相同,使用不同的材质实例也会导致游戏对象不能一起接受批处理。例外情况是阴影投射物渲染。

优化天空盒

使用Window->Rendering->Lighting->Environment天空盒
image

使用“伪造”天空盒
image

相同材质启用Enable GPU Instancing

注意事项:如果游戏对象在变换中包含镜像,则不会对这些对象进行批处理

未启用
image

已启用
image

启用游戏对象Inspector面板右上角 Batching Static

注意事项:尽量使用相同材质

未勾选 Batching Static
image

已勾选 Batching Static 不同材质
image

已勾选 Batching Static 同材质
image

网格的细节级别 (LOD)

当场景中的某个游戏对象距离摄像机很远时,与距离摄像机很近的游戏对象相比,可以看到的细节将会减少。但默认情况下,Unity 会使用相同数量的三角形来渲染两个远近不同距离的游戏对象。这可能会浪费 GPU 运算资源,从而影响场景中的性能。

LOD 技术允许 Unity 根据与摄像机的距离来相应减少为游戏对象渲染的三角形数量。

下图显示了 LOD 级别如何根据与摄像机的距离而变化。
image

处于 LOD 0 时,摄像机显示包含大量小三角形的网格。处于 LOD 1 时,摄像机显示的网格中三角形的数量大幅减少,而且三角形的大小大幅增大。

蒙皮网格合并

/// <summary>
/// 合并蒙皮网格
/// </summary>
/// <param name="skinneds">被合并的蒙皮</param>
/// <param name="renderer">合并后的蒙皮</param>
/// <param name="rootBone">根骨骼</param>
private void SkinnedMeshCombine(SkinnedMeshRenderer[] skinneds, SkinnedMeshRenderer renderer, Transform rootBone)
{
CombineInstance[] combineInstances = new CombineInstance[skinneds.Length]; // 设置网格
List<Material> materials = new List<Material>(); // 设置材质
List<Transform> bones = new List<Transform>(); // 设置骨骼
for (int i = 0; i < skinneds.Length; i++)
{
SkinnedMeshRenderer skinned = skinneds[i];
combineInstances[i].transform = skinned.transform.localToWorldMatrix;
combineInstances[i].mesh = skinned.sharedMesh;
materials.AddRange(skinned.sharedMaterials);
bones.AddRange(skinned.bones);
skinneds[i].gameObject.SetActive(false);
}
renderer.sharedMesh = new Mesh();
renderer.sharedMesh.CombineMeshes(combineInstances, false, false);
renderer.rootBone = rootBone;
renderer.bones = bones.ToArray();
renderer.materials = materials.ToArray();
}

image

音频优化

image

  • DeCompressOnLoad 加载后解压缩(适合小音效)
    音频文件将在加载后立即解压缩,对较小的音效使用此选项可避免动态解压缩的性能开销。请注意在加载后解压缩Vorbis编码的声音,会使用它压缩 时大约10倍的内存,ADPCM编码大约是3.5倍。所以请不要降此选项用于大文件。

  • CompressedInMemory 压缩在内存中(适合较大文件音效)
    将声音压缩在内存中,在播放是解压缩。这个选项有轻微的性能开销(尤其是对于ogg/Vorbis压缩文件)所以只能用于大文件音效,因为在加载时 解压缩会使用大量的内存。可在Profiler窗口的音频面板中的“DSP CPU”部分监视。

  • Streaming 流(适合大文件)
    动态解码声音,此方法是使用最最小量的内存来缓冲从磁盘逐渐读取并在运行中解码的压缩数据。解压缩发生在单独的流线程上,请注意解压缩发生在Profiler窗口的音频板中的"Streaming CPU" 部分中可监视其CPU使用率的单独流式线程上,即使没有加载任何饮品数据,流式片段也会有200KB的过载。

对象池

对象池是一种常用的性能优化技术,可以减少频繁创建和销毁对象的开销。

提高游戏运行速度的简单核对表

  • PC 平台保持顶点数量低于 200K 和 3M/帧(具体值取决于目标 GPU)。移动端应不超过 100K个顶点。。
  • 如果要使用内置着色器,请从 Mobile 或 Unlit 类别中选取。这些类别也适用于非移动平台,但它们是更复杂着色器的简化和近似版本。
  • 保持每个场景使用较少的不同材质,并尽可能在不同对象之间共享材质。
  • 在非移动对象上设置 Static 属性以便允许内部优化,如静态批处理。
  • 只有一个(最好是方向性的)pixel light 影响几何体(而不是有多个)。
  • 烘焙光照而不是使用动态光照。
  • 尽可能使用压缩纹理格式,并使用 16 位纹理而非 32 位纹理。
  • 尽可能避免使用雾效。
  • 如果复杂的静态场景具有大量遮挡,使用遮挡剔除减少可见几何体数量和绘制调用次数。设计关卡时注意遮挡剔除。
  • 使用天空盒“伪造”远处的几何体。
  • 使用像素着色器或纹理组合器来混合多个纹理而不是使用多 pass 方法。
  • 尽可能使用 half 精度变量。
  • 最大限度减少在像素着色器中使用复杂的数学运算,例如 pow、sin 和 cos。
  • 每个片元使用更少的纹理。
posted @   镜子-眼泪  阅读(115)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
欢迎阅读『Unity 优化性能』

喜欢请打赏

扫描二维码打赏

了解更多

点击右上角即可分享
微信分享提示