65.9K
CodeProject 正在变化。 阅读更多。
Home

地形渲染

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2012年8月21日

CPOL

12分钟阅读

viewsIcon

53205

地形渲染是一个游戏技术代码示例,它通过有效地将任务分配给 CPU 和 GPU 来演示如何实时渲染大型地形。本文概述了地形渲染应用程序,并提供了免费代码的链接。

Intel® Visual Computing Source 呈现
下载源代码并观看演示

引言

此示例通过有效地将任务分配给 CPU 核心和处理器图形单元,演示了如何在 Intel® 微架构代号 Sandy Bridge 上实时渲染大型地形。该示例将输入的高度图预处理成一个分层的四叉树表示,用于渲染具有自适应选择细节级别(LOD)的地形。预处理过程中计算的自适应简化三角剖分被紧凑地编码以节省运行时处理和内存空间。LOD 构建由 CPU 核心异步执行,而渲染则由处理器图形单元完成。

Application

地形渲染是一个使用 DXUT 和 Microsoft DirectX* 11(带有 D3D_FEATURE_LEVEL_10_0)的应用程序。该应用程序处理所有渲染、用户交互和 GUI。初始化时,应用程序会加载所有模型、分配资源并编译着色器。首次运行时,应用程序会预先计算三角剖分,这可能需要一些时间(最多一分钟),并将其存储在磁盘上。在后续运行中,应用程序将从磁盘加载数据。

概述

地形渲染通过有效地将任务分配给 CPU 核心和处理器图形单元,演示了如何在 Intel 微架构代号 Sandy Bridge 上实时渲染大型地形。通过构建简化的三角剖分可以优化地形渲染,该三角剖分可适应地形表面的特性。这种三角剖分在具有高频细节的尖锐区域包含更多图元,并为平坦区域分配少量的大三角形。这显著减少了总三角形数量,同时提供了几乎相同的视觉质量(参见图 1 和 2)。

319399/image001.gif

图 1:全细节三角剖分,100 万个三角形,在 Intel® 微架构代号 Sandy Bridge 上,1280x1024 屏幕分辨率下为 40 fps

319399/image002.gif

图 2:自适应简化三角剖分,20 万个三角形,在 Intel® 微架构代号 Sandy Bridge 上,1280x1024 屏幕分辨率下为 112 fps

全分辨率三角剖分(图 1)与自适应(图 2)相比冗余度超过 5 倍,导致性能几乎降低 3 倍,但视觉质量几乎相同。

预计算自适应三角剖分是一项复杂且计算密集型任务。在运行时进行此操作可能需要大量时间,并会导致明显的停顿或延迟。为了解决这个问题,该应用程序在预处理阶段预先计算整个地形的自适应三角剖分,并将结果数据以紧凑的形式存储在磁盘上(参见第 4 节)。例如,一个 8192×8192 地形的所有编码三角剖分大约消耗 6 MB(相比之下,存储高度图需要 128 MB)。在运行时阶段,此数据用于高效地构建三角剖分。

三角剖分不仅必须适应地形表面特征,还必须适应相机位置,因为远处的地形区域可以使用更粗糙的表示进行渲染,而不会损失视觉保真度。为了支持多个细节级别(LOD),输入的高度图会经过预处理,并构建一个块四叉树数据结构 [3](图 3)。在运行时,细节级别的选择是基于块进行的,而不是基于三角形进行的。如果在每一帧都构建自适应三角剖分,则需要在 CPU 和 GPU 内存之间进行大量的数据传输,这是不高效的。通过基于块的 LOD 选择,数据仅在新块创建时上传到 GPU 内存,这每隔几帧发生一次。

319399/image003.gif

图 3:块四叉树的三个级别

层次结构中的每个块都是一个固定大小的高度图 319399/image004.gif,并带有额外的顶点,以无缝连接相邻的块。每个块覆盖的区域与其四个直接子节点相同,但近似地形的精度较低。每个块都分配有一个唯一的自适应三角剖分,该三角剖分已按照第 4 节的描述进行了预计算和编码。

在预处理过程中,为层次结构中的每个块预先计算了一个世界空间几何误差 τ。此误差表示块的多边形表面与最精细分辨率下的高度图样本之间的最大几何偏差。给定 τ 和相机位置,我们可以使用以下标准公式 [3] 估算块的屏幕空间误差,即简化模型与原始全细节高度图样本之间的最大可见偏差。

319399/image005.gif

其中 W 和 H 是视口的宽度和高度,319399/image006.gif 是水平和垂直视场角,dist(V,c) 是相机 c 到块边界框 V 最近点之间的距离。

在运行时阶段,维护一个不平衡的块四叉树,其叶节点满足给定的屏幕空间误差边界 319399/image007.gif。此树中的每个块都存储高度图、法线图和自适应三角剖分索引。每一帧都会执行一个递归过程,根据新的相机位置更新树(图 4)。该过程创建新块并为需要额外精度的地形区域分配资源(319399/image008.gif),并在 LOD 变得过高时简化表示(319399/image009.gif)。

319399/image010.gif

图 4:更新不平衡块四叉树。

通过 CPU 核心高效地处理从编码表示构建三角剖分,而渲染则由图形单元执行。这项技术很有用,因为它通过构建简化的自适应三角剖分显著减轻了 GPU 的负担。这项技术最有用的应用是拥有过剩 CPU 计算能力但已充分利用其 GPU 能力的系统。

自适应三角剖分

如上所述,四叉树中的每个块都有其自己独特的、自适应于局部地形表面特征的三角剖分。此三角剖分在预处理阶段计算并在磁盘上存储。自适应三角剖分构造利用了 [1] 和 [2] 中描述的方法。为了构建三角剖分,补丁的所有样本都被分配到不同的级别,如图 5 所示。请注意,为了构建自适应三角剖分,还使用了四叉树数据结构。为了区分这个四叉树与上面描述的块四叉树,我们称之为**顶点四叉树**。

319399/image011.gif

图 5:高度图样本分配到顶点四叉树的不同级别

为了保证从顶点四叉树构建的三角剖分不包含裂缝,四叉树受到图 6 所示的依赖图的限制。每个顶点都依赖于同一或下一个更精细级别的顶点四叉树层级中的另外两个顶点。边界顶点仅依赖于一个其他顶点。这意味着,如果一个顶点被选中进行三角剖分,那么相关的顶点也必须被选中。

319399/image012.gif

图 6:受限四叉树顶点上的依赖关系。
上面描述的顶点四叉树与三角形二叉树紧密绑定。三角形二叉树的根(级别 0)由一个右三角形表示。树的级别 n 是通过沿最长边将级别 n-1 中的每个三角形进行二等分得到的(图 7)。

319399/image013.gif

图 7:三角形二叉树的前四个级别。

最粗糙的块三角剖分由两个直角三角形表示,它们是两个三角形二叉树的根。三角形最长边的中点顶点称为基点。如果某个顶点包含在受限四叉树中,则称其为**启用**,否则称其为**禁用**。现在,如果我们有一个正确的启用顶点集(所有依赖关系都已正确维护),则可以使用以下简单的递归过程从根开始在运行时构建无裂缝的三角剖分。

1.        if( triangle base vertex is enabled ) 

2.        { 

3.        bisect the triangle 

4.        process two new triangles 

5.        } 

6.        else 

7.        output current triangle to the list 
算法 1:使用启用标志集构建自适应三角剖分

319399/image014.gif

图 8:启用的顶点和相应的三角剖分。

为了确定启用顶点的集合,在预处理过程中执行以下自底向上的算法。

1.        Clear enabled_array[] with false 

2.        for ( quadtree level l = finest resolution to coarsest resolution ) 

3.        for ( each vertex v in level l ) 

4.        { 

5.        if ( for all vertices d which v is dependent on: enabled_array[d] == false ) 

6.        { 

7.        merge two triangles for which v is base vertex 

8.        calculate the coalesced triangle world space approximation error e 

9.        if ( e < threshold ) 

10.     enabled_array[v] = false 

11.     else 

12.     enabled_array[v] = true 

13.     } 

14.     else 

15.     enabled_array[v] = true 

16.     }
算法 2:确定启用顶点。

三角形世界空间近似误差是三角形平面与三角形覆盖的所有顶点之间的最大垂直距离。使用上述算法 2 构建的自适应三角剖分保证了简化三角剖分与原始高度图之间的最大几何偏差低于给定阈值。

现在可以清楚地看出,整个块三角剖分由指示顶点是否启用的标志集完全描述。这些标志可以在递归遍历过程中通过输出 1 位标志来高效编码。这是通过以下算法完成的,该算法与算法 1 非常相似。

1.        if ( triangle base vertex is enabled ) 

2.        { 

3.        output 1 

4.        bisect the triangle 

5.        process two new triangles 

6.        } 

7.        else 

8.        output 0
算法 3:编码启用标志。

因此,在预处理过程中,首先执行算法 2 来确定启用顶点的集合,然后执行算法 3 对其进行编码。在运行时,执行算法 1,该算法使用预计算的数据来构建自适应三角剖分。

实现细节

地形渲染系统被构建为一组逻辑上独立的组件。系统的主要架构如图 9 所示。

319399/image015.gif

图 9:地形渲染系统架构主要方案

该系统包含一个高程数据源(用 CElevationDataSource 类实现)和一个编码的三角剖分数据源(实现为 CTriangDataSource 类)。前者通过 GetElevData() 方法提供对高程数据的访问,该方法创建一个 CPatchElevationData 类的实例并返回指向它的指针。CPatchElevationData 类通过 GetDataPtr() 方法提供对存储的高度图数据的访问。

三角剖分数据源遵循相同的理念:当需要为某个块创建自适应三角剖分时,它通过调用 DecodeTriangulation() 来创建一个 CRQTTriangulation 类的实例。三角剖分索引通过调用 GenerateIndices() 生成。CRQTTriangulation 类还负责确定启用顶点,并通过 CTriangDataSource::CreateAdaptiveTriangulation() 方法实现上述算法 2。

算法 1 和 3 由 CRQTTriangulation::RecursiveGenerateIndices() 方法实现。该方法可以在两种模式下运行:编码和解码(由 m_bIsEncodingMode 标志指示)。在第一种模式下,该方法使用启用标志(由算法 2 预计算)作为输入来编码三角剖分。根据三角形的级别和方向,该方法确定哪个顶点是其基点。然后,它从数组中读取其启用/禁用的状态,并将相应的 1 位标志输出到输出比特流。

在解码模式下,该方法从比特流(从磁盘加载)中读取标志,并在启用标志数组中设置相应的启用/禁用状态。同时,该方法生成三角剖分。

所有块的 Microsoft Direct3D* 资源都存储在 CTerrainPatch 类的实例中。资源管理由 D3D 资源缓存处理。当需要新纹理时,缓存会尝试查找合适的未使用的资源。如果没有备用资源,缓存会创建一个新的。当资源不再需要时,它不会被释放,而是被放入缓存。缓存是线程安全的,因此多个线程可以同时访问它。

四叉树的构建由 CBlockBasedAdaptiveModel 类实现,而 Microsoft DirectX* 11 特有的方法则分离在 CAdaptiveModelDX11Render 中。

异步任务执行

在运行时阶段,维护一个基于四叉树的自适应视图相关地形模型。为此,从相应的数据源提取所需块的高程数据和自适应三角剖分,并创建地形块。为了隐藏执行这些任务所需的时间并消除停顿,细节级别处理是异步执行的。由于 LOD 可以增加或减少,系统可以执行两种类型的任务,它们由 CIncreaseLODTaskCDecreaseLODTask 类实现。这些类派生自基类 CTaskBase,该基类公开了 Execute() 虚拟方法。为了管理任务,系统利用了 TaskMgrTbb 组件。

法线贴图压缩

法线贴图用于对地形表面进行着色。由于法线贴图可以在运行时从块的高度图计算得出,因此无需将其存储在磁盘上。这节省了磁盘空间,但需要适度的额外计算。此外,如果地形是动态的并且可以变形,则无法静态预计算法线贴图。为了提高视觉质量,法线分辨率可以高于高度图。例如,如果高度图的分辨率为 (2^n+1)×(2^n+1) 样本,则法线图可以具有 4 倍的分辨率(4∙(2^n+1)×4∙(2^n+1) 样本)。为了减少内存存储需求,法线贴图以压缩形式保存。利用 BC3 压缩格式,可以使用每字节一个法线来存储法线贴图。压缩是异步完成的,为此利用了 DXTCompressor 组件。

性能

图 10 显示了在 Intel 微架构代号 Sandy Bridge-based 机器上以 1280x1024 屏幕分辨率录制的飞行过程中捕获的性能。

319399/image016.gif

图 10:屏幕空间阈值的飞行录制过程中的性能

对于三像素的误差阈值,LOD 的变化并不令人烦恼,而在 Intel 微架构代号 Sandy Bridge-based 机器上的平均性能超过 100 fps。对于五像素的阈值,几何形状的变化变得更加明显,但性能提高了 1.5 倍。

增加 LOD 所需的时间主要取决于所选法线贴图的细节级别偏差。对于 4 倍的法线贴图上采样因子,在一个核心上处理一个块大约需要 44 毫秒。因此,对于一个由 150 个块组成的模型,在一个核心上构建整个模型大约需要 6.6 秒。由于所有块都是独立处理的,因此工作负载可以均匀地分配到可用的核心上,从而使时间得到良好的缩放。还请注意,在典型的飞行过程中,每秒钟只有几个块被更新(通常少于 10 个)。如果相机位置发生剧烈变化,模型将在几帧内异步更新,这不会导致停顿。请注意,在模型更新之前,它将以粗略分辨率渲染。

319399/image017.gif

图 11:示例截图。请注意,此处使用了一个简单的方法来着色高程差异,但通过提高纹理质量可以获得更好的地形外观。

参考文献

  1. P. Lindstrom, D. Koller, W. Ribarsky, L. F. Hodges, N. Faust, and G. A. Turner. Real-time, continuous level of detail rendering of height fields. In Proc. SIGGRAPH 96, pages 109-118. ACM SIGGRAPH, 1996.
  2. Renato Pajarola. Large scale terrain visualization using the restricted quadtree triangulation. In Proceedings Visualization 98, pages 19-26 and 515. IEEE Computer Society Press, 1998.
  3. Thatcher Ulrich. Rendering massive terrains using chunked level of detail control. SIGGRAPH Course Notes (2002). Volume 3, Issue 5.
© . All rights reserved.