渲染

Real-time Rendering 第十九章 加速算法(下)

Spread the love

19.7 遮挡剔除 —— Occlusion Culling

正如我们所看到的,可见性可以通过z-buffer来解决。尽管它正确地解决了可见性问题,但z-buffer相对简单且具有较强的蛮力,因此并不总是最有效的解决方案。例如,假设观察者沿着一条放置了10个球体的线观察。如图19.18所示。从这个角度呈现的图像将只显示一个球体,尽管所有10个球体都将栅格化并与z-buffer进行比较,然后潜在地写入颜色缓冲区和z-buffer。图19.18的中间部分从给定的角度显示了这个场景的深度复杂性。深度复杂度是一个像素所覆盖的表面数量。在10个球的情况下,假设进行了backface culling,当所有10个球都位于中间时,像素的深度复杂度为10。如果场景从后往前渲染,中间的像素将被像素渲染10倍,即,有9份不必要的像素着色器执行。即使场景是由前向后渲染的,所有10个球体的三角形仍将被栅格化,深度将被计算并与z-buffer中的深度进行比较,即使生成了单个球体的图像。这个无趣的场景不太可能在现实中找到,但是它描述了(从给定的角度)一个渲染密集的模型。这些配置可以在真实场景中找到,比如雨林、引擎、城市和摩天大楼的内部。有关示例,请参见图19.19。

考虑到上一段中的例子,似乎有可能采用一种算法方法来避免这种低效率,从而在性能上获得回报。这种方法被称为遮挡剔除算法,因为它们试图剔除遮挡的对象,也就是场景中其他对象隐藏的对象。最优遮挡剔除算法只选择可见的对象。从某种意义上说,z-buffer只选择并渲染那些可见的对象,而不必通过大多数管道发送视锥体中的所有对象。有效遮挡剔除算法背后的思想是在早期执行一些简单的测试来剔除隐藏对象集。从某种意义上说,背面剔除是遮挡剔除的一种简单形式。如果我们事先知道一个对象是实心的并且是不透明的,那么后面的面就会被前面的面遮挡,所以不需要渲染。

遮挡剔除算法主要有两种形式,即基于点的和基于单元的。如图19.20所示。基于点的可见性通常用于渲染,也就是说,从单个查看位置看到的内容。另一方面,基于单元格的可见性是为单元格完成的,单元格是包含一组查看位置的空间区域,通常是一个盒子或球体。基于单元格的可见性中的不可见对象必须从单元格内的所有点都不可见。基于单元格的可见性的优点是,一旦计算了单元格的可见性,通常可以将其用于几个帧,只要查看器位于单元格内。然而,计算通常比基于点的可视性花费更多的时间。因此,它通常作为预处理步骤来完成。基于点和基于单元的可视性本质上类似于点光源和区域光源,在这些光源中,光可以被看作是场景的视线。对于一个不可见的物体,这等价于它在本影区域,即,完全笼罩在阴影中。

我们还可以将遮挡剔除算法分为在图像空间、对象空间或射线空间中操作的算法。图像空间算法在经过一定投影后在二维空间进行可见性测试,而对象空间算法则使用原始的三维对象。射线空间方法[150,151,923]在双空间中进行测试。每个感兴趣的点,通常是二维的,在这个对偶空间中被转换成一条射线。对于实时图形,在这三种算法中,图像空间遮挡剔除算法应用最为广泛。

图19.21显示了一种遮挡剔除算法的伪代码,其中函数isOccluded,通常称为可视性测试,检查对象是否被遮挡。G为待渲染的几何对象集合,或为遮挡表示,P为可与OR合并的潜在遮挡器集合。根据特定的算法,OR表示某种遮挡信息。OR在开始时设置为空。然后,处理所有对象(通过视图筛选测试的对象)。

考虑一个特定的对象。首先,我们测试对象是否根据遮挡表示被遮挡。如果它被遮挡,那么它就不会被进一步处理,因为我们知道它不会对图像产生影响。如果不能确定对象被遮挡,则必须渲染该对象,因为它可能对图像有贡献(在渲染的那个点)。然后将对象添加到P中,如果P中的对象数量足够大,则可以将这些对象的遮挡能力合并到OR中。因此,P中的每个对象都可以用作遮挡器。

注意,对于大多数遮挡剔除算法,性能取决于绘制对象的顺序。例如,考虑一辆装有马达的汽车。如果先把引擎盖盖上,那么发动机(很可能)就会被剔除。另一方面,如果先画出电机,则不会剔除任何东西。按前后顺序排序和渲染可以获得可观的性能收益。此外,值得注意的是,小对象也可能是优秀的遮挡器,因为到遮挡器的距离决定了它可以遮挡多少。例如,火柴盒可以遮挡金门大桥,如果观众足够接近火柴盒。

19.7.1 遮挡查询 —— Occlusion Queries

gpu通过使用特殊的渲染模式支持遮挡剔除。用户可以查询GPU,查看一组三角形与z缓冲区的当前内容相比是否可见。三角形通常形成更复杂对象的包围体(例如,一个框或k-DOP)。如果这些三角形都不可见,则可以剔除对象。GPU对查询的三角形进行栅格化,并将三角形的深度与z缓冲区进行比较,即,它在图像空间中工作。生成这些三角形可见的像素个数n的计数,但实际上没有修改任何像素或深度。如果n为零,所有三角形都被遮挡或剪切。

但是,零的计数不足以确定体积是否不可见。更准确地说,摄像机截锥平面附近可见的任何部分都不应该在包围体内。假设满足此条件,则整个包围体被完全遮挡,所包含的对象可以安全地丢弃。如果n >为0,则有一小部分像素未通过测试。如果n小于像素的阈值,则可以丢弃该对象,因为该对象不太可能对最终图像有太大贡献[1894]。这样,速度可以用来交换质量的损失。另一个用途是让n帮助确定对象的LOD(第19.9节)。如果n很小,则对象的一小部分(可能)是可见的,因此可以使用不太详细的LOD。

当发现包围体被隐藏时,我们通过避免向渲染管道发送潜在的复杂对象来提高性能。然而,如果测试失败,我们实际上会损失一些性能,因为我们花费了额外的时间测试这个包围体,但是没有任何好处。

这个测试有不同的版本。出于筛选的目的,不需要确切的可见片段数量——它只需要一个布尔值就足够了,该布尔值指示是否至少有一个片段通过了深度测试。OpenGL 3.3和DirectX 11以及以后的版本都支持这种类型的遮挡查询,在OpenGL[1598]中作为任何通过测试的采样枚举。这些测试可以更快,因为它们可以在一个片段可见时终止查询。OpenGL 4.3及更高版本还允许此查询的更快变体,称为任何保守通过测试的采样。实现可以选择提供不太精确的测试,它是保守的,并且在正确的部分有一些错误。例如,硬件供应商可以通过仅对coarse depth buffer(第23.7节)而不是对每个像素的深度执行深度测试来实现这一点。

查询的延迟通常比较长。通常,在此时间内可以渲染数百或数千个三角形,有关延迟的更多信息,请参见第23.3节。因此,这种基于gpu的遮挡剔除方法在包围框中包含大量对象且遮挡量较大时是值得的。GPU使用遮挡查询模型,CPU可以向GPU发送任意数量的查询,然后定期检查是否有可用的结果,即查询模型是异步的。GPU执行每个查询并将结果放入队列。CPU的队列检查非常快,并且CPU可以继续发送查询或实际的可渲染对象,而不必停止。DirectX和OpenGL都支持断言/条件遮挡查询,其中同时提交查询和对应的draw调用的ID。只有当遮挡查询的几何图形可见时,GPU才会自动处理相应的draw调用。这使得模型更加有用。

通常,应该对最有可能被遮挡的对象执行查询。Koval`e´ık和Sochor[932]收集运行统计查询几帧为每个对象在应用程序运行时。一个物体被发现隐藏的帧数会影响它在未来被测试遮挡的频率。也就是说,可见的对象很可能保持可见,因此可以较少地进行测试。如果可能的话,每个帧都会测试隐藏对象,因为这些对象最有可能从遮挡查询中获益。Mattausch等人[1136]提出了几种不使用断言/条件渲染的遮挡查询(OCs)优化。他们使用批量OCs,将几个OCs组合成一个OC,使用几个边界框而不是一个更大的框,并使用时间抖动采样来调度以前可见的对象。

这里讨论的方案提供了一个flavor的潜力和遮挡剔除方法的问题。何时使用遮挡查询,或者通常使用大多数遮挡方案,通常并不清楚。如果所有东西都是可见的,遮挡算法只会花费额外的时间,永远不会节省时间。其中一个挑战是快速确定算法没有帮助,因此减少了徒劳的节省时间的尝试。另一个问题是决定使用哪些对象集作为遮挡器。位于视锥内的第一个对象必须是可见的,因此在这些对象上花费查询是浪费的。在实现大多数遮挡剔除算法时,决定渲染的顺序和测试遮挡的时间是一件困难的事情。

19.7.2 Hierarchical Z-Buffering(HZB)

分层z缓冲(HZB)[591, 593]对遮挡剔除研究具有重要影响。虽然很少使用原始的CPU端形式,但是该算法是基于GPU硬件的z-culling方法(章节23.7)和使用运行在GPU或CPU上的软件进行自定义遮挡剔除的基础。我们首先介绍了基本算法,然后介绍了该技术在各种呈现引擎中的应用。

该算法将场景模型保持在八叉树中,并将帧的z缓冲作为图像金字塔,我们称之为z金字塔。该算法在图像空间中运行。八叉树支持场景遮挡区域的分层剔除,z-金字塔支持图元的分层z缓冲。因此,z-金字塔是该算法的遮挡表示。这些数据结构的示例如图19.22所示。

z-pyramid的最细(最高分辨率)级别只是一个标准的z-buffer。在所有其他层,每个z值都是相邻较细层对应的2×2窗口中最远的z。因此,每个z值代表屏幕上正方形区域的最远z。无论何时在z缓冲区中覆盖z值,它都会通过z金字塔的较粗级别传播。这是递归地完成的,直到到达图像金字塔的顶部,那里只剩下一个z值。金字塔结构如图19.23所示。

对八叉树节点进行分层剔除,步骤如下。按大致的前后顺序遍历八叉树节点。使用扩展遮挡查询(第19.7.1节)对z-金字塔测试八叉树的包围框。我们开始在包围盒子屏幕投影的最粗的z-金字塔单元格上进行测试。然后将单元格内框的最近深度(znear)与z-pyramid值进行比较,如果znear更远,则已知框被遮挡。这个测试继续递归地沿着z-pyramid向下进行,直到发现盒子被遮挡,或者直到到达z-pyramid的最底层,此时盒子是可见的。对于可见的八叉树框,在八叉树中继续递归地进行测试,最后将可能可见的几何图形呈现到渲染到z-buffer中。这样做是为了后续测试可以使用先前渲染对象的遮挡能力。

完整的HZB算法目前还没有使用,但是它经过了简化,并经过了调整,可以很好地在GPU上使用自定义剔除或在CPU上使用软件栅格化处理计算传递。一般来说,大多数基于HZB的遮挡剔除算法是这样工作的:

  1. 使用一些遮挡器表示生成一个完整的分层z-金字塔。

  2. 要测试对象是否被遮挡,可以将其包围体投射到屏幕空间,并在z-pyramid中估计mip级别。

  3. 根据选定的mip水平测试遮挡。如果结果不明确,可以选择继续使用更细的mip级别进行测试。

大多数实现都不使用八叉树或任何BVH,也不会在渲染对象之后更新zpyramid,因为这被认为执行起来过于昂贵。

第一步可以使用“最佳”遮挡器[1637],它可以被选择为n个对象中最近的一个集合[625],使用简化的艺术家生成的遮挡器图元,或者使用与前一帧可见的对象集合相关的统计数据。或者,可以使用前一帧的z缓冲区[856],但是这并不保守,因为对象有时可能由于不正确的剔除而弹出,特别是在快速相机或对象移动的情况下。Haar和Aaltonen[625]都提供了最好的遮挡器,并将它们与前一帧深度的1/16低分辨率重新投影结合起来。然后使用GPU构建z-pyramid,如图19.23所示。一些人使用AMD GCN架构的HTILE(第23.10.3节)来加速z-pyramid生成[625]。

在第二步中,将对象的包围体投影到屏幕空间。bv的常见选择是spheres、aabb和obb。最长的边,l(像素)的预计BV是用来计算mip水平,λ,(738、1637、1883、1884)

其中n为z-pyramid中mip水平的最大值。max操作符用于避免获得负的mip级别,而min用于避免访问不存在的mip级别。式19.5选取最小整数mip水平,使得投影BV最多覆盖2×2个深度值。这个选择的原因是它使成本可预测——最多需要读取和测试四个深度值。此外,Hill和Collin[738]认为这种测试可以被看作是“概率性的”,因为大对象比小对象更容易被看到,所以在这些情况下没有理由读取更多的深度值。

当达到步骤3时,我们知道,在那个mip级别上,投影的BV最多有一组2×2的深度值。对于给定尺寸的BV,它可能完全落在mip层的一个深度texel内。然而,根据它在网格中的位置,它可能覆盖所有四个texel。精确地或保守地计算了BV的最小深度。对于视图空间中的AABB,这个深度只是框的最小深度,对于OBB,可以将所有顶点投射到视图向量上,并选择最小的距离。对于球体,Shopf等[1637]计算出球体上最近的点为c – rc/||c||,其中c为视图空间中的球体中心,r为球体半径。注意,如果相机位于BV中,那么BV将覆盖整个屏幕,然后渲染对象。将BV的最小深度zmin与分层z缓冲区中(最多)2×2的深度进行比较,如果zmin总是较大,则BV被遮挡。如果没有检测到对象被遮挡,可以在这里停止测试并渲染对象。

你也可以继续测试金字塔中更深(更高分辨率)的层次。我们可以通过使用另一个存储最小深度的z-pyramid来查看是否有必要进行这样的测试。我们用这个新的缓冲区中相应的深度来测试到BV的最大距离zmax。如果zmax小于所有这些深度,那么BV肯定是可见的,可以立即渲染。否则,BV的zmin和zmax重叠了两个z缓冲层的深度,在这种情况下Kaplanyan[856]建议在更高分辨率的mip级别上继续测试。请注意,在分层z-buffer中针对单个深度测试2×2 texel与percentage-closer filtering非常相似(第7.5节)。事实上,测试可以使用bilinear filtering和 percentage-closer filtering来完成,如果测试返回一个正值,那么至少有一个texel是可见的。

Haar和Altonen[625]还提供了一个两个pass的方法,它总是呈现至少所有可见对象。首先,针对前一帧的z-金字塔对所有对象进行遮挡剔除,并渲染“可见”对象。或者,可以使用最后一帧的可见性列表直接渲染z-pyramid。虽然这是一个近似值,但是所有渲染的对象对于当前帧的“最佳”遮挡器都是一个很好的猜测,特别是在具有较高帧与帧一致性的场景中。第二遍获取这些渲染对象的深度缓冲区并创建一个新的z-pyramid。然后,在第一次遍历中被剔除遮挡的对象将进行遮挡测试,如果没有剔除遮挡,将渲染这些对象。这种方法生成一个完全正确的图像,即使相机快速移动或物体在屏幕上快速移动。Kubisch和Tavenrath[944]使用了类似的方法。

Doghramachi和Bucci[363]根据前一帧的深度缓冲区(已向下采样并重新投影)对被遮挡物的定向包围框进行栅格化。它们强制着色器使用early-z(第23.7节),对于每个包围盒,可见片段将对象标记为缓冲区位置中的可见对象,缓冲区位置由对象ID惟一确定[944]。这提供了更高的剔除率,因为使用了面向包围盒,并且完成了逐像素测试,而不是使用公式19.5对mip级别使用自定义测试。

Collin[283]使用一个256×144的浮点z缓冲(非分层结构)对艺术家生成的遮挡器进行栅格化,复杂度较低。这是在软件中完成的,使用CPU或spu(在PLAYSTATION 3上)与高度优化的SIMD代码。为了进行遮挡测试,计算了目标的屏幕空间AABB,并将其zmin与小z缓冲区中所有相关深度进行比较。只有经过剔除的对象才会被发送到GPU。这种方法可以起作用,但在保守性上并不正确,因为使用的分辨率低于最终framebuffer的分辨率。Wihlidal[1883]建议使用低分辨率z-buffer将zmax-值加载到GPU的HiZ(章节23.7)中,例如在AMD GCN上启动HTILE结构。另外,如果使用HZB进行compute-pass剔除,则可以使用软件z-buffer生成z-pyramid。这样,算法就利用了软件中生成的所有信息。

Hasselgren等人[683]提出了一种不同的方法,其中每个8×4块每像素有一个位和两个zmax值[50],导致每像素的总成本为3位。通过使用zmax-values,可以更好地处理深度不连续,因为后台对象可以使用其中一个zmax-values,前台对象可以使用另一个。这种表示称为屏蔽层次深度缓冲区(masked hierarchical depth buffer,MHDB),是保守的,也可以用于zmax-culling。在软件三角形栅格化过程中,每个瓦片只生成覆盖掩模和一个最大深度值,这使得MHDB的栅格化快速高效。在对MDHB进行三角形栅格化时,还可以对MDHB进行三角形遮挡测试,从而优化了栅格化器。MDHB针对每个三角形进行更新,这是其他方法所没有的优势。评估了两种使用模式。第一种方法是使用特殊的遮挡网格,并使用软件光栅化器将这些网格渲染到MDHB。然后,遍历被遮挡物上的AABB树,并根据MDHB进行层次测试。这是非常有效的,尤其是当场景中有许多小对象时。对于第二种方法,整个场景存储在AABB树中,使用堆对场景进行大致的前后顺序遍历。在每个步骤中,对MDHB执行截锥体剔除和遮挡查询。每当渲染对象时,MDHB也会更新。图19.19中的场景就是使用这种方法渲染的。开源代码对AVX2进行了大量优化[683]。

还有一些中间件包专门用于剔除,特别是用于遮挡剔除。Umbra就是这样一个框架,它被广泛地集成到各种游戏引擎中[13,1789]。

19.8 剔除系统 —— Culling Systems

多年来,剔除系统已经有了相当大的发展,并将继续这样发展。在本节中,我们将描述一些重要的概念,并指向相关文献以了解详细信息。一些系统在GPU的计算着色器中有效地执行所有的剔除,而另一些系统则结合了CPU上的粗剔除和GPU上的细剔除。

一个典型的剔除系统可以处理许多粒度,如图19.24所示。对象的集群或只是对象三角形的子集。例如,可以使用64个顶点的三角形带[625]或256个三角形组[1884]。在每个步骤中,都可以使用剔除技术的组合。El Mansouri[415]对对象使用小三角剔除、细节剔除、视锥剔除和遮挡剔除。由于集群在几何上比对象小,所以即使对于集群使用相同的剔除技术也是有意义的,因为它们更有可能被剔除。例如,可以使用细节、截锥体、聚集的背面和聚集上的遮挡剔除。

在每个集群级别上进行了筛选之后,可以执行另一个步骤,在每个三角形级别上进行筛选。要完全在GPU上实现这一点,可以使用图19.25中所示的方法。三角形的剔除技术包括除w后的截锥体剔除,即,将三角形的范围与±1进行比较,进行背面检测,退化三角形剔除,小三角形剔除,也可能进行遮挡剔除。在所有筛选测试之后剩下的三角形被压缩成一个最小列表,这样做是为了在下一步中只处理幸存的三角形[1884]。这个想法是指导剔除计算着色器发送一个绘图命令从GPU到自己在这一步。这是使用间接绘制命令完成的。这些调用在OpenGL中称为“multi-draw indirect”,在DirectX中称为“execute indirect”[433]。将三角形数量写入GPU缓冲区中的一个位置,该位置与压缩列表一起可被GPU用来渲染三角形列表。

有许多方法可以将剔除算法与它们的执行位置结合在一起,即无论是在CPU上还是GPU上,每种剔除算法都有很多不同的风格。最完美的组合还没有找到,但是可以肯定地说,最好的方法取决于目标体系结构和要渲染的内容。接下来,我们指出了CPU/GPU剔除系统领域的一些重要工作,这些工作对该领域产生了重大影响。Shopf等[1637]对GPU上的字符进行了所有的AI模拟,因此,每个字符的位置只能在GPU内存中使用。这使得他们开始探索使用计算着色器进行筛选和LOD管理,随后的大多数系统都受到了他们工作的很大影响。Haar和Aaltonen[625]描述了他们为刺客信条Unity开发的系统。Wihlidal[1883, 1884]解释了在寒霜引擎中使用的剔除系统。Engel[433]提出了一种用于剔除的系统,该系统使用可见性缓冲区帮助改进管道(第20.5节)。Kubisch和Tavenrath[944]描述了使用不同的剔除方法和API调用来渲染包含大量部件的大型模型并进行优化的方法。他们使用的一个值得注意的方法是使用几何体着色器创建一个边界框的可见边,然后让early-z快速剔除遮挡几何体。

19.9 Level of Detail(LOD)

细节级别(LODs)的基本思想是使用对象的简单版本,因为它对渲染的图像的贡献越来越小。例如,考虑一辆由一百万个三角形组成的汽车。当观察者靠近汽车时,可以使用此表示。当对象距离较远时,比如只覆盖200个像素,我们不需要所有的100万个三角形。相反,我们可以使用一个只有1000个三角形的简化模型。由于距离的原因,简化版本看起来与更详细的版本大致相同。参见图19.26。通过这种方式,可以预期显著的性能提升。为了减少应用LOD技术所涉及的总工作量,最好在剔除技术之后应用LOD技术。例如,LOD选择只计算视锥中的对象。

还可以使用LOD技术使应用程序在具有不同性能的一系列设备上以所需的帧速率工作。对于速度较低的系统,可以使用不太详细的lod来提高性能。注意,虽然LOD技术首先帮助减少顶点处理,但它们也降低了像素着色的成本。这是因为一个模型的所有三角形边缘长度的和将会更低,这意味着四分过渲染(光栅化阶段)被减少(章节18.2和23.1)

第14章中描述的Fog和其他的效果可以与LODs一起使用。例如,当对象进入完全不透明的雾中时,这允许我们完全跳过对象的呈现。此外,雾滴机制可以用来实现 time-critical rendering(第19.9.3节)。通过将远平面移动到离观察者更近的地方,可以在早期选择更多的对象,从而提高帧率。此外,较低的LOD通常可以在雾中使用。

一些物体,如球体、B ‘ ezier曲面和细分曲面,其几何描述的一部分是细节层次。底层几何图形是弯曲的,一个单独的LOD控件决定如何将其细分为可显示的三角形。有关参数曲面和细分曲面的自适应细分质量的算法,请参阅第17.6.2节。

一般来说,LOD算法由三个主要部分组成,即生成、选择和切换。LOD生成是用不同数量的细节生成模型的不同表示的部分。第16.5节中讨论的简化方法可用于生成所需的lod数量。另一种方法是用不同数量的三角形手工制作模型。选择机制根据一些标准(如屏幕上的估计面积)选择一个级别的详细模型。最后,我们需要从一个细节级别更改到另一个细节级别,这个过程称为LOD切换。本节介绍了不同的LOD切换和选择机制。

虽然本节的重点是在不同的几何表示之间进行选择,但是LODs背后的思想也可以应用到模型的其他方面,甚至使用的渲染方法。较低层次的细节模型也可以使用较低分辨率的纹理,从而进一步节省内存,并可能改善缓存访问[240]。着色器本身可以根据距离、重要性或其他因素进行简化[688、1318、1365、1842]。Kajiya[845]提出了一个尺度层次结构,展示了表面光照模型如何与纹理映射方法重叠,而纹理映射方法又与几何细节重叠。另一种技术是可以用更少的骨骼为远处的物体蒙皮。

当静态对象相对较远时,billboards和impostors (第13.6.4节)是一种自然的方式,以很少的成本来表示它们[1097]。其他的表面渲染方法,比如凹凸贴图,可以用来简化模型的表示。图19.27给出了一个示例。Teixeira[1754]讨论了如何使用GPU将法线贴图烘焙到表面上。这种简化技术最明显的缺陷是轮廓失去了它们的曲率。Loviscach [1085]提出了一种沿着轮廓边缘挤压鳍片的方法,以创建弯曲的轮廓。

可以用来表示对象的技术范围的一个例子来自Lengyel等人[1030,1031]。在这项研究中,毛皮在非常近的地方用几何图形表示,在较远的地方用alpha混合折线表示,然后用带有体纹理“外壳”的混合线表示,最后在较远的地方用纹理图表示。参见图19.28。知道何时以及如何最好地从一组建模和渲染技术切换到另一组,从而最大限度地提高帧率和质量,仍然是一门艺术,也是一个有待探索的开放领域。

19.9.1 LOD切换 —— LOD Switching

当从一个LOD切换到另一个LOD时,一个突然的模型替换常常会引起注意和干扰。这种差异称为弹出(popping)。这里将描述几种执行这种切换的不同方法,它们都具有不同的弹出特性。

离散几何LOD——Discrete Geometry LODs

在最简单的LOD算法中,不同的表示形式是同一对象的模型,其中包含不同数量的图元。该算法非常适合现代图形硬件[1092],因为这些独立的静态网格可以存储在GPU内存中并重用(第16.4.5节)。更详细的LOD具有更多的图元。图19.26和图19.29显示了三个LODs的对象。第一个图还显示了不同距离的LODs。从一个LOD切换到另一个LOD是突然的。也就是说,在当前帧上使用某个LOD,然后在下一帧上,选择机制选择另一个LOD并立即使用它进行渲染。对于这种类型的LOD方法,弹出通常是最糟糕的,但是如果切换发生在距离较远的地方,而呈现的LOD中的差异几乎不可见,那么它可以很好地工作。下面描述更好的替代方案。

混合LOD——Blend LODs

从概念上讲,切换的一个简单方法是在短时间内在两个lod之间进行线性混合。这样做肯定会使切换更加顺畅。为一个对象呈现两个LOD自然比只呈现一个LOD更昂贵,因此这在某种程度上违背了LOD的目的。然而,LOD切换通常只发生在很短的时间内,而且通常不是对场景中的所有对象同时进行切换,因此质量改进可能是值得的。

假设需要在两个LOD(比如LOD1和lod2)之间进行转换,并且LOD1是当前呈现的LOD。问题在于如何以合理的方式渲染和混合这两个lod。将两个LODs都设置为半透明将导致在屏幕上呈现一个半透明的对象(尽管有些不透明),这看起来很奇怪。

Giegl和Wimmer[528]提出了一种在实践中工作良好且易于实现的混合方法。首先不透明地将LOD1绘制到framebuffer(包括color和z),然后通过将其alpha值从0增加到1并使用“over”混合模式淡入LOD2。当LOD2已经淡入,因此它是完全不透明的,它将转换为当前LOD,然后LOD1将淡出。正在淡出(in或out)的LOD应该呈现为启用z-test和禁用z-write。为了避免稍后绘制的遥远对象会覆盖渲染已淡出LOD的结果,只需在所有不透明内容之后按排序顺序绘制所有已褪色的LOD,透明对象通常是这样做的。注意,在转换的中间,两个lod都是不透明渲染的,一个在另一个之上。如果转换间隔保持较短,这种技术的效果最好,这也有助于保持渲染开销较小。Mittring[1227]讨论了一种类似的方法,除了在版本之间使用screen-door透明(可能是亚像素级)来溶解。

Scherzer和Wimmer[1557]只更新每帧上的一个LOD,重用前一帧上的另一个LOD,从而避免渲染两个LOD。前一帧的反投影与使用可见纹理的组合遍历一起执行。更快的渲染和更好的转换是主要的结果。

有些对象可以使用其他交换技术。例如,SpeedTree package[887]平滑地移动或缩放了树LOD模型的一部分,以避免pop。一个例子见图19.30。图19.31显示了一组LOD,以及用于远处树木的广告牌LOD技术。

透明LOD——Alpha LODs

一个避免弹出的简单方法是使用我们所称的alpha LODs。该技术可以单独使用,也可以与其他LOD切换技术结合使用。它用于最简单的可见LOD,如果只有一个LOD可用,那么它可以是原始模型。指标用于LOD的选择(例如,距离这个对象)的增加,整体对象的透明度增加(减少α),和对象最后消失在它到达全透明(α= 0.0)。当度量值大于用户定义的隐形阈值时,就会发生这种情况。当达到隐形阈值时,只要度量值保持在阈值之上,就不需要通过呈现管道发送对象。当一个物体是不可见的,并且它的度量值低于不可见阈值,那么它就会降低它的透明度,并重新开始可见。另一种方法是使用19.9.2节中描述的滞后方法。

独立使用该技术的优点是,它的经验比离散几何LOD方法连续得多,因此避免了弹出。此外,由于对象最终完全消失,不需要渲染,因此可以预期会有显著的加速。缺点是对象完全消失,只有在这一点上才能获得性能的提高。图19.32显示了alpha LODs的示例。

使用alpha透明度的一个问题是,需要根据深度进行排序,以确保透明对象正确混合。为了淡化远处的植被,Whatley[1876]讨论了如何使用噪声纹理来实现screen-door的透明度。这有一种溶解的效果,随着距离的增加,物体上的更多纹理消失。虽然质量不如真正的alpha淡入效果好,但纱门的透明度意味着不需要排序或混合。

CLODs和Geomorph LODs —— CLODs and Geomorph LODs

网格简化过程可用于从单个复杂对象创建各种LOD模型。执行这种简化的算法将在第16.5.1节中讨论。一种方法是创建一组离散lod,并像前面讨论的那样使用它们。然而,边缘折叠方法(edge collapse methods)具有允许在lod之间进行转换的其他方法的属性。在这里,我们提出了两种利用这些信息的方法。这些都是有用的背景,但目前很少在实践中使用。

在每个边折叠操作完成后,一个模型有两个更少的三角形。在边折叠中,边会收缩,直到两个端点相交,然后消失。如果这个过程是动画的,那么在原始模型和稍微简化的版本之间就会发生平滑的转换。对于每条边的折叠,一个顶点与另一个顶点连接。在一系列的边折叠之后,一组顶点移动来连接其他顶点。通过存储一系列边缘折叠,可以逆转这一过程,从而使简化的模型随着时间变得更加复杂。边缘折叠的反转称为顶点分裂。因此,改变对象细节级别的一种方法是精确地根据LOD选择值确定可见三角形的数量。在100米之外,模型可能由1000个三角形组成,移动到101米,它可能会下降到998个三角形。这种方案称为连续细节级别(continuous level of detail ,CLOD)技术。因此,并不是一组离散的模型,而是一组可供显示的巨大模型,每个模型的三角形都比其更复杂的邻居少两个。

在顶点分裂中,一个顶点变成两个。这意味着一个复杂模型上的每个顶点都来自于一个简单模型上的某个顶点。Geomorph LODs[768]是一组通过简化创建的离散模型,保持了顶点之间的连接性。当从一个复杂的模型切换到一个简单的模型时,复杂模型的顶点在它们的原始位置和简单模型的顶点之间进行插值。当转换完成时,使用更简单的细节模型级别来表示对象。有关转换的示例,请参见图19.33。Geomorph 有几个优点。单个的静态模型可以预先选择,具有较高的质量,并且可以很容易地转换成三角形网格。像CLOD一样,平滑的过渡也可以避免弹出。主要缺点是每个顶点都需要插值;CLOD技术通常不使用插值,因此顶点位置集本身不会改变。另一个缺点是,对象总是在变化,这可能会分散注意力。对于有纹理的对象尤其如此。

gpu支持一种称为分数细分的相关思想。在这种方案中,曲面的细分因子可以设置为任意的浮点数,从而避免了凸点的产生。例如,分数镶嵌可以用于B ‘ ezier patch和位移映射原语。有关这些技术的更多信息,请参见第17.6.1节。

19.9.2 LOD选择——LOD Selection

考虑到对象存在不同层次的细节,必须选择渲染哪个层次的细节,或者混合哪个层次的细节。这是LOD选择的任务,这里将介绍几种不同的技术。这些技术也可用于为遮挡剔除算法选择良好的遮挡器。

通常,一个度量,也称为效益函数,会根据当前的观点和对象的位置进行评估,这个度量的值选择一个适当的LOD。这个度量可以基于,例如,对象的包围体的投影面积或者从视点到对象的距离。效益函数的值在这里记作r。关于如何快速估计直线在屏幕上的投影,请参见第17.6.2节。

基于范围——Range-Based

选择LOD的一种常见方法是将对象的不同LOD与不同的距离范围关联起来。最详细的LOD的范围是从0到某个用户定义的值r1,这意味着当到对象的距离小于r1时,这个LOD是可见的。下一个LOD的范围是从r1到r2,其中r2是> r1。如果到对象的距离大于或等于r1,小于r2,则使用这个LOD,以此类推。图19.34给出了四种不同LOD及其范围的示例,以及场景图中使用的相应LOD节点。

如果用于确定要使用哪个LOD的度量值在围绕某个值ri的帧与帧之间变化,则可能发生不必要的弹出。在水平之间可以发生快速的来回循环。这可以通过在ri值附近引入一些滞后来解决[898,1508]。图19.35显示了基于范围的LOD,但适用于任何类型。这里,LOD范围的上一行只在r增加时使用。当r减小时,使用范围的底一行。

在转换范围内混合两个lod如图19.36所示。但是,这并不理想,因为到对象的距离可能会在转换范围内停留很长时间,这会由于混合了两个lod而增加呈现负担。相反,当对象达到一定的转换范围时,Mittring[1227]在有限的时间内执行LOD切换。为了得到最好的结果,这应该与上面的滞后方法相结合。

基于投影区域——Projected Area-Based

LOD选择的另一个常见度量是包围体的投影面积,或者对它的估计。在这里,我们将展示该区域的像素数量,称为屏幕空间覆盖率,如何通过透视来估计球体和盒子的像素数量。

从球体开始,估计是基于这样一个事实,即一个物体的投影大小随着距离观察者的距离沿视图方向减小。如图19.37所示,它说明了如果与查看器的距离增加一倍,投影的大小将减少一半,这对于面向观察者的平面对象是适用的。我们用圆心c和半径r来定义一个球体。观察者位于v处,沿着归一化方向向量d观察。从c到v沿着视图方向的距离就是球体中心在视图向量d上的投影:d·(v – c)。我们还假设观察者到视锥台的近平面的距离为n。近平面用于估计,使得位于近平面上的对象返回其原始大小。然后对投影球的半径进行估计

投影在像素的面积因此是πp2wh,其中w×h是屏幕分辨率。较高的值选择更详细的LOD。这是一个近似。实际上,三维球体的投影是一个椭圆,如Mara和McGuire[1122]所示。他们还推导出一种计算保守边界多边形的方法,即使在球与近平面相交的情况下也是如此。

通常的做法是简单地在对象的边界框周围使用一个边界球。另一个估计是使用包围盒的屏幕边界。然而,在实际覆盖的投影面积上,薄的或平的物体可能会有很大的不同。例如,想象一根意大利面,一端在屏幕的左上角,另一端在屏幕的右下角。它的边界球将覆盖屏幕,它的边界框的最小和最大二维屏幕边界也将覆盖屏幕。

Schmalstieg和Tobler[1569]提出了一种计算盒子投影面积的快速程序。这个想法是对相机相对于盒子的视点进行分类,并使用这个分类来确定哪些投影顶点包含在投影盒子的轮廓中。这个过程是通过一个查询表完成的。使用这些顶点,可以计算视图中的面积。分类主要分为三种情况,如图19.38所示。实际上,这种分类是通过确定视点位于边界框平面的哪一侧来完成的。为了提高效率,将视点转换为盒子的坐标系,分类时只需要比较。比较的结果放入位掩码中,位掩码用作LUT的索引。这个LUT决定了从视图中看到的剪影中有多少个顶点。然后,使用另一个查找来实际查找轮廓顶点。当它们被投影到屏幕上后,就会计算出轮廓的面积。为了避免(有时严重的)估计错误,有必要将形成的多边形裁剪到视图截锥体的两侧。源代码可以在web上找到。冷耶尔[1026]对该方案进行了优化,可以使用更紧凑的LUT。

将LOD选择仅基于范围或投影并不总是一个好主意。例如,如果一个对象有一个带有一些大三角形和一些小三角形的AABB,那么这些小三角形可能会产生严重的混叠,并由于四分体阴影而降低性能。如果另一个对象具有完全相同的AABB,但是其中包含中三角形和大三角形,那么基于范围和基于项目的选择方法都将选择相同的LOD。为了避免这种情况,Schulz和Mader[1590]使用几何平均值g来帮助选择LOD:

其中ti为物体三角形的大小。使用几何平均而不是算术平均的原因是,即使有几个大三角形,许多小三角形也会使g更小。对于最高分辨率的模型,这个值是离线计算的,并用于预先计算第一个开关应该发生的距离。随后的开关距离是第一个距离的简单函数。这使得他们的系统可以更频繁地使用较低的LODs,从而提高了性能。

另一种方法是计算每个离散LOD的几何误差,即,估计简化模型与原始模型最多偏离多少米。然后可以投射这个距离,以确定在屏幕空间中使用LOD的效果。然后选择同样满足用户定义的屏幕空间错误的最低LOD。

其他选择方法——Other Selection Methods

基于范围和基于投影区域的LOD选择通常是最常用的度量标准。然而,还有许多其他的方法是可能的,我们将在这里提到一些。除了投影面积,Funkhouser和S’equin[508]还建议使用物体的重要性(例如,墙壁比墙上的时钟更重要)、运动、滞后(切换LODs时,效益降低)和聚焦。最后一点,观众的注意力,可能是一个重要的因素。例如,在体育游戏中,控制球的图形是用户最关注的地方,因此其他字符的细节级别相对较低[898]。类似地,当在虚拟现实应用程序中使用眼球跟踪时,应该在查看者看到的地方使用更高的LODs。

根据应用程序的不同,其他策略可能是有效的。可以使用全局可见性,例如,可以使用较低的LOD渲染通过浓密叶子看到的附近对象。更多的全局度量是可能的,比如为了保持在给定的三角形预算之内而限制使用的非常详细的lod的总数[898]。有关此主题的更多信息,请参见下一节。其他因素还有可视性、颜色和纹理。感知度量也可以用来选择LOD[1468]。

McAuley[1154]提出了一个植被系统,在树干和叶簇成为impostor之前,它们有三个LODs。他从不同的角度和不同的距离对每个对象的集群之间的可见性进行预处理。由于树后面的集群可能被较近的集群完全隐藏,因此即使树离得很近,也可以为此类集群选择较低的LODs。对于草地渲染,通常使用离观者较近的几何体,较远的广告牌,以及距离较远的简单地面纹理[1352]。

19.9.3 时序严格的LOD渲染——Time-Critical LOD Rendering

对于渲染系统来说,拥有一个恒定的帧速率通常是一个可取的特性。事实上,这通常被称为“硬实时”或时间关键的渲染。这样的系统有特定的时间,比如说16毫秒,并且必须在这段时间内完成它的任务(例如,渲染图像)。当时间到了,系统不得不停止处理。如果场景中的对象是由LODs表示的,那么一个硬实时渲染系统将能够向用户显示更多或所有场景,而不是在分配的时间内只绘制几个非常详细的模型。

Funkhouser和S’equin[508]提出了一种启发式算法,该算法对场景中所有可见对象的细节级别进行选择,以满足帧率恒定的要求。这种算法是预测性的,因为它根据所需的帧速率和对象的可见性来选择可见对象的LOD。这种算法与反应性算法(reactive algorithm)形成对比,反应性算法根据渲染前一帧所花费的时间进行选择。

对象称为O,并在称为L的详细级别上渲染,该级别为对象的每个LOD提供(O,L)。然后定义两个启发式。一种启发式方法估计在特定细节级别渲染对象的成本:Cost(O,L)。另一个评估对象在特定细节级别渲染的好处:Benefit(O,L)。效益函数估计在某一LOD处对对象图像的贡献。

假设视图截锥体内的对象或与视图截锥体相交的对象称为S,那么算法的主要思想是使用启发式选择的函数来优化对象S的lod的选择。具体来说,我们想要最大化

约束条件下

其中T为目标帧时间。

换句话说,我们想要选择对象的细节级别,以便在期望的帧率内为我们提供“最佳图像”。接下来,我们描述了如何估计成本和效益函数,然后我们提出了上述方程的优化算法。

成本函数和收益函数都很难定义,因此它们在任何情况下都是有效的。成本函数可以通过使用不同的查看参数对LOD的渲染进行多次计时来估计。不同的受益函数见第19.9.2节。在实践中,对象的BV的投影面积可以作为一个效益函数。

最后,我们将讨论如何为场景中的对象选择细节级别。首先,我们注意到以下几点:对于某些视点,场景可能过于复杂,无法跟上所需的帧率。为了解决这个问题,我们可以为每个对象在其最低的细节级别定义一个LOD,它只是一个没有图元的对象,即,我们避免渲染对象[508]。使用这个技巧,我们只渲染最重要的对象,跳过不重要的对象。

为了选择场景的“最佳”LODs,需要在如式19.9所示的约束条件下对式19.8进行优化。这是一个np完全问题,这意味着要正确地解决它,唯一要做的就是测试所有不同的组合并选择最佳组合。这对于任何一种算法都是不可行的。一个更简单、更可行的方法是使用贪心算法,该算法试图最大化每个对象的值=Benefit(O,L)/Cost(O,L)。该算法处理视图截锥内的所有对象,并选择按降序渲染对象,即,第一个是值最大的。如果一个对象对于多个LOD具有相同的值,则选择并渲染效益最高的LOD。这种方法最“合算”。对于视图截锥内的n个对象,该算法在O(n log n)时间内运行,它产生的解至少是最佳解的一半[507,508]。还可以利用帧与帧之间的一致性来加快值的排序。

关于LOD管理以及LOD管理与门户剔除相结合的更多信息可以在Funkhouser博士论文[507]中找到。Maciel和Shirley[1097]将LODs和impostor结合起来,提出了一种用于渲染户外场景的近似常量时间算法。一般的思想是使用对象的不同表示形式(例如,一组lod和分层impostor)的层次结构。然后以某种方式遍历树,给出给定时间内的最佳图像。Mason和Blake[1134]提出了一种增量分层LOD选择算法。同样,对象的不同表示可以是任意的。埃里克森等人[441]提出了细节的层次结构层次(hierarchical levels of detail, HLODs)。使用这些方法,场景也可以以恒定的帧速率渲染,或者渲染的错误是有限的。与此相关的是功耗预算渲染。Wang等[1843]提出了一种优化框架,该框架可以选择好的参数来降低功耗,这对于手机和平板电脑来说非常重要。

与时间严格渲染相关的是另一组应用于静态模型的技术。当相机不移动时,渲染整个模型,积累缓冲可以用于抗混叠,景深,柔和的阴影,一个渐进的更新。然而,当相机移动时,可以降低所有对象的细节级别,并使用细节剔除来完全剔除小对象,以满足一定的帧率。

19.10 大场景渲染——Rendering Large Scenes

到目前为止,已经暗示要渲染的场景适合于计算机的主内存。情况可能并非总是如此。例如,一些主机只有8gb的内部内存,而一些游戏世界可以由数百GB的数据组成。因此,我们提出了纹理的流化和转换方法,一些通用的流化技术,最后给出了地形渲染算法。请注意,这些方法几乎总是与本章前面描述的剔除技术和LOD相结合。

19.10.1 虚拟贴图与流—— Virtual Texturing and Streaming

假设你想用一个分辨率非常高的纹理来渲染一个巨大的地形数据集,而这个纹理太大了,无法存入GPU内存。例如,《RAGE》中的一些虚拟纹理分辨率为128k×128k,这将消耗64 GB GPU内存[1309]。当CPU上的内存有限时,操作系统使用虚拟内存进行内存管理,根据需要将数据从驱动器交换到CPU内存[715]。稀疏纹理[109,248]提供了这种功能,使分配一个巨大的虚拟纹理成为可能,也称为megatexture。这些技术有时被称为虚拟纹理(virtual texturing)或部分常驻纹理(partially resident texturing)。应用程序决定每个mipmap级别的哪些区域(tile)应该驻留在GPU内存中。tile通常是64 kB,它的纹理分辨率取决于纹理格式。在这里,我们介绍了虚拟纹理(virtual texturing)和流媒体技术(streaming techniques)。

关于使用mipmapping的高效纹理系统的关键特点是,理想情况下,所需的纹理数量应该与最终渲染图像的分辨率成正比,而不依赖于纹理本身的分辨率。因此,我们只需要可见的texel位于物理GPU内存中,与整个游戏世界中的所有texel相比,这是一个非常有限的集合。主要概念如图19.39所示,其中整个mipmap链在虚拟内存和物理内存中都被划分为块。这些结构有时被称为虚拟mipmap或clipmaps[1739],后者指的是较大的mipmap中被剪切出来使用的一小部分。由于物理内存的大小要比虚拟内存小得多,所以只有一小组虚拟纹理块可以装入物理内存。几何图形在虚拟纹理中使用全局uv参数化,在像素着色器中使用这样的uv坐标之前,需要将它们转换为纹理坐标,将该点转换为物理纹理内存。这可以使用GPU支持的页表(GPU-supported page table )(如图19.39所示)或间接纹理(indirection texture)来完成,如果是在GPU上的软件中完成的。任天堂GameCube中的GPU支持虚拟纹理。最近,PLAYSTATION 4、Xbox One和许多其他gpu都支持硬件虚拟纹理。在映射和未映射到物理内存时,需要使用正确的偏移量更新间接纹理。使用一个巨大的虚拟纹理和一个小的物理纹理效果很好,因为远处的几何体只需要将一些高级的mipmap贴图加载到物理内存中,而靠近摄像机的几何体可以加载一些低级的mipmap贴图。注意,虚拟纹理可以用于从磁盘上传输巨大的纹理,也可以用于稀疏阴影映射(sparse shadow mapping)[241]。

由于物理内存有限,所有使用虚拟纹理的引擎都需要一种方法来确定哪些块应该驻留,即,位于物理内存中,而哪一块不应该。有几种这样的方法。Sugden和Iwanicki[1721]使用反馈渲染方法,其中第一次渲染传递写出所有需要的信息,以知道一个片段将访问哪个纹理平铺。当该传递完成后,纹理将被读入CPU并进行分析,以确定需要哪些块。读取非驻留的块并将其映射到物理内存,而不需要映射的块则去除映射从物理内存中。他们的方法不适用于阴影、反射和透明度。但是,screen-door技术(第5.5节)可以用于透明效果,效果相当好。反馈渲染也被van Waveren和Hart[1855]使用。注意,这种传递可以是单独的渲染pass,也可以与z-prepass结合使用。当使用单独的通道时,可以使用分辨率仅为80×60像素的近似方法来减少处理时间。Hollemeersch等[761]使用计算pass而不是将feedback buffer读入CPU。结果是在GPU上创建了一个紧凑的tile标识符列表,并将其发送回CPU进行映射。

使用gpu支持的虚拟纹理,驱动程序有责任创建和销毁资源,映射和取消映射块,并确保物理分配得到虚拟分配的支持[1605]。使用gpu -硬件虚拟纹理,sparseTexture查找返回一个代码,除了过滤的值(用于常驻tile)之外,该代码还指示对应的tile是否是常驻的[1605]。使用软件支持的虚拟纹理,所有这些任务都落在开发人员的肩上。关于这个主题的更多信息,我们参考van Waveren的报告[1856]。

为了确保所有东西都能放入物理内存,van Waveren调整了全局纹理LOD偏移,直到工作集符合[1854]。此外,当只有比期望更高级别的mipmap tile可用时,需要使用更高级别的mipmap tile,直到较低级别的mipmap tile可用为止。在这种情况下,可以立即升级更高级别的mipmap tile并使用它,然后随着时间的推移可以混合新的tile,以便在可用时实现平滑的转换。

相反,Barb[99]总是加载所有小于或等于64 kB的纹理,因此,总是可以进行一些纹理处理,尽管如果还没有加载分辨率更高的mipmap级别,其质量会更低。他使用离线反馈渲染来预计算不同位置的玩家在不同材质和屏幕分辨率下,每个mipmap关卡所覆盖的立体角。在运行时,这些信息被流进来,并根据每种材质的分辨率和最终屏幕分辨率进行调整。这将为每个纹理、每个mipmap生成一个重要值。然后,每个重要值除以对应的mipmap级别中的纹理数量,这将生成一个合理的最终度量,因为即使将纹理细分为更小的、相同映射的纹理,它也是不变的。更多信息请参见Barb的演示文稿[99]。图19.40显示了一个示例渲染。

Widmark[1881]描述了如何将流媒体与过程纹理生成相结合,以获得更多样和更详细的纹理。Chen扩展了Widmark的方案来处理比这大一个数量级的纹理[259]。

19.10.2 贴图转码 Texture Transcoding

为了使虚拟纹理系统更好地工作,可以将其与转码相结合。这是一个从磁盘读取用可变速率压缩方案(如JPEG)压缩的图像的过程,对其解码,然后使用gpu支持的纹理压缩方案之一对其编码(第6.2.6节)。图19.41显示了其中一个这样的系统。反馈渲染遍历的目的是确定当前帧需要哪些块,这里可以使用第19.10.1节中描述的任何一种方法。获取步骤通过存储层次结构获取数据,从光存储或硬盘驱动器(HDD),通过可选的磁盘缓存,然后通过软件管理的内存缓存。Unmapping指的是释放一个驻留tile。读取新数据后,对其进行转换编码,并最终将其映射到新的驻留块。

使用转码的优点是,当纹理数据存储在磁盘上时可以使用更高的压缩比,当通过纹理采样器访问纹理数据时使用gpu支持的纹理压缩格式。这需要可变速率压缩格式的快速解压缩和对gpu支持格式的快速压缩[1851]。还可以压缩已经压缩的纹理,进一步减小文件大小[1717]。这种方法的优点是,当纹理从磁盘读取并解压时,它已经是纹理压缩格式,可以被GPU使用。具有免费源代码的crunch库[523]使用了类似的方法,达到了1-2位/ texel的结果。有关示例,请参见图19.42。它的后继者称为基(basis),是一种专有格式,具有块的可变位压缩(variable-bit compression for blocks),可以快速转码为纹理压缩格式[792]。GPU上的快速压缩方法可用于BC1/BC4[1376]、BC6H/BC7[933、935、1259]和PVRTC[934]。Sugden和Iwanicki[1721]对磁盘上的可变速率压缩方案使用Malvar的压缩方案[1113]的变体。对于法线,它们达到了40:1的压缩比,对于albedo纹理,使用YCoCg变换达到了60:1的压缩比。Khronos正在为纹理开发一种标准的通用压缩文件格式。

当需要高纹理质量和较小的纹理加载时间时,Olano等[1321]使用可变速率压缩算法将压缩后的纹理存储在磁盘上。纹理也被压缩到GPU内存中,直到需要的时候,GPU使用自己的算法对它们进行解压,然后再以未压缩的形式使用它们。

19.10.3 通用流 —— General Streaming

例如,在模型大于物理内存的游戏或其他实时渲染应用程序中,还需要一个流系统来处理实际的几何图形、脚本、粒子和AIs。平面可用三角形、正方形或六边形的规则凸多边形平铺。因此,这些也是流系统的常见构建块,其中每个多边形都与该多边形中的所有资产相关联。如图19.43所示。应该注意的是,正方形和六边形是最常用的[134,1522],可能是因为它们的近邻比三角形少。观察者位于图19.43中的深蓝色多边形中,流系统确保将近邻(浅蓝色和绿色)加载到内存中。这是为了确保周围的几何图形可用来渲染,并确保当查看器移动到邻近的多边形时数据在那里。注意,三角形和正方形有两种类型的邻居:一种共享一条边,另一种只共享一个顶点。

Ruskin[1522]使用六边形,每个六边形都有一个低分辨率和高分辨率的几何LOD。由于低分辨率lod占用的内存很小,所以全世界的低分辨率lod都是随时加载的。因此,只有高分辨率的lod和纹理被流进和流出内存。Bentley[134]使用正方形,每个正方形的面积为100×100平方米。高分辨率的mipmaps与其他资产是分开流的。该系统使用1-3个LODs进行近距离到中距离的查看,然后使用烘焙的impostor进行远距离的查看。在一款赛车游戏中,Tector[1753]会随着赛车的前进而沿着赛道加载数据。他将使用zip格式压缩的数据存储在磁盘上,并将块加载到压缩的软件缓存中。然后根据需要解压缩这些块,并由CPU和GPU的内存层次结构使用。

在某些应用中,可能需要平铺三维空间,而不只是使用上面描述的二维平铺。注意,立方体是唯一一个也平铺三维空间的规则多面体,因此它是此类应用程序的自然选择。

19.10.4 地形渲染 —— Terrain Rendering

地形渲染是许多游戏和应用程序的重要组成部分,例如谷歌Earth和用于大型世界渲染的Cesium开源引擎[299,300]。图19.44显示了一个示例。我们描述了几种在当前gpu上运行良好的流行方法。应该注意的是,这些都可以添加分形噪声,以便在地形上缩放时提供高水平的细节。此外,当游戏或关卡加载时,许多系统都会动态生成地形。

其中一种方法是几何体剪贴图[1078]。它类似于纹理剪贴图(texture clipmaps)[1739],因为它使用了与mipmapping相关的层次结构,即,几何被过滤成一个金字塔的粗糙和粗糙的水平向顶部。如图19.45所示。在绘制巨大的地形数据集时,只有n×n个样本,即、height,则缓存到内存中,以便查看器周围的每个级别。当观察者移动时,图19.45中的窗口也随之移动,新数据被加载,旧数据可能被删除。为了避免层与层之间产生裂缝,每隔两层就有一个过渡区。在这样的过渡层中,几何和纹理都被平滑地插入到下一个更粗的层中。这是实现在顶点和像素着色器的。Asirvatham和Hoppe[82]提出了一种高效的GPU实现,其中地形数据存储为顶点纹理。顶点着色器访问这些,以获得地形的高度。法线贴图可以用来增强地形上的视觉细节,当近距离放大时,Losasso和Hoppe[1078]也可以添加分形噪声位移来获得更多细节。有关示例,请参见图19.46。Gollent在Witcher 3中使用了一种几何剪贴图的变体[555]。Pangerl[1348]和Torchelsen等[1777]给出了几何剪贴图(geometry clipmaps)的相关方法,这些方法也非常适合GPU的能力。

有几种方案专注于创建tile并呈现它们。一种方法是将heightfield数组分成若干块,每个块有17×17个顶点。对于一个非常详细的视图,可以呈现一个单独的tile,而不是向GPU发送单独的三角形或小扇形。一个平铺可以有多个层次的细节。例如,在每个方向上只使用其他每个顶点,就可以形成一个9×9的平铺。利用每四个顶点得到一个5×5的平铺,每八个顶点得到一个2×2的平铺,最后四个角得到两个三角形的1×1的平铺。注意,原来的17×17顶点缓冲区可以存储在GPU上重用;只需要提供一个不同的索引缓冲区来更改渲染的三角形的数量。下面介绍一种使用这种数据布局的方法。

在GPU上快速渲染大地形的另一种方法叫做分块LOD[1797]。其思想是使用n个离散的细节级别来表示地形,其中每个较细的LOD相对于其父LOD被分割为4×,如图19.47所示。然后,该结构被编码到一个四叉树中,并从根节点遍历以进行渲染。当访问一个节点,它将渲染屏幕空间的错误(这是描述下)如果低于某些pixel-error阈值,τ。否则,将递归地访问这四个子元素。这可以在需要的地方获得更好的分辨率,例如,靠近观察者。在一个更高级的变体中,地形块根据需要从磁盘装载[1605,1797]。遍历类似于上面描述的方法,只是子元素只有在已经从磁盘加载到内存中时才会递归访问。如果没有加载它们,它们将排队等待加载,并渲染当前节点。

Ulrich[1797]计算屏幕空间错误为

w是屏幕的宽度,d是相机到地面tile的距离θ是弧度的水平视野,ǫ是几何误差在同一单位作为几何误差项,d。经常使用两个网格之间的豪斯多夫距离(906、1605)。对于原始网格上的每个点,在简化网格上找到其最近的点,并将这些距离中最小的点称为d1。现在对简化网格上的每个点执行相同的步骤,在原始网格上找到最近的点,并调用距离d2中最小的点。Hausdorff 距离ǫ= max (d1, d2)。如图19.48所示。注意,从o到简化网格最近的点是s,而从s到原始网格最近的点是a,这就是为什么必须在从原始网格到简化网格的两种组合中进行测量,反之亦然。直观上,Hausdorff距离是使用简化网格代替原始网格时的误差。如果应用程序无法计算Hausdorff距离,可以使用为每次简化手动调整的常数,或者在简化过程中发现误差[1605]。

为了避免出现影响从一个LOD切换到另一个时,Ulrich [1797]提出了一种简单的变形技术,在一个顶点(x, y, z)从高分辨率的tile是线性插值顶点(x, y′z),这是近似从父tile(例如,使用双线性插值)。

启发式(如公式19.10中的启发式)可用于确定每个tile的详细程度。tiling方案的主要挑战是裂缝修复。例如,如果一个磁砖的分辨率是33×33,而它的相邻磁砖的分辨率是9×9,那么在它们相交的地方就会出现裂纹。一种纠正措施是沿着边缘移除高度详细的三角形,然后形成一组三角形,适当地连接两块瓷砖之间的缝隙[324,1670]。当相邻的两个区域有不同程度的细节时,就会出现裂缝。Ulrich 使用额外的丝带几何描述方法,这是一个合理的解决方案如果τ设置为小于5像素。Cozzi和Bagnell[300]使用屏幕空间后处理通道填充裂缝,在该通道中,裂缝周围的碎片(而不是裂缝中的)使用高斯核加权。Strugar[1720]有一种优雅的方法来避免裂缝,无需屏幕空间方法或额外的几何形状。如图19.49所示,可以用一个简单的顶点着色器实现。

为了提高性能,Sellers等人[1605]将大块LOD与视图截锥剔除和水平剔除相结合。Kang等[852]提出了一种类似分块LOD的方案,最大的区别在于他们使用基于gpu的细分对节点进行细分,并确保边缘细分因子匹配,以避免裂纹。它们还展示了如何使用带有保留特征的地图的几何图像来渲染带有突出部分的地形,而这是基于高度图的地形无法处理的。Strugar[1720]对分块LOD方案进行了扩展,使三角形分布更好、更灵活。与Ulrich的方法(使用每个节点的LOD)不同,Strugar使用每个顶点的变形,并带有各个层次的细节。虽然他只使用距离来衡量LOD,但也可以使用其他因素,例如附近有多少深度变化,这样可以产生更好的轮廓。

源地形数据通常由均匀的高度场网格表示。可以对这些数据使用独立于视图的简化方法,如图16.16所示,位于705页。对模型进行简化,直到满足某一极限准则[514]。小的表面细节可以通过颜色或凹凸贴图纹理捕获。由此产生的静态网格,通常被称为三角不规则网络(triangulated irregular network , TIN),是一个有用的表示,当地形面积小,在不同的地区相对平坦[1818]。

Andersson[40]使用一个受限的四叉树来弥补这些缺口,并降低大型地形所需的绘制调用总数。他使用的是四叉树,而不是在不同分辨率下呈现的统一的瓦片网格。每块瓦片的基本分辨率都是33×33,但是每块瓦片可以覆盖不同的面积。受限四叉树的概念是,每个瓦片的邻居只能有一个层次的细节不同。参见图19.50。这一限制意味着在有限的情况下相邻块的分辨率不同。这里的思想不是创建空白并呈现额外的索引缓冲区来填充这些空白,而是存储所有可能的索引缓冲区排列,这些索引缓冲区创建的平铺也包括空白过渡三角形。每个索引缓冲区由全分辨率边(一条边上有33个顶点)和较低层次的细节边(只有17个顶点,因为四叉树受到限制)组成。图19.51显示了这种现代地形渲染的一个例子。Widmark[1881]描述了一个完整的地形渲染系统,用于Frostbite 2引擎。它具有有用的功能,例如贴花、水、地形装饰、使用艺术家生成的或程序生成的遮罩[40]组成不同材质着色器,以及程序生成的地形位移。

海洋渲染的一个简单技术是使用一个统一的网格,将每一帧转换为摄像机空间[749]。如图19.52所示。Bowles[186]提供了许多技巧来克服某些质量问题。

除了上面提到的地形技术,它可以减少任何时候需要保存在内存中的数据集的大小,还可以使用压缩技术。Yusov[1956]使用四叉树数据结构压缩顶点,采用一种简单的预测方案,其中仅对差异进行编码(使用少量比特)。Schneider和Westermann[1573]使用由顶点着色器解码的压缩格式,在最大化缓存一致性的同时探索细节级别之间的地貌。Lindstrom和Cohen[1051]使用带有线性预测和残差编码的流编解码器进行无损压缩。此外,他们使用量化来提供进一步改进的压缩率,尽管结果是有损的。解压可以使用GPU完成,压缩率从3:1到12:1。

地形绘制还有许多其他方法。Kloetzli[909]在《文明V》中使用了一个自定义的计算着色器来创建地形的自适应细分,然后将其提供给GPU进行渲染。另一种技术是使用GPU的细分器来处理每个补丁的细分[466]。注意,许多用于地形绘制的技术也可以用于水绘制。例如,Gonzalez- Ochoa和Holder[560]在《Uncharted 3》中使用了一种适用于水的几何剪贴图变体。它们通过在层之间动态添加三角形来避免T-junctions。随着GPU的发展,这方面的研究还将继续。

Further Reading and Resources

虽然重点是碰撞检测,但Ericson的书[435]中有关于形成和使用各种空间细分方案的相关资料。

关于遮挡剔除有大量的文献。早期算法研究的两个很好的起点是cohenor等人[277]和Durand[398]的能见度调查。Aila和Miettinen[13]描述了一个用于动态场景的商业剔除系统的体系结构。Nießner et al. [1283] present a survey of existing methods for backfacing-patch, view frustum, and occlusion culling of displaced subdivision surfaces.。关于LODs使用的一个有价值的信息资源是Luebke等人[1092]的《Level of Detail for 3D Graphics》一书。

Dietrich等[352]对绘制大规模模型领域的研究进行了综述。Gobbetti等人提供了另一个关于大规模模型渲染的很好的概述[547]。Sellers等人[1605]的SIGGRAPH课程是较新的资源,拥有优秀的材料。Cozzi和Ring的书[299]介绍了地形绘制和大规模数据集管理的技术,以及处理精度问题的方法。Cesium blog[244]提供了许多实现细节和进一步的加速技术,用于大型世界和地形渲染。

发表评论

电子邮件地址不会被公开。 必填项已用*标注