UE关卡中的那些剔除!哪种剔除更加节约性能?(优化篇二)
Game艺视界原创文章原创 15115 0
实名

通过了实名认证的内容创造者

发布于 2023-4-28 15:41:44

您需要 登录 才可以下载或查看,没有账号?注册

x

本期笔记内容我们真正想要做的是只渲染那我们所关心的对象,来减少需要提交到GPU的图元数,场景总是会有很多东西是看不到的他们会增加drawcall比如某些物体。虽然他在屏幕之外但是不做处理还是会被发送到gpu渲染
剔除就是先遍历一遍所有物体,然后除去我们就算看不到也不在意的东西之后得到最后的渲染结果


UE4提供了几种现成的剔除方法:
剔除方法用于跟踪关卡中每个Actor的可视性状态。场景数据根据项目所采用的方法进行剔除和测试
以下剔除方法根据成本按照以下顺序应用:
[backcolor=rgba(255, 192, 192, 0.376)]基于距离的剔除
基于视锥体的剔除:即只保留视锥体内和视锥体相交的物体

预计算可见性的剔除:这种剔除一般是用离线计算完成的也可以用到移动设备

动态遮挡查询剔除:这种剔除一般依靠cpu或者gpu视你的目标设备而定

8faa42b87b5976769042517d7a926d39.png

*默认情况下,虚幻引擎对所有项目均使用视图视锥剔除和硬件遮挡查询(动态遮挡)。如果你的项目包含大量Actor,可能会影响GPU性能,尤其是场景视图中有大量Actor的情况

当你在开发游戏的时候一般不会意识到这些其实优化是一个很重要的东西
怎样把工作分配给cpu跟Gpu,如果仅仅是Gpu在计算所有的遮挡物体和被剔除的物体的话就会给性能造成一个瓶颈


可视性和遮挡剔除
虚幻引擎提供了可视性和遮挡剔除方法。这些剔除方法用于优化游戏性能。每种方法都可以通过设置是否应绘制到屏幕上来减少关卡中的可见Actor数量。部分方法(如视锥体和硬件遮挡查询)可以同时使用,或者更好地适应特定设备和平台(如用于虚拟现实的轮询遮挡)

剔除的工作原理
可视性和遮挡剔除方法的大体思路是减少任意给定时刻的可见对象数量,从而达到优化性能的目的
例如,如果我们从摄像机位置能够看到的视野来看,只能看到少量对象(场景视图)。但是,我们知道这不是整个场景,因为这个场景是由许多对象构成的,只是这些对象从这个位置看不到(俯视场景视图)

7026bbc0835c68aa6dae811fe43e37fd.png


自顶向下场景视图
摄像机视野(视图视锥)外部的对象不可见,可以被剔除(红色轮廓的对象)

不再渲染摄像机视图视锥外部的被剔除对象,这样该视图中只会留下少量的被其他对象遮挡的对象,需要检查它们是否可见。因此,在这个阶段,会向GPU发送查询来测试每个对象的可视性状态。被另一个对象遮挡的对象会从视图中剔除(蓝色轮廓的对象)

视图视锥外部或被遮挡的所有对象现在都从视图中剔除。最终场景视图现在与我们已知在场景中从摄像机位置能够看到的对象相匹配
虚幻引擎提供了多种剔除方法,每种都有自己的优势和劣势,有些方法可供特定平台使用
使用Actor边界测试可视性关卡中放置的每个Actor都有一组使用箱体和球体形成的边界,在引擎中用于多种目的。每个边界都能专门用于测试是否可见。Actor的边界由两部分组成:球体和箱体。边界球体用于简单距离测试的快速碰撞检测,通常,尺寸比其包含的对象要大。另一方面,边界箱体更接近于对象形状,提供更准确的结果。

选择显示(Show)>高级(Advanced)>边界(Bounds) 可以在关卡视口中显示Actor的边界。或者,在骨架网格体编辑器中,你可以选择 角色(Character)>边界(Bounds),然后在静态网格体编辑器中,从主工具栏选择 边界(Bounds)
显示了边界(球体和箱体)的Actor每个静态网格体和骨架网格体都有其自己的边界箱体和球体,它们会在导入时、在视口中伸缩或旋转时自动缩放为几何结构的大小

你可以使用以下方法编辑Actor的边界:
在关卡或蓝图中选中后,在 细节(Details) 面板中设置 边界缩放(Bounds Scale)。边界缩放(Bounds Scale) 统一地缩放Actor的边界,相当于原始比例值的乘数

打开静态网格体或骨架网格体编辑器,使用 细节(Details) 面板对 正边界扩展(Positive Bounds Extension) 和 负边界扩展(Negative Bounds Extensions) 应用非等分缩放
增大Actor边界会更快速地剔除Actor,因此可能会对性能和阴影质量造成潜在影响

  01-距离剔除  
按照消耗性能的排序
首先我们说距离剔除,顾名思义距离剔除是根据物体到摄像机的距离来决定剔除,他可以根据距离对每个物体进行剔除或者在一定范围内根据物体大小来剔除,你需要设定剔除距离的阈值,他会获取在这个距离和大小阈值内的所有物体


这是一种这个物体判断的方法,我设置距离比较短,让物体在摄像机一定距离的时候消失大于这个距离就不会被渲染了,并不是所有东西都适合这个方法,因为你并不希望所有东西一下全消失,只在一些你并不关心或者看不到细节的小东西上起作用把他们从场景中移除
用CPU来减少物体的数量,进而可以把他们从发送到gpu的群组中剔除并且你可以拆分对象,这对于静态网格体来说是很实用的对任意平台的项目都适用。不管移动还是VR或PC平台。你需要考虑剔除了多少物体,你不想要大量的物体突然出现导致大量查询的产生

距离剔除方法(如按Actor设置和剔除距离体积)根据Actor离摄像机的距离来渲染或不渲染Actor
按Actor距离
关卡中的每个Actor都有自己的绘制距离设置,可以使用细节(Details)面板进行设置。在这里,你可以设置应渲染该Actor的距离摄像机的最小和最大绘制距离(用虚幻单位)
使用Actor的绘制距离设置进行以下设置:
  • 应从摄像机看到Actor的 最小绘制距离(Minimum Draw Distance)。这是在不再渲染Actor之前能够接近Actor的最近距离
  • 应从摄像机看到Actor的 最大绘制距离(Maximum Draw Distance)。这是在不再渲染Actor之前能够远离Actor的最远距离
  • 你可以查看(但不能编辑)当前最大绘制距离(Current Maximum Draw Distance)。它显示剔除距离体积(如果存在)设置的已存储剔除距离

  02-视锥体剔除  
视图视锥(View Frustum) 剔除使用摄像机视野(FOV)的可见屏幕区域来剔除不在该空间的对象。该视图视锥是一个金字塔形状,包含一个近端和远端裁切平面,定义了应在该空间内可见的最近和最远对象。所有其他对象都被删除以节省处理时间

他会把摄像头范围外的物体剔除,所以视野外的任何东西我们都不需要关心当然也不需要渲染
我们有相机同时关注近裁切面1,他意味着你在大于这个距离的情况下才会被渲染,你们看看这张图就是我们最终构建的场景。然后我们有相机的FOV或相机视锥体2,然后我们还有远裁切平面3。远近裁切平面用于构建场景深度,3是我们能看到的最远距离,超出裁切平面的东西就不会被渲染以此构成场景深度



  • 近端裁切平面(Near Clipping Plane) 是能够看到对象的最接近摄像机的点
  • 摄像机视锥(Camera Frustum) 是近端和远端裁切平面之间的可视查看区域的金字塔形状表示
  • 远端裁切平面(Far Clipping Plane) 是能够看到对象的离摄像机最远的点


在关卡视口中编辑时,选择 显示(Show)>高级(Advanced) 并启用 摄像机视锥(Camera Frustum),可以显示视图视锥

视锥体的作用它剔除了一切在视锥体之外的物体,你会注意到他又几条边界是否剔除是由哪些边的长度以及是否视锥体相交来决定的

我们只关心视锥体内的东西视锥体外的于是就把他们移除不需要考虑发送到GPU以及做遮挡查询

  03-预处理剔除  
在离线的情况下,像生成灯光信息一样计算处物体的剔除信息

预计算可见性是我们在移动项目上采用的另一种剔除方式,通常它可以被用到中小型的场景,你需要做的是将一个包围体放在一般角色可以交互的区域,以及所有当你做光照构建处理的小网格,对于每一个小网格来说,它存储了在那个位置Actor的可见性,当摄像机在格子内就会建立一个动态遮挡查询的查找表,对于创建特定的风格化项目是很理想的比如玩家跳跃的高度不会超出这个格子,因为一旦高于当前编辑的这个格子高度,你就失去了剔除对象可见性的能力,以为他只在格子内生效。剔除很好用记住他适合用于中小型场景
数据存的越多,你就需要回读得越多,他的存储具有伸缩性

预计算可视性体积(Precomputed Visibility Volumes) 在阴影投射表面上的单元格中存储不可以动Actor的可视性状态。该剔除方法生成离线可视性数据(在照明构建期间),最适用于中小型关卡。预计算可视性是低端硬件和移动设备的理想选择。对于这类硬件和设备,就性能成本而言,你可以节省更占用运行时内存的渲染线程成本,而就性能而言有更大的灵活性

  04-动态遮挡剔除  
Unreal Engine中的动态遮挡系统提供了几种剔除方法可供选择。每种方法跟踪关卡中位于摄像机视图视锥(或视野)内,并被另一个Actor遮挡的Actor的可视性状态。查询将发出到GPU或CPU以检查每个Actor的可视性状态。使用启发法减少所需可视性检查次数,继而提高总体剔除效率和性能

虚幻引擎在发出遮挡查询时使用场景深度缓冲。它支持更长的视图距离,因为它依赖于近似距离,而不是最大绘制距离(或远端裁切平面)。使用深度缓冲让可移动或不可以动Actor能够遮挡另一个Actor或被另一个Actor遮挡,包括在不透明或遮罩混合模式下使用材质的Actor


我们使用的查询方法不同,也就决定了工作是放在cpu还是gpu上完成,一句话总结遮挡剔除的过程我们会想办法判断,在摄像机视野范围内一个物体是否被另一个物体遮挡了,然后通过遮挡查询来移除它




内存占用最多的一种是遮挡剔除,他用起来是最奢侈的,因为我们是用GPU做的,硬件遮挡剔除。也有使用CPU做的软件遮挡查询。我们用GPU硬件遮挡查询来检测视锥体内的物体。所以在视野里被另一个actor遮挡的物体。我有一个场景可以很好的说明这个,被其他actor完全挡住的物体都会被剔除。这需要做检测,把视野里所有的actor都丢到GPU做检测。这可能引起性能瓶颈。我们有一个场景深度,如果很多物体被送入了GPU做检测。由于所有物体都要做GPU查询。就有可能会出现性能瓶颈。他必须知道自己是否被遮挡

软件遮挡检测这个功能是4.20版本新加入的。我们在堡垒之夜移动端用到了它。它是基于CPU的,它的性能消耗是基于拟构建查询的lod网格体等级和遮挡物的数量。你可以在必要的时候使用,同时只用在指定大小的地下




另一个新的附加功能是可以在vr上使用的轮询遮挡查询,他把双眼的遮挡铲鲟换成了单眼,尽管它能得到精确的结果但必须做两次遮挡查询,同时也可能或产生双倍开销通过启用这个,可以在运行时进行检查,由于只关注一边眼睛我们节省了很多性能,但问题是可能有瑕疵由于一次检查一个眼睛,你就可能会看到一些东西突然出现你可以考虑它是否适合项目,这是一个很好的节约性能的功能

硬件遮挡查询
所采用的主要动态遮挡方法是 硬件遮挡查询,这种方法作为每个Actor一个查询的形式,每一帧向GPU发出一次可视性检查。Actor的可视性一帧后读取回来,有时这会产生负面作用,导致它们在摄像机快速移动时"弹出"。硬件遮挡成本取决于GPU上执行的查询数量。使用距离和预计算可视性方法减少GPU每帧执行的查询数量。

硬件遮挡查询默认是启用的,在支持它的任何平台上均有效,包括iOS上支持**ES 3.1**或更高版本和Android上支持**Vulkan**的更高端移动设备。不支持硬件遮挡查询的设备可以禁用,方法是从项目设置(Project Settings) 的 渲染(Rendering)>剔除(Culling)下面,取消选中遮挡剔除(Occlusion Culling)或在设备配置文件中设置以下控制台变量:
    r.AllowOcclusionQuerues=0

层级Z缓冲遮挡
层级Z缓冲(HZB)遮挡的工作方式与硬件遮挡查询类似,唯一不同的是它的剔除对象方式更保守,意味着结果会剔除更少的对象。它使用Mip映射版本的场景深度渲染目标以检查Actor的边界。此外,从较低MIP采样时也会需要更少的纹理存取。
要启用HZB遮挡,可以使用以下控制台命令:
      r.HZBOcclusion=1

面向移动平台的软件遮挡查询
软件遮挡查询 是一种遮挡方法,使用一个Actor的指定细节层次(LOD)来遮挡它后面的Actor。这种剔除方法会对CPU上的场景进行光栅化来剔除Actor,而相较而言,硬件遮挡查询则对GPU执行可视性检查。软件遮挡的保守式设计意味着可以在任何移动设备上启用和使用。
    vr.RoundRobinOcclusion=1

进入场景我们实际查看下

开启摄像机视锥体显示:视锥体内是我们在屏幕上捕获的内容的,通过相机高亮显示我们可以看到最后的图像,你看不到任何红色或蓝色。我想表达的就是这些不同颜色的对象虽然显示在场景里却最终没有出现,我们得到了最终的图像同时也得到了一堆无色的网格体,我们只要确定它不被渲染因为我们耗费额外的性能

我们一开始就不关心红色,因为所有这些物体都在相机视锥体外哪些将会被视锥体剔除,这些东西会被完全抛弃,场景中的其他网格蓝色,都被白色的物体遮挡了。所以当我们选中摄像机的时候完全看不到蓝色的物体,为什么要在看不到东西上耗费性能那!GPU在这自动做了动态遮挡,它检查了所有处于视锥体内的并且在远裁切平面内的物体,我们并不在乎哪些物体

我们在控制台输入:freezerendering (渲染冻结)
所以视锥体外的东西我们都不关心所有被其他物体遮挡的对象我们也都不关心
无法关闭视锥体裁剪他们一直是开启的,移动设备上也是所有视锥以外东西我们都不关心

遮挡剔除就是我们只关注视锥体内的物体,同时移除不关注的一般情况下遮挡剔除都是基于Gpu的,其他看不见的物体也就是处于视锥体和剪切平面之外的东西。要比可以看见物体的数量多得多,因为这个所以要移除他们

如果您在控制台输入:stat initviews 包含循环计数器统计了调用函数所花费时间的长短,还有一些统计场景中图元的计数器
View visibility、occlusion cull、frustum cull这些函数被调用的次数还有一个decompress occlusion它用于与计算可见性的。然后可以看到每一项所花费的时间
最后一栏:这个数量取决于你如何观察当前场景,你是否在控制摄像机是否在使用游戏视角如果你在编辑器模式下就必须意识到会有一些额外的计数开销例如天光、方向光因为他们是在游戏世界中是以actor形式存在的哪些包含网格体的东西

下图这块是我们要讨论的内容;视锥体裁剪的图元数量
已经有40个物体被视锥体剔除了,因为从当前相机视角我们看不到他们。遮挡剪裁关注所有处于视锥体并被其他东西遮挡的物体
toggledebugcamera:这个命令只有在编辑器运行时才能执行
F:冻结渲染
,(逗号):调整FOV


边界缩放
actor有一个边界缩放值控制包围体的大小bound


包围盒就是用来检测物体可见性的,你可以在每个actor上做这样的设置,所以你还可以在静态网格体编辑器中执行这样的操作

统一缩放包围盒

可以看到某些对象会更常出现而不会被剔除,如果某个对象藏在墙后面遮挡测试就会建厂对象包围盒并不是检测实际的网格体,所以如果我把他放大或者缩小,它会可能突然出现又消失,因为无法在每个时刻都能看到包围盒。这有助于提前加载但这取决于场景大的复杂度 我建议少用点


回顾和提示与技巧
  • 剔除方法


      • 查看视锥体和硬件遮挡查询
      • 距离剔除 - 尽可能使用 - 但今天没有展示
  • 大视野距离-大遮光器
  • 减少黑暗区域明显的“爆裂声”
  • 深思熟虑的资产建设(如有可能)
  • 有用的调试命令
    • 冻结渲染
      • 如果您搜索“冻结”命令,则还有其他命令:
        • 叶子 - Foliage.Freeze
        • GPU 粒子 - FX.FreezeGPUSimulation
        • CPU 粒子 - FX..FreezeParticleSimulation
  • r.VisualizeOccludedPrimitives1
  • 切换调试相机

另一个我推荐的时基于距离的剔除,它易于设置
[backcolor=rgba(255, 192, 192, 0.376)]针对于开放世界的游戏制作,因为这类有戏往往都有很大的视野范围,我们可以用大型的遮挡物来剔除小物件键入类似山脉的东西完全可以充当大型遮挡物,用堡垒之夜举例:他的相机有很大的可视范围同时也很容易注意到遮挡物连绵起伏的群山,所以总使会有物体被剔除的


我们使用遮挡物来剔除哪些在它们后面你的东西,还可以帮助你节省一些性能。使用基于距离的剔除挺有帮助的这减少了切换的突兀感和黑暗的区域

问答:剔除与LOD区别
这是两个不同的东西,他们是协同工作一起生效。LOD是为了减少场景中几何图形的数量也就是网格体的面数,二剔除方法是为了减少drawcall并不需要绘制我们不关心的物体

风筝那个Demo我们把这个技术用在了主人公周围草在玩家可视范围内的草我们不是一次性绘制完的而是渐进显示的如果你一旦进入范围就画完了就会有很多overdraw你就只能为了视觉效果牺牲性能了,所以你需要降低性能的消耗


性能统计信息
无论游戏大小多大,一个最重要的开发阶段是性能优化。从场景剔除对象是优化性能的好方法。统计信息窗口使你可以查看关于可视性和遮挡剔除性能的各种统计数据

例如,对于预计算可视性,你需要留意运行时加载剔除对象数据所用的内存。对于硬件遮挡查询,如果遇到性能问题,例如卡顿或迟滞,你需要检查任何给定帧期间发送到GPU的Primitive数量

在控制台窗口中输入 stat initviews 可以调用统计信息窗口

显示的统计信息拆分到两个部分:循环计数器(Cycle Counters) 和**计数器(Counters)。循环计数器(Cycle Counters)统计信息侧重于处理Primitive所用的渲染循环数量,以毫秒(ms)计。这里你应重点关注 视图可视性(View Visibility)、遮挡剔除(Occlusion Cull)、视图相关性(View Relevance) 和 视锥剔除(Frustum Cull)**。计数器(Counters)统计信息添加已经在当前视图中处理的所有Primitive

在测试可视性和遮挡剔除时需要记住以下几点:
  • 遮挡剔除在线框视图模式下是禁用的
  • 在视口中工作时,使用热键 G 来切换到游戏模式视图,以查看某些剔除方法,例如剔除距离体积或预计算可视性体积
  • 为获得最准确结果,在 在编辑器中运行(PIE)或 独立游戏(Standalone Game) 中使用Stat InitViews统计信息。在计算可视性和遮挡剔除结果时,将包含表示光源、摄像机和其他对象的Actor的几何结构
  • 留意可见静态网格体元素(Visible Static Mesh Elements),因为这是对渲染线程时间影响最大的一个因素,应该仔细观察并优化

调试剔除
下面是可以用于可视性和遮挡剔除的调试选项。
可视化被遮挡Actor
在编辑器中工作时,可以使用可视化命令检查Actor是否被遮挡
     r.VisualizeOccludedPrimitives 1

启用时,将围绕着任何被遮挡Actor绘制一个绿色边界框
如果你的关卡中有大量Actor,在不隐藏部分关卡及其包含的Actor的情况下,该可视化可能并不是有效的调试方法


在该示例中,请注意,只有完全被墙壁和门挡住的Actor被遮挡了。右侧通过墙壁上的孔能够看到的Actor并没有完全被挡住
冻结场景渲染
在编辑器中工作时,可以"冻结"关卡中Actor的渲染状态,这样可以在关卡视口中自由移动并检查遮挡结果。
从所需视图输入命令`FreezeRendering`,如以下示例所示

输入后,在场景中自由移动以查看遮挡结果。画面移到墙壁后面后,会看到没有渲染完全被遮挡的对象,但仍会渲染没有被完全遮挡的Actor

使用以下命令冻结其他类型Actor的渲染状态:
使用游戏视图模式
  • 在编辑器中工作时,你可以使用 游戏视图(Game View) 模式来了解游戏会有怎样的效果。通过这种视图模式,你可以在任何运行模式下(如编辑器中运行(PIE)模式或独立游戏)直观地看到游戏效果,同时仍可以在场景中四处移动并自由地编辑对象
  • 在编辑器中工作时,使用键盘快捷键 G 或使用视口下拉菜单,并选择游戏视图来启用游戏视图。
  • 启用 游戏视图(Game View) 时,Actor图标会被隐藏(就像在游戏中一样),例如照明和粒子系统的Actor图标。如果使用剔除距离体积或预计算可视性体积(并且在单元格中),你会立即看到这些体积的剔除结果,具体取决于它们的的剔除强度

延伸阅读:
官方文档

https://docs.unrealengine.com/4.26/zh-CN/RenderingAndGraphics/VisibilityCulling/SoftwareOcclusionQueries/
好!本期笔记分享就到这里,我们下期见!

微信公众号:Game艺视界
使用道具 <
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表