Unity 中的遮挡剔除

Unity 4.3

一、基础

在这里插入图片描述
下面的博客文章是由 Umbra 软件公司的 Jasin Bushnaief 撰写,解释 Unity Pro 4.3 中遮挡剔除的更新。

Unity 4.3 包含了大量的改进。其中一个完全重新实现的子系统是遮挡剔除。不仅简化了接口,改进了剔除运行时本身,还添加了一些新特性。

在这一系列的三篇文章中,我将详细介绍 Unity 4.3 中新的遮挡剔除系统是如何工作的。

第一篇文章介绍了遮挡剔除是如何完成的,以及用户界面的基本用法。
第二篇文章着重于最佳实践,以获得最大限度的遮挡剔除。
第三篇也是最后一篇文章重点介绍了一些常见的问题场景以及如何解决它们。

在这里插入图片描述
先从最基本的开始。遮挡剔除是指消除隐藏在其他对象后面的所有对象。这意味着资源不会浪费在隐藏的东西,导致更快和更好看的游戏。在 Unity 中,遮挡剔除是由 Umbra 软件开发的一个名为 Umbra 的中间件组件执行的。在 Unity 中控制 Umbra 的用户界面,可以在 Bake 选项卡下的 Window-> Ocusion Culling 中找到。

Umbra 是如何工作的

umbra 的遮挡剔除过程大致可分为两个不同的阶段。在编辑器中,Umbra 处理游戏场景,以便可见性查询可以在游戏运行时在玩家中执行。所以首先,Umbra 需要将游戏场景作为输入,并将其烘烤成一个轻量级的数据结构。在烘焙过程中,Umbra 首先对场景进行体素化,然后将体素分组成单元格,并将这些单元格与传送门结合起来。这些数据,以及其他一些重要的数据,在 Unity 中称为遮挡数据。

在运行时,Umbra 然后执行软件门户栅格化到一个深度缓冲区,根据这个缓冲区可以测试对象可见性。实际上,Unity 给了 Umbra 一个相机位置,Umbra 返回一个可见物体的列表。可见性查询总是保守的,这意味着永远不会返回假否定。另一方面,一些对象可能被视为可见的Umbra,即使在现实中他们似乎不是。
在这里插入图片描述
重要的是要认识到,虽然这个系统看起来与以前的 Unity 版本类似,但是整个系统基本上已经被重写了。很多事情都变得更好了,无论是内部还是外部!

如何使用Umbra

显然有几个考虑因素,以获得最佳的遮挡剔除。理想情况下,您希望尽快得到最不保守的结果。然而,这其中涉及到权衡。您希望得到的结果越精确 (即最不保守) ,就需要生成更高分辨率的数据。但是,较高分辨率的数据在运行时中的遍历速度较慢,从而导致较慢的遮挡剔除。如果遮挡剔除所需的帧时间比通过剔除所节省的时间更多,那么很明显它没有什么意义。另一方面,如果只筛选少数对象,那么非常快速的筛选也没有多大帮助。所以这是一种平衡。

Umbra 让你控制这种平衡的方法是让你定义几个烘焙参数。这些参数决定烘焙过程应该期望什么类型的输入以及生成什么类型的数据。在运行时,使用 Umbra 非常简单。如果你已经烘焙了遮挡数据,并且相机在检查器中启用了遮挡剔除,Unity 将自动使用 Umbra。

最小的洞(Smallest Hole)

输入采用最小孔参数控制。当体素化遮挡几何时,最小的孔几乎直接映射到体素大小。这意味着,如果你的几何图形包含你想要看穿的孔、缝隙或裂缝,使用比这些小的最小的孔是一个好主意。另一方面,很多时候几何图形包含许多你不希望看穿的无意中的裂缝。一个合理的体素分辨率将修补这些。它可能有助于考虑最小的洞作为 “输入分辨率” 的烘烤。

注意,将最小的空洞设置为一个非常小的值意味着烘焙速度会慢得让人无法接受,并且 / 或者在编辑器中占用大量的内存。在某些罕见的情况下,它甚至可能导致烘焙失败,由于内存不足。不过话又说回来,尽管使用更大的数值会更快、更便于记忆,但这可能会导致 Umbra 无法看穿栅栏或栅栏之类的东西。所以也不总是越大越好。一般来说,最小的孔尽可能大,没有可见的误差是可取的。在实践中,我们发现 5 cm 到 50cm 之间的数值对于大多数规模 “类似人类” 的游戏来说相当有效。Unity 的默认值是 25 厘米,这是一个很好的值。

在这里插入图片描述

最小的封堵器(Smallest Occluder)

虽然最小的孔主要处理输入几何体的类型,但最小的遮挡器决定生成什么样的输出数据。本质上,您可以将最小遮挡物视为数据的输出分辨率。该值越大,在运行时执行遮挡剔除的速度越快,但代价是增加了保守性(误报)。该值越小,生成的结果越准确,但代价是占用更多的CPU时间。显然,更高分辨率的数据也意味着更大的遮挡数据大小。

顾名思义,较小的值意味着在遮挡数据中捕获了非常精细的特征。在后台,这直接映射到 Umbra 创建的大单元格。大量的小单元格意味着它们之间有很多小的孔,自然而然地,栅格化大量的小 孔 比反之更昂贵。

改变最小遮挡物的影响可以在下面的图片中看到。注意深度缓冲区,本质上就是 Umbra 所看到的,随着最小遮挡物的增加而丢失细节。
在这里插入图片描述
在大多数游戏中,保持最小的遮挡物略大于玩家,所以大约几米,是一个很好的默认值。因此,如果你的游戏规模不是微观的或银河系的,那么 2 米到 6 米之间的任何地方都可能有意义。Unity 中的默认值是 5 米。

背面阈值(Backface threshold)

也许最难掌握的参数称为 背面阈值。虽然在许多情况下您实际上并不需要更改它,但在某些情况下,了解它如何影响生成的数据可能会派上用场。

首先,需要注意的是,该参数仅用于一个目的:遮挡数据大小优化。这意味着,如果您的遮挡数据大小没问题,您可能应该完全忽略背面阈值。其次,该值被解释为百分比,因此值 90 表示 90%,依此类推。

那么,背面阈值实际上是什么呢?好吧,想象一个典型的场景,主要由固体物体组成。此外,可能存在法线向上的地形网格。在这样一个场景中,你希望你的相机在哪里?嗯,当然不是在地下,这是肯定的。此外,您可能也不希望您的相机位于固体对象内部。(Your碰撞检测通常会处理这个问题)这些无效的位置也是你倾向于“看到”主要是背面三角形的位置(尽管它们当然可能会被背面剔除)。因此在许多情况下,可以安全地假设场景中的任何位置,即摄像机看到大量背面三角形的位置,都是“无效”的位置,这意味着游戏中的摄像机永远不会在这些位置结束。
在这里插入图片描述
背面阈值 参数可帮助您利用这一事实。通过定义从任何有效摄像机位置可以看到的背面几何体数量的限制,Umbra 能够从数据中去除超过此阈值的所有位置。这在实践中的工作原理是,Umbra 将通过射出光线来简单地在所有单元格中进行随机采样(参见上一篇文章),然后查看其中有多少光线击中了背向的三角形。如果超过阈值,则可以从数据中删除该单元格。请务必注意,只有遮挡物会影响背面测试,而遮挡物的朝向与此无关。值 100 将完全禁用背面测试。

因此,如果将背面阈值定义为 70,例如,对于 Umbra,这意味着场景中超过 70% 的可见遮挡物几何体不面向摄像机的所有位置都可以从遮挡数据中剥离,因为摄像机在现实中永远不会结束。例如,自然不需要能够从地形下方正确执行遮挡剔除,因为摄像机无论如何都不会在那里。在某些情况下,这可能会显著节省数据大小。

需要强调的是,从遮挡数据中去除这些位置意味着这些位置的遮挡剔除在这些位置中未定义。在这种情况下,“Undefined” 意味着结果可能是正确的、不正确的(几乎是随机的)或返回错误。在出现错误的情况下,所有对象都只是被视锥体剔除。

在这里插入图片描述
请随意尝试该参数。尝试将值减少到 90,例如,这应该会在 terrains 下方删除大量数据。了解它如何对遮挡数据大小产生任何明显影响。如果你愿意,你可以走得更低。请记住,这样做的风险由您自己承担。如果您开始弹出渲染伪影,请将该值增回 100,看看它是否能解决问题。

二、最佳实践

良好质量的遮挡

这似乎很明显,但当然,首先要确保的是您的场景实际上包含有意义的遮挡。此外,如果可能,遮挡最好由良好的大遮挡物组成,而不是从某个角度观察时仅积累为遮挡物的精细细节。Umbra 通常无法执行遮挡物融合,因此,即使拥有大量树叶的茂密森林会遮挡其后面的某些东西,它也只有在单个遮挡物“积累”后才会这样做。所以从这个意义上说,从 Umbra 的角度来看,树木和森林通常是非常糟糕的遮挡物。另一方面,山是一个很好的遮挡物,Umbra 肯定能够按预期将其捕获到遮挡数据中。
在这里插入图片描述

对象标志(Object flags)

Umbra 关心的对象主要有两种类型:遮挡物(occluders)和被遮挡物(occludees)。
前者只是几何体,Umbra 基本上将它们视为一个单一的实体模型。后者是 Umbra 实际使用遮挡数据测试其可见性的那些。遮挡物几乎由设置了“Occluder static”标志的所有几何体组成,不出所料,分别由具有“Occludee static”标志的遮挡物组成。
在这里插入图片描述
根据经验,默认情况下,您可以并且应该将大多数(如果不是全部)渲染器设置为遮挡对象,以便 Umbra 将其剔除。此外,默认情况下,大多数静态渲染器也可以是遮挡物。只需确保如果您的渲染器是非不透明的,它也不应该是遮挡物。(如果是这种情况,Unity 实际上会发出警告。这自然包括透明对象等。

但是,如果您的对象包含您希望看穿的非常小的孔(例如,考虑奶酪刨丝器或茂密的植物),但全局减少最小孔的值没有意义(请参阅上部分文章了解原因),则简单地从渲染器中删除遮挡物标志是正确的做法。

此外,由于遮挡物被视为实体,因此如果摄像机不与遮挡物相交,通常可以保证正确的剔除。这意味着,例如,如果碰撞系统无法阻止摄像机在遮挡物内飞行,则可能应该删除遮挡物标志以获得有意义的结果。

对象粒度(Object granularity)

鉴于 Umbra 执行对象级遮挡剔除,因此拥有几公里大小的对象没有多大意义。如此巨大的物体很难剔除,因为物体的某些部分几乎总是可见的,尤其是与 Umbra 的保守剔除相结合。因此,将地形分割成多个块通常是一个好主意,除非您希望整个地形始终可见。

在遮挡剔除方面,通常最好的对象细分是自然细分,这意味着自然不同的对象在剔除中可能也应保持独立。因此,过于激进地对对象进行分块通常没有帮助。应该只对可见性相似的对象进行分组。另一方面,过于精细的细分可能会引入一些不必要的每对象开销。实际上,只有当场景中有数万个被遮挡物时,这才会成为一个问题。

也许应该强调的是,只有 occludees 的对象细分才重要。无论如何,遮挡物都被认为是一大凸多边形。

水密模型 - 无孔的有体积固体(Watertight models)

在文章上部中,简要介绍了 Umbra 如何首先对遮挡几何体进行体素化,将这些体素分组到单元格中,然后将这些单元格与门户连接起来。在此过程中,Umbra 始终是保守的,这意味着在各种情况下,Umbra 认为遮挡物略小于实际大小,或者相反,空白区域略大。

这意味着,如果遮挡几何体中碰巧有一个无意的孔洞,该洞没有被体素化修复,而是被保留了下来,那么它很有可能在最终输出数据中被放大。这可能会导致 occluation 中出现令人惊讶的 “泄漏”。摄像机可能正在观察一堵看似坚固的墙,但墙后面的东西并没有被遮挡,因为某处有一些不明显的小裂缝。

因此,虽然体素化确实修补了遮挡几何体中的许多无意的裂缝和间隙,但尝试尽可能紧密地对几何体进行建模仍然非常重要。在下一篇文章中,我将介绍 Visibility Lines 可视化,这可能有助于您调试此类问题。

寻找正确的参数值(Finding the right parameter values)

诚然,使用 Umbra 最难的部分是找到正确的参数值。Unity 中的默认值可以作为很好的起点,假设一个 Unity 单位映射到游戏中的一米,并且游戏的比例是“类似人类的”(例如,不是在分子水平上建模,您的典型对象也不是行星或巨大的杀手-机甲-机器人)。
在这里插入图片描述
一个好的经验法则是从相对较大的值开始,然后逐步减少。例如,在最小孔的情况下,您可以使用的值越大,烘焙过程就越快。因此,仅当开始遇到剔除伪影(即漏报)时,才应将其调低。同样,对于最小遮挡物,从相对较大的值开始通常是有意义的。

然后你可以开始向下调整它,看看 Umbra 如何更好地剔除。当剔除开始占用太多时间和/或遮挡数据变得太大时停止。

至于背面阈值,从 100 开始。如果您的遮挡数据太大,或者当您在相机非常非常接近甚至可能与遮挡物相交时碰巧得到奇怪的结果,请尝试使用 90 甚至更小的值。

三、故障排除

可视化(Visualizations)

Unity 提供了几个帮助程序,用于了解遮挡剔除中发生的情况。这些可视化效果可以帮助您找出为什么遮挡剔除的行为不如您预期的那样。可以通过启用 Occlusion 窗口中的 Visualization 窗格并选择摄像机来找到可视化效果。

在这里插入图片描述
然后,可以在 Scene 视图的 Occlusion Culling 对话框中启用和禁用各个可视化效果。
在这里插入图片描述

相机体积(Camera Volumes)

Camera Volumes 可视化效果仅以灰色框的形式显示摄像机位于哪个单元格中。有关单元格是什么的更多信息,请查看第一篇文章。例如,这是确定最小遮挡物的值如何更改数据输出分辨率的一种方法。此外,如果单元格边界看起来没有意义,例如,当单元格错误地延伸到应该是遮挡墙的另一侧时,则可能有问题。
在这里插入图片描述

可见性线(Visibility Lines)

可见线可视化的目的是向您展示本影看到的视线。它的工作方式是,Umbra将其深度缓冲投影回场景,并在相机视图中最远的非遮挡点绘制线条。这可以帮助你弄清楚,例如,哪些洞或缝隙会导致遮挡中的“泄漏”,最终导致一些物体变得可见。这也可能揭示一些可疑的情况,某些对象显然应该是一个很好的遮挡,但却没有遮挡任何东西,因为,比如说,忘记为对象启用静态遮挡标志

门户 (Portals)

门户的可视化将把所有经过的门户画成半透明的轴对齐的四边形。这不仅可以帮助你了解 Umbra 浏览了多少个门户,从而帮助你处理遮挡剔除性能调整问题,而且它还提供了另一种方式来观察 Umbra 的视线。所以你可以看到场景中是否有一些不会引起遮挡的点,以及门户是如何被放置到场景中的。

在这里插入图片描述

故障排除(Troubleshooting)

隐藏的东西没有被清除!

有时人们想知道为什么有些物体被暗影报告可见,而实际上它们似乎是被遮挡的。这可能有很多原因。最重要的是要明白,阴影总是保守的。这意味着当空气中存在任何不确定性时,它总是选择可见的物体而不是不可见的物体。这也适用于所有打平的情况。

另一件需要注意的事情是遮挡数据代表了场景遮挡器的简化版本。更具体地说,它代表了一个保守的简化版本,意味着一些遮挡侵蚀和丢失细节。

保留在数据中的细节级别由最小的遮挡器控制。减少值将产生更高分辨率的数据,这些数据应该不那么保守,但同时,剔除将失去一些速度,数据将变得更大。

可见物体正在被剔除

也许最令人费解的问题场景是当某些东西被暗影报告为闭塞,即使它不应该是。在所有保守的承诺和永远不返回虚假的否定之后,怎么会发生这种情况?

可能会有一些事情发生。第一种,也是目前最常见的情况是,你通过一个洞,缝隙或裂缝看到的东西,得到巩固的暗影的体素化。所以通常你首先要做的就是降低最小孔的值,看看这样是否能解决问题。您可以尝试暂时调低它,甚至相当多,只是为了测试是否存在这个问题。

在某些情况下,这一点可能并不完全明显。例如,如果你有一个书架在你的场景中,单独的书被标记为遮挡物,太大的一个最小的洞可能会导致一些书被书架或其他的书遮挡。所以,减小最小孔的数值可能是你首先要做的事情。

另一种情况下,物体可能会消失,是当你的背面限制已设置为 100 以下的东西,你的相机是在附近的背面三角形。请注意,相机实际上并不一定要看着三角形,也不一定要在特定的位置面向远离相机的三角形。只要在摄像机附近有一个拓扑连接的地方 (即不在墙后面或任何东西后面) 就足够了,从那里可以看到一些背面的三角形。

要解决这个问题,首先要做的显然是尝试将 backface 限制设置为 100,看看这样是否能解决问题。如果是这样的话,修改几何图形可能是有意义的,或者通过重新建模一些资产,使其成为双面的或实体的,或者只是从有问题的对象中移除静态遮挡标志。或者,如果您不关心遮挡数据的大小,或者不能从后台优化中获得巨大的好处,那么通过将值设置为 100 来禁用后台测试当然也是一种选择。

剔除变得非常奇怪非常接近或内部封堵器(Culling gets weird very close or inside an occluder)

如果你的相机进入遮挡物内部,或者无限接近遮挡物,那么剔除可能会表现得很奇怪。通常这可能发生在一个游戏与第三人称摄像机。因为阴影认为遮挡物是固体物体,从内部剔除通常意味着你场景中的大部分东西都会被剔除。另一方面,如果背面测试已经启用,许多位置内的遮挡将被从数据完全删除,产生未定义的结果。因此,不应该让相机去内遮挡

更具体地说,一般来说,当相机离遮挡物的距离远于最小孔的值时,Umbra 将能够保证正确的剔除。在大多数情况下,靠得更近仍然有效,但在某些情况下,由于体素分辨率对遮挡数据的准确性的限制,超级靠近遮挡物可能会导致相机被错误地分配到遮挡物内部的位置。提示: 使用 “摄像机体积” 可视化来查看摄像机位于哪个单元以及它看起来是什么样子。

一般来说,当背面测试启用时 (即背面阈值小于 100) ,Umbra 会在遮挡物附近做得更好,因为它能够检测遮挡物的内部,并相应地将所有有效位置稍微向它们扩大,这样即使你任意靠近遮挡物,你也能得到正确的结果。因此,如果你不能阻止你的相机去非常接近 (甚至稍微内部) 遮挡,第一件事你可能想尝试的是设置背面阈值小于 100。这将有助于扩张,并可能解决问题。

如果调整背面阈值没有帮助,或者如果你的相机深入到遮挡物内部,剩下的唯一事情就是简单地从物体上移除遮挡标志。

筛选太慢了(Culling is too slow)

缓慢淘汰的原因通常很简单。Umbra 遍历了太多的门户,因此可见性查询需要很长时间。控制遮挡数据中门户分辨率的参数是最小的遮挡器。较大的值将产生一个分辨率较低的门户图,在一定程度上,该图通常可以更快地遍历。然而,在某些情况下,情况并非如此。具体来说,当不得不保守地简化遮挡数据时,有时低分辨率图表的保守性增加可能导致视距增加,并且穿越门户的总量也随之增加。但这不是最典型的情况。一般来说,一个较大的最小遮挡值将产生在运行时处理速度更快的数据,以降低遮挡的准确性为代价。

另一个方法是修改场景的几何形状,这样在有问题的区域,视图距离就不会变得太长。手动将遮挡插入到开放区域当然会导致导线更快地终止,减少处理门户的数量,从而使遮挡剔除更快。

烘焙太慢了(Baking is too slow)

烘焙的速度很大程度上取决于一件事: 需要处理的体素数量。反过来,被处理体素的数量由两个因素定义: 场景的尺寸和体素的大小。假设您不能对前者做很多事情,那么您可以使用最小的孔参数轻松地控制后者。一个较大的值当然会加速烘焙。因此,如果你的对象因为过于积极的遮挡生成而不正确地消失,那么从一个相对较大的值开始,然后调整它是有意义的。一个微小的空洞可能会导致烘焙耗时长久和 / 或消耗大量的记忆。

闭塞数据太大了(Occlusion Data is too large)

如果烘烤场景会产生过多的遮挡数据,有几种方法可以尝试。首先,将 backface 限制的值更改为小于 100 的值,例如 99、50 甚至 30,这可能是一个良好的开端。如果您这样做,请确保剔除工作正确的所有领域,您的相机可能在。有关更多信息,请参见上一篇文章。

如果不能选择改变背面限制,产生不可预测的结果或者没有足够地减少数据大小,你可以尝试增加最小遮挡物的值,这决定了遮挡数据的分辨率,因此对数据大小有非常重要的影响。请注意,增加最小遮挡也增加了结果的保守性。

最后,值得注意的是,大场景自然会比小场景产生更多的遮挡数据。遮挡数据的大小显示在 “遮挡” 窗口的底部。在这里插入图片描述

分裂阶段的失败 (“Failure in Split Phase”)

在一些罕见的情况下,场景是巨大的大小和最小的遮挡参数已被设置为一个超小的值,烘焙可能会失败与错误 “Failure in Split Phase”。出现这种情况是因为烘烤的初始步骤试图将场景细分为计算块。细分是基于最小的遮挡参数,当场景是巨大的大小 (如,几十公里,每个方向) 太多的计算瓦片可能会创建,导致内存错误。这反过来向用户显示为 “分裂阶段的失败”。增加最小遮挡物的值和 / 或将场景分割成更小的块可以消除这个错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值