场景优化

Unity3D场景性能优化/渲染/卡顿/搭建优化 遮挡剔除/层消距离技术/LOD(多层次细节)_u3d5.6的遮挡剔除-CSDN博客

遮挡剔除(Occlusion Culling)

Unity 中的遮挡剔除 Occlusion Culling 是一种性能优化技术,它可以帮助开发者减少需要渲染的场景物体数量,从而提高游戏的帧率和流畅度。
遮挡剔除的基本思路是在运行时计算场景中哪些物体被遮挡而不需要被渲染,哪些物体是可见的需要被渲染。这样可以减少渲染所需的时间和开销,提高游戏性能。

Unity 中的遮挡剔除主要有两种方式:静态遮挡剔除和动态遮挡剔除。

  • 静态遮挡剔除(Static Occlusion Culling)
    • 是在场景构建时进行的,主要是通过 Unity 自带的预处理工具将场景物体分成一些区域,然后计算这些区域之间的遮挡关系。这种方式适用于静态场景和场景中的大部分物体都是静态的情况。静态遮挡剔除的优点是计算量小,不会对游戏运行时的性能造成太大影响。
  • 动态遮挡剔除(Dynamic Occlusion Culling)
    • 则是在游戏运行时进行的,主要是通过摄像机视野和场景中物体之间的遮挡关系来计算需要渲染的物体。这种方式适用于动态场景和场景中有大量动态物体的情况。动态遮挡剔除的优点是可以适应动态变化的场景,但需要计算量较大,可能会对游戏运行时的性能造成一定影响。

层消隐距离技术

​ 因距离太远太小而看不见的物体将其隐藏

 
1
如果场景中存在大量小"物件”,则可以使用"层消隐距离"来优化场景;"层消隐距离"就是在比较远的距离将小物体剔除以减少绘图调用的数量(比如:可以一个大型场景中,高大型的物体任然可见,但是一些小装饰内容(小狗、车子之类的则可以隐藏)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using UnityEngine;

public class LayerBlankingDistance : MonoBehaviour
{
public float Distance = 10; //定义显示距离
public int Layer = 9; //定义被剔除的层级
//定义大小为32的一维数组,用来储存所有层的剔除信息
float[] distance = new float[32];

//经过测试 只能够在Start函数中使用
void Start()
{
//Layer层显示的距离在Distance内 超过这个距离就不会显示
distance[Layer] = Distance;
//将数组赋给摄像机的LayerCullDistance
Camera.main.layerCullDistances = distance;
}


}

LOD多层次细节

​ LOD(Level of detail)多层次细节,是最常用的游戏优化技术。

​ unity 有个LOD Group 在里面放置高中低模

    它按照模型的位置和重要程度决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。

    这就是说,根据摄像机与模型的距离,来决定显示哪一个模型,一般距离近的时候显示高精度多细节模型,距离远的时候显示低精度低细节模型,来加快整体场景的渲染速度。

    作用 : 优化GPU

    缺点 : 同一模型要准备多个模型,消耗内存。

    特点 : 以内存做消耗来优化GPU

图片优化

MipMap

5.1 概念
为了加快渲染速度和减少图像锯齿,贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为 MIP map 或者 mipmap。

5.2 优缺点
5.2.1 优点

    优化显存带宽,用来减少渲染。因为可以根据距离摄像机远近,选择适合的贴图来渲染。所以UI不适用MipMap。

5.2.2缺点

    运行时占用更多内存,且增加包的容量。

    【使用 Mip maps 需要使用 33%以上的内存,但不使用它会导致巨大的性能损失】

5.3 内部操作
Mipmap纹理技术是目前解决纹理分辨率与视点距离关系的最有效途径,它会先将图片压缩成很多逐渐缩小的图片,例如一张6464的图片,会产生 6464 , 3232 , 1616 , 88 , 44 , 22 , 11的7张图片,(从2的0次幂 到2的n次幂,n+1即为图片大小)当屏幕上需要绘制像素点为2020 时,程序只是利用 3232 和 1616 这两张图片来计算出即将显示为 2020 大小的一个图片,这比单独利用 32*32 的那张原始片计算出来的图片效果要好得多,速度也更快。

纹理优化POT(内存)

减小 Max Size

1
使用能生成视觉上可接受的结果的最低设置。这种非破坏性方式,可以快速降低纹理内存。

使用 2 的幂 (POT)

    Unity 要求移动端纹理压缩格式(PVRCT 或 ETC)采用 POT 纹理尺寸。

    说明:当游戏中的图标满足边长为2的n次方的时候,满足POT格式,比非POT格式的图标占用更少的内存。可以被GPU直接解析。

    例如 119*119的图标(非POT格式)大小为55.3k,可以利用图集解决。

    128*128的图标(POT格式)大小为16k。

    所以并非图标越小占用内存越少。

遮挡问题 Mask 与 RectMask2D

6.1 Mask
Mask实现的具体原理是一个DC来创建Stencil mask(来做像素剔除),然后画所有子UI,再在最后一个DC移掉Stencil mask。

1
这头尾两个DC无法跟其他UI操作进行Batch,所以表面上看加个Mask就会多2个DC,但是,因为Mask这种类似“汉堡包式”的渲染顺序,所有Mask的子节点与其他UI其实已经处在两个世界了,上面提到的层级合并规则只能分别作用于这两个世界了,所以很多原本可以合并的UI就无法合并了。

6.2 为什么Mask内外不能合批?
原因是不同材质不能合批。具体来讲就是 Mask用的Stencil模板材质;而非Mask用的default Ul mat材质。

    因此mask会比rectMask2D额外生成两个DC。

6.3 RectMask2D小结:
1.RectMask2D本身不产生drawcall.

    2.不同RectMask2D的子对象不能合批.

RectMask2D控件的局限性包括:

    仅在2D空间中有效

    不能正确掩盖不共面的元素

RectMask2D的优势包括:

    不使用模板缓冲区

    无需额外的绘制调用

    无需更改材质

    高速性能
  1. mask和rectmask2d的区别

    Mask使用模板缓冲来实现区域切除逻辑(Stencil),会占用两个DC。它实现最初设置模板缓存会给Mask添加一个特殊的材质,并且以像素为单位存储是否需要显示最后还原模板缓存,这两次操作各增加一次DC。它可以和其他Mask子物体进行合批,如果两个mask重叠了,那就不能进行合批,会产生额外的dc。
    
    rectmask2d继承自IClipper接口,内部主要实现的就是一个方法来实现了区域的切除逻辑,本身是不占用DC的,完全遮住的情况下不会绘制顶点和面,不参与深度运算不占用DC(和mask的最大区别)。 缺点:它无法和RectMask的子物体进行合批,只能和自身的子物体进行合批(注:如果本身带了Image组件的话是可以进行合批的)
    
    mask2d只能矩形,要不同形状的遮罩还是得mask,所以RectMask2D并不一定完全好,他在特定情况下无法合批。
    

光照优化

LightMap

就是指在三维软件里实现打好光,然后渲染把场景各表面的光照输出到贴图上,最后又通过引擎贴到场景上,这样就使物体有了光照的感觉。 光照烘焙的结果

CPU端代码优化

AOI

【游戏优化】AOI算法、Unity游戏优化(一)_unity aoi-CSDN博客

​ 例如当mmo类型游戏中 存在多个玩家时,一个玩家移动,将通知所有玩家随之跟新,发送与接收数据太多造成卡顿,利用AOI机制(兴趣范围)

1
2
3
以状态修改的角色为原点,设置半径,获取该角色的兴趣范围,当其他角色存在该兴趣范围的时候,进行接收通知并更新状态,减少cpu压力。

可以利用该思想去解决不同的方案

优点:易于实现,且不需要特殊的数据结构。

    缺点:计算成本较高:需要该角色与所有角色的距离判断。(1+n)n/2。

优化:

多线程并行计算,提升计算效率(需要考虑线程切换本身的消耗)
延迟计算,减少计算间隔(避免不必要的每帧都要计算)
分批计算,降低每帧计算角色的数量。
优化设计方案:

由单项角色寻找变为双向寻找(每个角色都与其他角色遍历 变为 A与B计算过,B就不用与A计算)
将地图优化为格子,计算每个格子中的角色
区域大小改为2的N次方表示,角色所区域可以利用位运算来计算。
将计算的区域变小,虽然格子变多,但格子里的角色变少,需要计算的量也会变少。
扩大地图比例,玩家不变,玩家更加稀疏,优化算法。

脏标记模式

​ 将工作推迟到必要时进行以避免不必要的工作。就是用一个标志位来标记内容是否发生变化,如果没有发生变化就直接使用缓存数据,不需要重新计算。

要点:

    脏标识模式:当前有一组原始数据随着时间变化而改变。由这些原始数据计算出目标数据需要耗费一定的计算量。这个时候,可以用一个脏标识,来追踪目前的原始数据是否与之前的原始数据保持一致,而此脏标识会在被标记的原始数据改变时改变。那么,若这个标记没被改变,就可以使用之前缓存的目标数据,不用再重复计算。反之,若此标记已经改变,则需用新的原始数据计算目标数据。

使用场合:

    1,原始数据转换到目标数据会消耗很多时间,都可以考虑使用脏标记模式来节省开销。

    2,游戏中物体局部变换到世界变换的计算,当没有变化时不需要每帧重复计算。(从根节点沿着它的父链将变换组合起来,矩阵相乘=世界变换)。还有游戏场景图中每帧渲染的对象,对于没有发生变化的对象可以不必重新渲染。再有我们的文档存档也可以用到,内存中就是我们的原始数据,存盘到磁盘就是我们的目标数据,当然不需要实时存盘。

    3,若原始数据的变化速度远高于目标数据的使用速度,此时数据会因为随后的修改而失效,此时就不适合使用脏标记模式。