Unity 性能优化


一、优化相关前提


1.Unity游戏安装包/运行卡的原因?

Unity编译打包时,使用C#编写的脚本会通过Mono的C#编译器打包成IL(中间语言),然后在Mono的虚拟机中运行。

image-20250411135407904

  • 用Mono方式来进行打包的程序会出现一堆动态链接库,程序员写的程序控制逻辑就在Assembly-CSharp.dll动态链接库里面

  • 这个动态链接库里包括了所有的功能代码在里面,当要执行程序时,就必须在程序启动之前把它加载到Mono虚拟机里面

    • Mono本身是一个虚拟机,因为C#本身是运行在DotNet平台上面的,而DotNet平台本身就是一个基于虚拟机的的平台,Mono虚拟机是对DotNet虚拟机的跨平台移植

IL2CPP打包方式

  • 使用IL2CPP方式打出来的包是没有动态连接库的,它将Mono虚拟机和Assembly-CSharp.dll动态链接库整合在了一起,放在libil2cpp.so文件里.
  • 解决方案:
    • 就是性能优化。

2.什么是Drawcall(绘画调用)

  • DrawCall就是cpu对图形绘制接口的调用,CPU通过调用图形库(OpenGL)接口,命令GPU进行渲染操作。
  • DrawCall性能优化的元凶是CPU,每一次绘制时CPU都要调用DrwaCall。而在调用DrawCall前都会有很多准备工作:检测渲染状态、提交所需要的数据、提交所需要的状态。。。。。当DrawCall过多,CPU就会有很多额外开销用于准备工作,而GPU的计算能力很强,这时GPU就可能会闲置。
  • 后期补充
  • DrawCall优化:减少DrawCall数量即可
  • image-20250411141625324

3.Profile性能分析器

  • 影响性能的参数,可以在Profiler中查看

  • 选择峰值查看,那一帧往往开销较大

  • 一般选用Play Mode模式,查看游戏运行时的性能

  • Deep Profile功能,收集所有C#方法占用性能。但是这个功能非常耗费性能,如果是大型项目,开启就会捕捉成百上千个脚本,非常消耗性能。一般采用单独脚本分析。

  • Profile Analyzer配合Profiler使用。当我们使用Profiler收集了数据之后,不要关闭Profiler,并且打开Profile Analyzer,把Profiler收集的数据导入到Profile Analyzer,使用Profile Analyzer可以帮助我们选出一段数据中最具有代表性的一帧,方便我们进行性能分析。也能看出收集的数据整体的一些情况,例如中位数、平均值、最大值、最小值等。


(1) 如何用分析器分析指定一段代码?

Profiler.BeginSample(名称)和 Profiler.EndSample()将指定代码括起来即可。中间的代码即可进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
int a = 1;
void Update()
{
//这个名字不要写中文名。可能会搜索不到
Profiler.BeginSample("123");//开始截取代码,参数是分析器里面的名称是自定义的
for (int i = 0; i < 1000; i++)
{
a += 1;
}
Profiler.EndSample();
}
file:///D:/Unity/Editor/2022.2.13f1c1/Editor/Data/Documentation/en/Manual/ProfilerWindow.html

​ 其他模块具体可看Unity手册


4.调用栈(Call Stack)

  • 是计算机程序在执行过程中记录函数调用的一种数据结构

  • 调用栈是一个栈结构,即先进后出。它用于记录程序执行过程中,每一个函数被调用情况。

  • 当一个函数被调用时,它的相关信息,如函数名、参数、返回地址等,会被添加进调用栈中。当该函数执行完成后,相应的信息会从调用栈中移除。通过不断的添加和移除,调用栈就记录了程序执行的顺序。

  • 调用栈对于程序调试和分析非常有用。当程序出现错误或异常时,可以通过查看调用栈来确定错误发生的位置和函数调用顺序。调试器通常会显示当前调用栈的信息,以帮助开发人员查看函数的执行过程并找出发生异常的原因。

  • 选中Deep Profile右侧的Call Stacks按钮,这样在收集性能数据的时候,每一帧都会记录该方法的的调用栈信息。

  • GC.Alloc、UnsafeUtility.Malloc、JobHandle.Complete是Unity的方法,启用Call Stacks且勾选它们后,如果Unity有调用它们,则可以在Hierarchy或Raw Hierarchy右侧的搜索框中搜索到它们,这样就可以查看它们的性能了。

    • GC.Alloc表示GC的内存分配情况。
    • UnsafeUtility.Malloc(Persistent)用于在内存中分配指定大小的未初始化内存块。这个方法会直接在堆上分配内存,并可以绕过自动内存管理功能,需要手动管理内存的生命周期和释放。一般情况下,只有在处理非托管内存的特定场景下才会使用UnsafeUtility.Malloc方法。
    • JobHandle.Complete表示Job的完成情况。这里的Job是指Unity的Job System的一组特定的任务。

(1)托管内存

  • 垃圾回收器自动管理的内存,当到达一定量时,会由垃圾回收器自动释放它们。
  • 托管内存存放在托管堆中。托管堆是一种用于存储和管理对象的内存区域
  • 垃圾回收机制:
    • 每当创建一个新的托管对象时,托管堆就会分配内存空间给这个对象,并记录这个对象的信息。当托管对象不再被引用或不可达时,垃圾回收器会自动将其标记为垃圾,并在适当的时候回收其占用的内存空间。

(2)非托管内存

  • 非托管内存不会自动被回收,它们需要我们程序员写代码去管理和释放它们。
  • 非托管内存并不固定存储在一个地方,它们往往分散存储在不同的地方,例:操作系统的内存、临时缓冲区等。

(3)DOTS简介

  • DOTS(Data-Oriented Technology Stack)是Unity引擎中的一个新的编程模型和工具集。它旨在提供更高性能、更可扩展和更并行化的游戏开发体验。
  • 三个主要部分:
    • ECS框架:
      • ECS是一种用于组织和管理游戏对象的方式。它将游戏对象分解为实体(Entity)组件(Component)系统(System)。这种模式更加适合于并行处理和优化,可以提高游戏性能。
    • Job System(作业系统):
      • Job System允许开发者将任务并行化,利用多核处理器的能力。它通过将任务划分为小的作业(jobs)并在多个线程上并发执行来提高性能。Job System还可以与ECS结合使用,使得开发者可以更好地控制游戏的行为。
    • Burst Compiler(突发编译器):
      • Burst Compiler是一种高性能的C#编译器,可以将C#代码转换为高效的本机代码,以进一步提高游戏的性能。

5.设置性能分析器的一些属性

  • Color Blind Mode
    • 色盲模式,开启后会调整Profile窗口的颜色,照顾色盲用户。
  • live按钮:实时采集 ,数据收集期间也会动态的显示数据
  • Show Stats for ‘current frame’
    • 开启后,当点击Frame:XXX/XXX左侧的按钮,从而选中最后一帧,则会显示最后一帧的统计信息。
  • Preferences
    • 点击后会打开Project Settings窗口,用于设置性能分析器的一些属性。
      • Frame Count:开始收集性能的数据时,每次最多可以查看多少帧。例如数值是300,则表示最多可以查看300帧。
      • Default recording state:选择Enable,则重启Unity再打开Profiler窗口,如果此时的模式是Edit Mode,则会自动开始点击圆形按钮,开始收集数据。选择Disabled,则重启Unity再打开Profiler窗口,需要手动点击圆形按钮,才会开始收集数据。选择Remember,则会按照当前圆形按钮是启用还是禁用来决定下一次重启Unity再打开Profiler窗口时,该圆形按钮是否启用。
      • Default editor target mode on start:选择Play Mode,则重启Unity再打开Profiler窗口,左上方会选择Play Mode,即在播放模式下才会收集数据。如果选择Edit Mode,则重启Unity再打开Profiler窗口,左上方会选择Edit Mode,即在编辑器模式下收集数据。
      • Custom connection ID:当有多个Unity项目的实例同时运行,它们都要使用性能分析器来分析性能,则可以通过这个Custom connection ID来区分它们。

6. CPU Usage模块

  • 下方窗口可以选择Timeline、Hierarchy、Raw Hierarchy。

  • Timeline:

    • 可以通过时间轴的方式查看这一帧中CPU依次干了什么。
  • Hierarchy:

    • 可以查看CPU在这一帧中做的事情所消耗的性能和所花费的时间。

    • Total:

      • 表示一共占用了CPU使用情况的百分之几
    • Self:

      • 表示自身的代码占用了CPU使用情况的百分之几,调用其它方法的代码不算在内的。
    • Calls:

      • 表示被调用了几次
    • GC Alloc:

      • 表示GC分配的内存,当一个对象被释放后,它GC分配的内存不会马上被回收,所有GC分配的内存的总量达到一定程度,会触发GC,此时垃圾回收器才会把这些内存回收,不过同时也会造成游戏卡一下。
    • Time ms:

      • 表示一共耗时多少毫秒
    • Self ms:

      • 表示表示自身的代码耗时多少毫秒,调用其它方法的代码不算在内的。

      image-20250412195856977


7.Frame Debugger

  • Frame Debugger窗口也叫帧调试器窗口,用于查看每一帧的画面是如何渲染出来的,可以详细查看这一帧的绘制过程。

  • 打开Frame Debugger面板的方式:

    • Window——Analysis——Frame Debugger、
  • 按下“Enable”,则会启动帧调试,此时如果运行了游戏,则会自动暂停,然后当前这帧的渲染情况可以在这个窗口中查看。

  • 用Frame Debugger窗口查看当前一帧的每一步时,可以配合Stats窗口使用,以此来确定哪一个物体造成的性能开销较大。

    绘制的步骤越少,性能越好。

    • Frame Debugger窗口也能看到每一帧的Shader信息,但是需要有一定Shader基础才能看懂。

        大多数平台都支持帧调试器的使用,可以用手机的数据线成功连接到电脑,在手机上运行Unity的游戏,Frame Debugger窗口中会多出该手机设备供我们选择,我们就可以分析该手机设备上运行的Unity项目的性能。
        也可以让手机和电脑都连接同一个wifi,这样一来,Frame Debugger窗口中也会多出该手机设备供我们选择,我们就可以分析该手机设备上运行的Unity项目的性能。
        但是要注意,构建时必须在Project Settings窗口中勾选“Development Build”。而且有些平台可能不支持Frame Debugger的使用,例如WebGL平台。
      

8、Memory Profiler


  • 拍摄的 内存快照会存储在Assets的同级文件夹MemoryCaptures里面。

  • 看官方文档。要安装插件

  • Memory Profiler可以查看游戏当前一帧具体的内存使用情况,我们可以详细地看到各种东西占用了多少内存。

    • 如果发现某样东西占用了过高的内存,则可以考虑问题是不是出在它身上,从它身上入手来进行优化。也可以分析是不是存在内存泄漏问题,即可以分析是不是某些资源一直占着内存得不到释放,才导致内存占用过高。
  • 在Unity编辑器中运行游戏,Memory Profiler记录的内存情况可能会不准确,它会把Unity编辑器的一些内存占用情况也记录进去。我们应该把游戏构建到电脑或者手机,然后在电脑或者手机上运行游戏,再用Memory Profiler拍内存快照来分析,此时这些内存快照记录的数据才是准确的。

    • 构建项目到电脑时,应勾选Build Settings窗口的Development Build、Autoconnect Profiler,这样当发布的游戏运行时,就可以在Unity引擎的Memory Profiler左上角的下拉菜单选择关联刚才发布的程序,拍下快照查看它的内存占用情况。

    • 用手机数据线连接到电脑,在手机上运行游戏,并在电脑分析,也必须勾选Build Settings窗口的Development Build、Autoconnect Profiler,而且手机上应勾选“开启开发者选项”、“USB调试”、“USB安装”。当用手机数据线连接到电脑,Memory Profiler左上的下拉菜单可以选择连接到电脑的手机设备。


9、Physics Debugger窗口

  • 也叫物理调试窗口,使用它可以在Scene窗口中查看当前场景中游戏对象的碰撞器、触发器、刚体、关节等与物理模拟相关的东西的范围。尤其是在游戏对象较多的场景中,使用物理调试窗口可以方便我们观看它们的范围。

  • 打开方法:Window——Analysis——Physics Debugger。

  • 在Scene窗口中勾选Collision Geometry,则会在Scene窗口中显示游戏对象的碰撞几何体,具体的情况可以在Physics Debugger窗口中查看到和修改。如果勾选Mouse Select,则会开启鼠标悬停和鼠标选择功能,方便查看碰撞几何体。

  • Selected Object Info表示当前选中的游戏对象的信息。

  • Show Physics Scene表示要在哪些场景中显示碰撞几何体。

  • Show Layers表示哪些Layer层的游戏对象要显示碰撞几何体。

  • Show Static Colliders表示显示静态的碰撞器组件的范围。

  • Show Triggers表示显示勾选了Is Trigger属性的碰撞器的范围。

  • Show Rigidbodies表示显示Rigidbody组件的范围。

  • Show Kinematic Bodies表示显示勾选了Is Kinematic的Rigidbody组件的范围。

  • Show Articulation Bodies表示显示Articulation Body组件的范围。Articulation Body组件用于模拟机器人、车辆等物体的关节行为。例如可以实现机器人的动作、车辆的悬挂等物理效果。

  • Show Sleeping Bodies表示显示处于睡眠状态的Rigidbody组件的范围。当一个刚体在一段时间内没有受到外力作用,则会自动进入睡眠状态,进入睡眠状态的刚体不会参与物理模拟。当它再次受到外力的作用时,它就会从睡眠状态中唤醒,此时就又会重新参与物理模拟了。

  • Collider Types

    • Show BoxColliders表示显示盒子碰撞器的范围。
    • Show SphereColliders表示显示球体碰撞器的范围。
    • Show CapsuleColliders表示显示胶囊体碰撞器的范围。
    • Show MeshColliders(convex)表示显示网格碰撞器的范围。
    • Show MeshColliders(concave)表示显示网格碰撞器的范围。
    • Show Terrain Colliders表示显示地形碰撞器的范围。
  • Colors

    • Static Colliders表示显示静态的碰撞器的颜色。
    • Triggers表示显示勾选了Is Trigger属性的碰撞器的颜色。
    • Rigidbodies表示显示Rigidbody组件的颜色。
    • Kinematic Bodies表示显示勾选了Is Kinematic的Rigidbody组件的颜色。
    • Articulation Bodies表示显示Articulation Body组件的颜色。
    • Sleeping Bodies表示显示处于睡眠状态的Rigidbody组件的颜色。
    • Variation表示要显示的颜色与随机颜色混合的程度。调节这个变量,会影响所有显示的几何碰撞体的颜色。
  • Rendering

    • Transparency控制绘制的碰撞几何体的透明度。
    • Force Overdraw,勾选后,绘制的碰撞集合体会更加清晰。
    • View Distance控制能在Scene窗口中看到碰撞几何体的距离。
    • Terrain Tiles Max控制能在Scene窗口中看到Unity自带的地形系统Terrain的瓦片的最大数量。

10、Import Activity窗口


  • 可以查看项目中的资源的信息。例如什么时候导入这个资源的、这个资源的文件大小等。

11 、Code Coverage窗口


  • Code Coverage用来评估我们编写的代码,在游戏测试运行的时候有多少被执行了。如果有一部分代码没有被执行,我们就要考虑是不是我们代码的逻辑有问题,从而改进我们的代码。
  • 打开方法:Window——Analysic——Code Coverage
  • 官方文档:https://docs.unity.cn/Packages/com.unity.testtools.codecoverage@1.1/manual/index.html
  • 使用思路:
    • 先勾选Enable Code Coverage,然后勾选Auto Generate Report,再点击Start Recording按钮,接着点击播放按钮运行游戏。点击Start Recording按钮后,它会变成Stop Recording,测试完毕后点击它,就会停止测试。然后我们就可以在Result Location的路径找到生成的报告,从中看出在测试的时候有多少代码被执行了。\
  • 其他属性
    • Results Location表示生成的报告要存放到的路径。
    • History Location表示生成的报告的历史记录要存放到的路径。必须勾选了Generate History才会生成。
    • Enable Code Coverage表示启用代码覆盖,测试之前要勾选它。
    • Included Assemblies表示测试的时候要考虑哪些程序集的代码。
    • Included Paths表示测试的时候要考虑哪些文件夹或文件中的代码。
    • Excluded Paths表示测试的时候不考虑哪些文件夹或文件中的代码。
    • Generate HTML Report,勾选后,生成报告时,会以html的格式来生成报告。如果取消勾选,则会以其它形式来生成报告,但是这样不方便我们观看。建议勾选。
    • Generate Summary Badges,勾选后,生成的报告的文件夹中,会额外生成.svg和.png后缀的文件,概括这次测试中有百分之几的代码被使用了。
    • Generate History,勾选后,生成报告时,会自动在History Location的路径生成历史记录。
    • Generate Additional Metrics,勾选后,生成报告时,报告中会有额外的衡量指标。
    • Auto Generate Report,勾选后,我们点击Start Recording开始测试,然后点击Stop Recording结束测试后,会自动在Results Location的路径生成报告。
    • Clear Data清空上一次测试的数据。
    • Clear History,清空History Location路径中的所有历史记录。
    • Generate from Last,根据上一次测试的数据,在Results Location的路径生成报告。

二、静态批处理


1.什么是静态批处理

  • 静态合批也叫静态批处理,是Unity的一种优化技术。对于始终静止不动的物体使用静态合批后,CPU会把它们合并为一个批次发送给GPU处理,这样可以减少Draw Call带来的性能消耗,从而提升游戏性能。
  • 注:静态批处理比[动态批处理](https://docs.unity3d.com/cn/current/Manual/dynamic-batching.html)更高效,因为静态批处理不会转换 CPU 上的顶点。有关静态批处理的性能影响的更多信息。
  • 注:要使用静态合批,必须确保Edit——Project Settings——Player——Other Settings——Static Batching是勾选的。

2.如何进行静态批处理

  • 物体设置为静态的方法:

    • 选中该物体,点击在Inspector窗口右上角的Static右方的下拉菜单,选择Batching Static。

    • 使用静态合批虽然可以提升游戏性能,但是设置为静态的物体在整个游戏中就不能再运动了,强行使它们运动会出问题。


3.静态批处理的注意事项

  • 而且即使按照以上步骤进行了静态合批,也不一定保证会成功,必须满足以下全部条件,静态合批才会成功:
    1、游戏对象处于激活状态。
    2、游戏对象有一个Mesh Filter组件,并且该组件已启用。
    3、Mesh Filter组件具有对网格的引用。
    4、网格已启用Read/Write功能
    5、网格的顶点计数大于0。
    6、该网格尚未与另一个网格组合。
    7、游戏对象有一个Mesh Renderer组件,并且该组件已启用。
    8、网格渲染器组件不将任何材质与DisableBatching标记设置为true的着色器一起使用。
    9、要批处理在一起的网格使用相同的顶点属性。例如,Unity可以将使用顶点位置、顶点法线和一个UV的网格与另一个UV进行批处理,但不能将使用顶点定位、顶点法线、UV0、UV1和顶点切线的网格进行批处理。
  • image-20250413214501293
  • 注:即使静态合批成功,合出来的每个批次可以包含的网格顶点数是有限的,最多是64000个顶点。如果超过这个数,则会创建到另一个批次中。
  • 合成成功后(Static Batching会有数据显示)image-20250413220650463

4.思考unity光照会影响静态批处理吗

  • 在Unity中,光照设置对静态批处理的影响主要取决于物体是否使用了光照贴图(Lightmaps)和是否接受实时光照
    以下是光照如何影响静态批处理的一些情况:
    • 1,使用光照贴图的静态物体:
      当静态游戏对象使用光照贴图时,它们的光照信息被预计算并存储在贴图中。这些物体可以被静态批处理,因为它们的光照不会在运行时改变。光照贴图是静态光照的一部分,通常用于烘焙静态场景的光照。
    • 2,接受实时光照的静态物体:
      静态物体也可以接受实时光照,但这不会影响它们是否可以进行静态批处理。静态批处理主要关注的是网格和材质的合并,而不是光照模式。不过,实时光照的计算可能会在运行时产生额外的性能开销。
    • 3,动态光照与静态批处理:
      如果场景中的光源是动态的(例如,移动的点光源或聚光灯),它们会在运行时影响物体的光照。静态批处理的物体仍然可以接受动态光照,但是光照的计算是在GPU上实时进行的,这与批处理的网格合并无关。
    • 4,阴影和静态批处理:
      静态批处理的物体可以产生和接受阴影。如果使用了阴影贴图,阴影可以被预计算并存储,类似于光照贴图。实时阴影的计算不会影响静态批处理,但会增加渲染负担。

总的来说,光照设置本身不会直接影响静态批处理的过程。静态批处理是在构建时对使用相同材质的静态网格进行合并的优化技术。然而,光照设置会影响渲染性能,因此在设计场景时,你应该考虑到光照对性能的潜在影响,尤其是在移动和其他性能受限的平台上。


5.在游戏运行时进行静态合批(用的少)


  • 如果要在游戏运行时进行静态合批,则可以使用StaticBatchingUtility类的Combine方法
  • StaticBatchingUtility.Combine(GameObject 根物体)
    对指定的根物体的所有子孙物体进行静态合批。
    只有当它们符合静态合批的所有条件,静态合批才会成功。
    成功之后,这些物体就不能再运动了,强行运动会出问题。但是该根物体仍然允许运动。
  • StaticBatchingUtility.Combine(GameObject[] 要进行静态合批的游戏对象,GameObject 根物体)
    对指定的游戏对象进行静态合批,并指定它们静态合批的根物体。
    只有当它们符合静态合批的所有条件,静态合批才会成功。
    成功之后,这些物体就不能再运动了,强行运动会出问题。但是该根物体仍然允许运动。

三、动态批处理


1.什么是动态批处理

  • 动态合批也叫动态批处理,是Unity的一种优化技术

    移动的物体使用动态合批后,则Unity不会一个个绘制它们,而是把它们合并为一个批次(Batch),再由CPU把它们一次性提交给GPU进行处理,这样可以减少Draw Call带来的性能消耗,从而提高性能。

  • 官方文档:


2.如何将进行动态批处理

  • 动态合批默认是由Unity自动完成。
  • 可以在Edit——Project Settings——Player——Other Settings——Dynamic Batching查看。
  • 默认Dynamic Batching是勾选的,当条件满足时,Unity会自动对使用了相同材质(Material)的物体进行动态合批。如果取消勾选,则不会进行动态合批。

3.动态批处理的注意事项

  • 即使勾选了Dynamic Batching,也必须同时满足以下条件,动态合批才会成功:

    • (1)Unity不能对包含超过900个顶点属性和225个顶点的网格应用动态批处理。这是因为网格的动态批处理对每个顶点都有开销。例如,如果你的着色器使用顶点位置、顶点法线和单个UV,那么Unity最多可以批处理225个顶点。然而,如果你的着色器使用顶点位置、顶点法线、UV0、UV1和顶点切线,那么Unity只能批处理180个顶点。
    • (2)如果GameObjects使用不同的材质实例,Unity就不能将它们批处理在一起,即使它们本质上是相同的。唯一的例外是阴影施法者的渲染。
    • (3)带有光贴图的游戏对象有额外的渲染参数。这意味着,如果你想批处理光照贴图的游戏对象,它们必须指向相同的光照贴图位置。
    • (4)Unity不能完全将动态批处理应用于使多通道着色器的GameObjects
      • 几乎所有的Unity着色器都支持正向渲染中的多个光源。为了实现这一点,他们为每个光处理一个额外的渲染通道。Unity只批处理第一个渲染通道它不能批处理额外的逐像素灯光的绘制调用。
        遗留延迟渲染路径不支持动态批处理,因为它在两个渲染通道中绘制GameObjects。第一个通道是灯光预通道,第二个通道渲染GameObjects。
  • 注:物体必须使用相同的材质,才有可能成功进行动态合批。

  • 注:使用动态合批往往能减少CPU和GPU的开销,提升游戏性能,但同时也会占用一定的内存

    是否要开启动态合批,要根据自己的项目来定。可以尝试启用,在性能分析器中看看效果如果,如果效果好,再确定启用它。


四、GPU Instancing


1.什么是GPU Instancing


2.如果启用GPU Instancing

  • 要启用GPU Instancing,我们可以选中一个材质,然后在Inspector窗口勾选Enable GPU Instancing,这样就可以了。

3.GPU Instancing注意事项

  • 要成功使用GPU Instancing进行优化,游戏对象必须同时满足以下条件
    • (1)使用相同的材质和网格
    • (2)材质的着色器必须支持GPU Instancing。例如:标准着色器和表面着色器。
    • (3)网格的顶点布局和着色器必须相同。如果网格的顶点布局或者着色器不同,那么它们就无法被合成一个实例。
    • (4)每个实例需要有不同的变换信息(例如位置、旋转缩放)。虽然多个实例可以使用相同的材质和网格,但是它们必须拥有不同的变换信息,才能被正确地实例化并渲染出来。
  • 注: 另外需要注意的是,GPU Instancing与SRP Batcher不兼容。如果项目使用了SRP Batcher,并且配置为优先使用SRP Batcher而不是GPU实例化,启用GPU实例化可能不会生效。SRP Batcher是Unity提供的一种渲染优化技术,它可以将多个网格合并成单个批次进行渲染,从而提高性能。在这种情况下,GPU实例化将被忽略。
  • 注:使用GPU Instancing往往能减少CPU和GPU的开销,提升游戏性能,但同时也会占用一定的内存。
  • 是否要启用GPU Instancing,要根据自己的项目来定。可以尝试启用,在性能分析器中看看效果如果,如果效果好,再确定启用它。
  • 一般来说,当场景中有大量重复的网格实例时,可以尝试启用GPU Instancing。例如场景中有大量树木、草地、石块等,这些实例具有相同的网格和材质,只是位置、颜色等属性稍有差异,那么启用GPU Instancing或许能够显著提高性能。

五、遮挡剔除


1.什么叫做遮挡剔除

正常情况下,如果一个障碍物A挡住了后面的物体B,虽然我们看不见物体B,但是Unity仍然会消耗性能来渲染这个物体B。这样CPU和GPU就会有一部分性能白白浪费在渲染物体B身上。

2.如何进行遮挡剔除

  • 以一堵墙挡住几个小球为例,选中这堵墙,在Inspector窗口右上角的Static右侧的下拉菜单处选择Occluder Static,则这堵墙就是遮挡物。分别选中这些小球,在Inspector窗口右上角的Static右侧的下拉菜单处选择Occludee Static,则这些小球就是被遮挡物。

  • Occludee Static:被遮挡的静态物体

  • Occluder Static:遮挡物的静态物体

  • 注意:无论是勾选了Occluder Static还是Occludee Static,勾选后物体就无法运动了

对于一个物体,两个标签都可以勾选,这样它既可以遮挡剔除别的物体,也可以被别的物体遮挡剔除。

有时候被遮挡物只勾Occludee Static,烘焙之后可能看不出遮挡剔除的效果,建议把Occluder Static也勾上,再重新烘焙,或许就能看出效果了。

  • 设置完之后,要创建一个遮挡区域,当摄像机处于这个遮挡区域中,遮挡剔除才会生效。

  • Occlusion Area组件的Size决定了遮挡剔除区域的范围,它越大,烘焙之后生成的遮挡剔除区域就越大。Center控制遮挡区域中心点的世界坐标。Is View Volume表示是否定义视图体积,只有启用了这个选项,Occlusion Area组件才可能生效。

  • 点击创建区域,后把区域拉长完全盖住遮挡物体

  • 要让遮挡剔除生效,还要在Occlusion Culling窗口的Bake选项卡中点击右下方的Bake按钮,进行烘焙,遮挡剔除才可能生效。而且以后每次调整完场景的遮挡物、被遮挡物、Occlusion Area组件的范围,都要这样烘焙一次。如果点击旁边的Clear按钮,则会清除之前烘焙的数据。

  • 烘焙完之后,当摄像机在Occlusion Area组件的范围内,则被遮挡的物体不会被渲染。遮挡物实际上遮挡了摄像机视锥体的范围,只要物体完全没有出现在摄像机视锥体的范围内,则都不会被渲染。但是一旦物体的任意一小部分暴露在了摄像机视锥体的范围内,则这个物体整个会被渲染出来
    当摄像机移出了Occlusion Area组件的范围,则遮挡剔除会失效。

  • 示例:

3.Occlusion Culling窗口

image-20250417153621004

  • ​ Object选项卡。分别点击All、Renderers、Occlusion Areas按钮可以筛选Hierarchy窗口的内容。

  • 之后,在Hierarchy窗口或Scene窗口中选择一个筛选出来的游戏对象,就可以Occlusion Culling窗口中查看它的遮挡剔除设置。

image-20250417153741003

  • Bake选项卡。用于烘焙。遮挡剔除必须烘焙之后才可能生效。

  • Set default parameters:用于将参数重置为默认值。

  • Smallest Occluder:表示用于遮挡其它游戏对象的最小游戏对象的大小,以米为单位,数值越小遮挡的效果越精确,但是性能开销也越大。

  • Smallest Hole:表示摄像机可以看到的最小间隙的直径,以米为单位,需要注意的是,设置过小的Smallest Hole值可能会导致一些细微的间隙被错误地认为是可见的,从而导致部分遮挡物不被正确地剔除。

  • Backface Threshold:数值越小,烘焙所产生的文件所占空间就越小,但也可能造成视觉上的失真。Bake按钮用于烘焙。Clear按钮用于清除上一次烘焙的数据。

image-20250417154038723

  • Visualization选项卡。选择后,可以在Scene窗口看到遮挡剔除的效果。此时在Scene窗口可以看到三个选项。

4. 遮挡剔除注意事项

进行遮挡剔除的烘焙时,不会烘焙动态的游戏对象的信息到遮挡剔除的数据中。

  • 动态的游戏对象只能作为被遮挡物,而不能充当遮挡物。

  • 要让动态的游戏对象成为遮挡剔除中的被遮挡物,可以选中它,启用它身上Mesh Renderer组件身上的Dynamic Occlusion属性。

image-20250417154259282

5. Occlusion Portal组件

​ 使用Occlusion Portal组件也可以实现遮挡剔除。

  • 例:

  • 一堵墙上添加Occlusion Portal组件

  • 而且这堵墙不要不勾选Inspector窗口右上角下拉菜单的Occluder Static和Occludee Static,但是被它遮挡的物体仍然要勾选Occluder Static和Occludee Static。

  • 设置好之后,打开Occlusion Culling窗口,在Bake选项卡进行烘焙。

  • 这样一来,Occlusion Portal组件就会生效。

  • 当取消勾选它的Open属性后,被这堵墙就会使用遮挡剔除。当勾选它的Open属性后,被这堵墙就不会使用遮挡剔除。我们可以使用代码来控制Open属性,控制这堵墙在什么时候使用遮挡剔除,什么时候不使用遮挡剔除

  • Occlusion Portal组件的Center属性控制了中心的位置Size属性控制了遮挡的范围。点击Edit Bounds左侧的按钮后,可以在Scene窗口手动调节Occlusion Portal组件的遮挡范围。每次调整完,或者修改过场景,都要重新烘焙。

image-20250417154621975
image-20250417154646252

六、光照优化

在光照系统中补充。


七、图片优化