上期我们学习了简单的工作流的优化,学习了AssetGraph工具
接下来我们即将进入下一系列编辑器创建的资源优化
我们先从简单的Scene开始学起
在Unity性能优化之场景资源优化篇里,主要对场景资源内存的优化,下面介绍一些常用的优化方法。总之性能优化是有利也有弊的,总是牺牲一个方面,去成全另一个方面。可根据项目的具体情况选择合适的优化方案。
场景(Scene)
场景结构设计原则
1.合理设计场景一级节点的同时,避免场景节点深度太深,一些代码生成的游戏对象如果不需要随父节点进行Transform的,一律放到根节点下。不宜分的太细,对于Find查找和Transform会产生严重的性能影响。
有父节点和无父节点和无父节点,1000万个空的Gameobject,效率相差很大,近几十倍,如图
2.尽量使用Prefab节点构建场景,而不是直接创建的GameObject节点
如果已经加载到内存中,在场景加载时需要引用prefabs对象,无论多少场景引用,内存始终那么一块,所以无论内存使用量上还是加载速度上,甚至生成的包体上,使用prefabs构建的场景都要比直接用gameobject更优化
3.避免DontDestroyOnLoad节点下有太多生命周期过长或引用资源过多的复杂节点对象。Additve模式添加的场景要尤为注意。
unity默认的对象是通过场景切换来创建和释放资源的,但是对于调用了DontDestroyOnLoad对象节点,以及添加了hideflags.HideAndDontSave节点的对象,切换场景后unity不会释放该资源,调用了DontDestroyOnLoad对象会被带入下一个场景中,可想而知,如果该对象下引用大量资源那将占用巨大内存开销。
4.最好为一些需要经常访问的节点添加tag。
复杂场景中,对于设置好Tag的节点,使用FindGameObjectWithTag方法取查找该节点更高效
5.场景的静态合批
unity中的许多系统都可以在Editor中预计算有关静态游戏对象的信息,由于静态游戏物体不会移动,因此这些计算的结果在运行时仍然有效,这意味着unity可以节省运行时的计算成本,并可能提高性能
静态合批的主要作用是将多个静态物体(在运行时不会移动、旋转或缩放的物体)的网格合并为一个更大的网格,从而减少DrawCall的数量,提高渲染性能。合批方法为将Static里的BatchingStatic勾选上,如下图:
静态合批的条件:
- 物体标记为(静态)Static状态
- 相同材质
- 共享顶点格式,合批的网格需要具有兼容的顶点格式。例如,如果一个网格具有顶点颜色信息,而另一个没有,它们可能无法合批。
- 未发生变换,在游戏运行过程中,被合批的物体不能移动、旋转或缩放。
静态合批的缺点:
- 内存增加,由于将多个物体的网格合并为一个,可能会导致合并后的网格数据变大,从而增加内存的占用。
- 灵活性受限,合批后的物体在运行时不能单独进行移动、旋转或缩放操作,这在某些需要动态变化的场景中会限制设计的灵活性。
- 材质共享限制,要求参与合批的物体使用相同的材质,如果为了合批而强行统一材质,可能会损失一些视觉效果的多样性。
6.场景的动态合批
动态合批是一种在运行时自动合并小型网格物体的技术,以减少 DrawCall 数量来提高性能。动态合批的工作原理是在每一帧中,Unity 会检测可以合并的小型动态物体(可以移动、旋转或缩放),并将它们的网格组合在一起进行渲染。所以Unity会帮我们进行处理。
动态合批的优点:
- 实时优化:能够在运行时根据物体的状态和位置自动进行合批,无需提前设置。
- 灵活性:适用于会移动、旋转或缩放的物体。
动态合批的缺点和限制:
- 顶点数量限制:每个合批的物体顶点总数不能超过一定数量(通常是 900 个顶点)。
- 材质限制:合批的物体必须使用相同的材质。
- 增加 CPU 负担:动态合批需要在每一帧进行计算和判断,可能会增加一定的 CPU 开销。
7.启用GPU Instancing (GPU实例化)
在 Unity 中,GPU Instancing(GPU 实例化)是一种用于优化渲染大量相同或相似物体的技术。通过在GPU端一次性处理多个相同网格和材质的物体实例,大大减少了CPU向 GPU发送的指令数量(DrawCall),从而显著提高渲染效率。
GPU Instancing的优点:
- 性能提升,减少DrawCall
- 减少内存占用:多个实例可以共享相同的网格数据和材质,避免了重复存储,节省了内存。
- 灵活性:支持物体的变换(位置、旋转、缩放)以及一些材质属性的变化。
例如,在一个森林场景中,大量相同类型的树木可以通过 GPU Instancing 进行高效渲染。
GPU Instancing的缺点:
- 材质和网格要求:物体需要使用相同的网格和材质,虽然可以有一些材质属性的变化,但有一定的限制。
- 图形 API 支持:并非所有的图形 API 和设备都能完全支持 GPU Instancing,需要考虑兼容性。
- 复杂的材质计算:某些复杂的材质计算可能不适合 GPU Instancing,或者需要特殊处理。
8.使用遮挡剔除技术(Occlusion Culling)
遮挡剔除的核心思想是在渲染场景时,不渲染那些被其他物体完全遮挡住的物体。
在使用时需要对游戏物体进行Static处理,可根据实际情况进行选择勾选某项,如下图:
使用OcclusionArea组件设置遮挡剔除的范围,当摄像机处于该区域时才会触发遮挡剔除的效果,设置完后需要进行烘焙Bake。
使用Occlusion Portal组件可以控制是否启用遮挡剔除的功能。
遮挡剔除的缺点:
- 内存占用:遮挡数据会占用一定的内存。为了进行遮挡判断,Unity 需要存储关于场景中物体的遮挡关系和相关数据。这些数据可能包括物体的包围盒信息、层次结构以及可见性状态等,这都需要一定的内存来保存。
- 预处理开销:生成遮挡数据的预处理过程可能较为耗时,尤其对于大型和复杂的场景,这可能会增加开发时间。
9.光照的优化
1.控制光源数量:尽量减少场景中的光源数量,只保留必要的光源。过多的光源会增加计算复杂度。
例如,如果可以通过一个点光源和环境光达到相似的效果,就避免使用多个点光源。
2.选择合适的光源类型:根据场景需求选择合适的光源类型。例如,对于大面积的照明,使用方向光可能更合适;对于局部照明,使用点光源或聚光灯。
比如,模拟太阳光时使用方向光。
3.烘焙光照:对于静态物体,可以使用光照烘焙技术将光照信息存储在纹理中,减少实时计算。
在室内场景中,将静态的灯光效果烘焙到纹理上,可以显著提高性能。
4.调整光源参数:合理设置光源的强度、范围、衰减等参数,避免过亮或过暗的区域。
比如,对于一个小房间的灯光,调整其范围和强度以获得均匀的照明。
5.利用 Light Probes(光照探针):在动态物体经过光照变化较大的区域时,使用光照探针来获取近似的光照信息,减少实时计算。
例如,在角色移动经过不同光照区域时,通过光照探针提供平滑的光照过渡。
6.优化材质:使用对光照计算效率高的材质,避免使用复杂的光照模型。
某些简单的材质可能不需要复杂的光照计算就能达到较好的视觉效果。
7.阴影优化:调整阴影的分辨率和距离,避免过高的质量设置。
例如,对于远处的物体,可以使用较低分辨率的阴影。
10.MipMap
Mipmap 的主要原理是为原始纹理生成一系列逐渐缩小的版本。当物体在屏幕上所占的像素较少时(即距离摄像机较远),Unity 会自动选择使用较小的 Mipmap 级别来渲染纹理,从而减少了采样时的计算量和可能出现的走样现象。
说白了就是会根据摄像机到场景中图片的距离大小,来显示不同清晰度的图片,距离越近显示的图片越清晰,距离越远显示的图片越模糊。
MipMap的缺点:
- 增加内存占用:因为需要存储不同清晰度的纹理。
- 可能导致模糊:在某些情况下,特别是当物体在两个 MipMap 级别之间切换时,可能会导致纹理看起来稍微有些模糊。
11.LOD
在Unity中,LOD(Level of Detail,细节层次)是一种用于优化渲染性能的技术。
LOD的核心概念是根据物体与摄像机的距离或屏幕上所占的像素大小,使用不同细节程度的模型来表示同一个物体。也就是说会根据摄像机与游戏物体的距离远近,来显示不同的清晰度模型。
使用时,为一个物体创建多个具有不同细节级别的模型,从高细节到低细节。
在运行时,根据设定的条件(如距离、屏幕占比等),Unity 自动切换使用相应的LOD 模型。
LOD缺点:
- 增加内存:需要为一个模型创建好多种不同细节级别的模型。
- 视觉过渡问题:在不同LOD级别之间切换时,如果处理不当,可能会出现明显的视觉跳跃。
12.AssetBundle加载资源的优化
使用AssetBundle加载资源后要及时的卸载资源,避免占用资源内存。
AssetBundle.Unload(false):内存中的AssetBundle对象包含的资源会被销毁。
AssetBundle.Unload(true):不仅仅内存中的AssetBundle对象包含的资源会被销毁。根据这些资源实例化而来的游戏内的对象也会销毁。
> 注意:上面介绍的优化方向,主要是场景设计需要注意的一些优化原则,关于场景的优化还有很多动态的方法,上面有提到如烘焙,遮挡剔除,场景划分,LOD,HLOD,还有很多方法,后续的会着重讲解。
今天是2024年11月30日
重复一段毒鸡汤来勉励我和你
你的对手在看书
你的仇人在磨刀
你的闺蜜在减肥
隔壁的老王在练腰
而你在干嘛?