BandMan home

统一渲染

网上许多讨论了一些最流行的 LOD 管理方法。我认为大多数 LOD 方案对于它们提供的好处来说太复杂了,我对我将选择在大型通用场景中实现 LOD 管理的方法进行了高级概述。

系统目标

我们希望系统减少用于渲染远处物体的三角形数量,因为这些物体不需要精确的细节。一些算法,如 Lindstrom-KollerROAM 的后代,通过以单个三角形的粒度修改世界网格来实现这一点。这些算法在高细节水平上慢得令人无法接受。此外,这些系统中的许多系统都试图利用帧相干性,这使得它们不适合交互式系统。

因此,我们希望系统以大粒度运行,一次性提高或降低大块几何体的细节。 Thatcher Ulrich 恰当命名的“Chunked LOD”系统就是这样工作的,而且运行速度非常快。但是,它使用“二元三角树”细分,与 ROAM 使用的细分相同。这种镶嵌在如何分配三角形方面效率非常低(尽管您不会在任何促进二叉三角树的研究论文中找到认真的效率分析!)。此外,这意味着规则网格高度场是唯一一种可以轻松使用该算法的几何图形。通过大量工作,该算法可以扩展到其他拓扑。但是所需的工作量很大,它极大地使运行时系统复杂化,并且系统仍然无法处理任意输入网格。

Thatcher 选择了 BTT 拓扑,以便轻松填充相邻地形块之间的裂缝。这很重要,因为裂缝看起来很糟糕。我们希望正确、快速地填充裂缝,但不限制网格拓扑。任意网格拓扑很重要,因为我们想要创建一种 unified LOD 管理方法,该方法可用于广阔的室外地形、小型封闭地牢或动画角色。

除了拓扑之外,我们希望系统对网格存储和渲染设置最少数量的其他约束。在渐进式网格中可以看到此类约束的一个示例。将渐进式网格渲染为一系列三角形条带是一项复杂且效率低下的任务;将其渲染为一系列顶点缓存优化的三角形条带几乎是不可能的。如果我们引入太多约束,那么当我们向引擎添加新功能时,例如模板阴影或法线映射几何增强,我们可能会发现新功能的约束与旧的 LOD 约束发生冲突。这迫使我们消除新功能,或者转储我们的 LOD 系统并创建一个新功能。两种结果都是不好的。

最后,我们希望系统不会出现从一个 LOD 到另一个 LOD 的弹出。弹出看起来很丑,会分散玩家的注意力;通常我们正在寻找周围环境中的运动,而 LOD 弹出会产生错误的运动。因为我们选择在较大的网格粒度上调整细节,所以我们自然会得到很多爆裂,除非我们付出很大的努力来避免它。

总而言之,我现在将所有主要目标列为要点:

减少远处几何体的细节 大粒度工作 不要产生可见的爆裂声 不要在几何块之间产生裂缝 不限制网状拓扑 不要过度限制应用程序对网格表示的选择 适应未来的技术

减少远距离几何的细节

我的总体方法是在预处理时将世界几何体切割成碎片,并为每个碎片生成多个细节级别。在运行时,我将为每个部分选择适当的细节级别,然后渲染它,填补裂缝。

为了生成各种级别的细节,我将使用 Garland-Heckbert 误差二次简化,或“EQS”(更多信息请参见)。 EQS 的输入是一个任意拓扑的网格,它产生一个任意拓扑的网格。我们喜欢这样,因为它不限制我们。

当减少网格时,我使用 Garland 和 Heckbert 所说的“子集放置”:我选择一个顶点,然后拖拽到附近顶点的位置,然后移除所有已经退化的三角形。另一种选择是“最佳放置”,您可以在其中求解新顶点的位置,然后将两个顶点移动到该新位置。子集放置比最佳放置更容易编程,但它可能会产生较低质量的输出三角剖分。尽管如此,我还是选择了子集放置,因为它具有极端的通用性。输出顶点是输入顶点的子集,这意味着它们包含有效且未失真的网格数据。对于最佳放置,您必须进行内插和外推以生成每个顶点的值(不仅是位置,还有纹理坐标,可能是切线帧、动画网格的骨骼混合权重或其他任意顶点相关数据)。线性插值可能不够好,或者可能需要专门的重整化;外推值可能需要以特定于应用程序的方式进行限制。绝对可以做这些事情,但仍然不能保证机器生成的坐标看起来不错。为了获得潜在的微小视觉收益,需要付出大量额外的工作和软件的复杂性。此外,由于工作高度依赖于存储在每个顶点的数据类型,因此很难制作以“不干涉”方式运行的通用工具。

临床:选择使用最佳放置会在网格简化工具和数据格式之间产生额外的依赖性。由于依赖性是软件工程和项目管理的祸根,因此最好在几乎所有情况下都使用子集放置。

将几何体分成几部分

现在,为了更容易地将世界分成块,并简化其他任务,如裂缝填充,我们将限制自己渲染高度场。不过,到本系列结束时,我们将开始操作三角汤。这里有一条很好的路线——我想让系统足够简单,以便我可以快速进行编程,所以我只从高度字段开始;但同时,我不希望这是一个永久性的限制,所以我必须小心不要依赖不能扩展到高度场之外的技术。换句话说,对于我现在强加的每一个简化,我都需要有一个可信的故事来说明未来系统的这一部分将如何升级。如果你愿意,这是一种算法引导。

使用高度场,很容易划分世界:你有整个世界的一些大的高度样本数组,然后复制出该数组的矩形子部分。因为数组是均匀采样的,所以我们可以很容易地匹配块边缘的顶点,这是填充裂缝所必需的(稍后讨论)。请注意,这些小节的大小或纵横比没有限制;您可以根据自己的需要任意选择。这与二叉三角树算法形成鲜明对比,二叉树算法希望你的块是方形的并且大小为二加一的幂,这通常很不方便。

在未来的升级中,为了处理任意扩展到所有三个维度的不受限制的输入网格,我将根据轴对齐的平面裁剪网格,将它们分成一堆立方体形状的区域。当在平面上裁剪三角形时,我们自己创建所有新顶点,实际上我们是成对创建的。通过保存有关哪些顶点对应的信息,我们将很容易执行裂缝填充。但这是未来文章的主题。

大粒度运行

假设我们选择高度字段的子部分,它们是 21x21 样本。这给了我们 20x20 四边形,或每块 800 个三角形。假设我们使用网格简化在 400 个三角形、200、100、50 和 25 处生成块的低分辨率版本。根据每个块到相机的距离,我们选择这些细节级别之一并对其进行渲染。结果如图 1a 所示。这很糟糕,因为大多数渲染块由少量三角形组成。当前的图形硬件和驱动程序不太喜欢。游戏会运行缓慢。

为了解决这个问题,我们可以在将地形块放入EQS例程之前将它们分层组合以减少它们。由于我们正在处理高度场,因此在简化之前,我选择一次组合 4 个块。如果原始块每个是 800 个三角形,我将其中的 4 个组合成一个 3200 个三角形的块;然后我将网格简化为 800 个三角形。因此,所有渲染块都具有相同数量的三角形,而不管比例如何,如图 1b 所示。这简化了我们稍后将讨论的一些数学运算。

使用全 3D 输入几何体,我会将 8 个块合并为 1 个,这需要更极端的缩减率。您可能希望考虑其中的含义。

Fork me on GitHub