Geometry Clipmaps: Terrain Rendering Using Nested Regular Grids
几何裁剪图下的使用嵌套的规则格网渲染地形
Frank Losasso | Hugues Hoppe | 张嘉华(翻译) | 梁成(翻译、女) |
斯坦福大学和微软研究院 | 微软研究院 | 华南理工大学 | 华南理工大学 |
| http://research.microsoft.com/~hoppe | newzjh@126.com | aliceliang@163.com |
使用粗糙的geometry clipmap插图 (size n=31) | 216,000×93,600美国大峡谷附近观察图 (n=255) |
图1:使用geometry clipmaps的地形渲染, 展示clipmap各层次 (size n×n)以及过渡区域(右图蓝色)
摘要(Abstract )
渲染吞吐量已经达到能够用一种新的LOD(细节层次技术)去控制地形渲染的程度。我们介绍geometry clipmap,它能够通过一系列位于观察者中心的嵌套网格加速地形渲染。网格以顶点缓冲(vertex buffers)的形式存储在显示内存,当视点移动时不断地填充。这个简单的框架提供了可视的连贯性,帧约束(同一的帧速),complexity throttling,和graceful degradation。而且,它允许两种新的令人兴奋的实时功能特性: 解压缩(decompression)和合成(synthesis)。我们的主要数据是一个40GB的整个美国的高度图。一个压缩的图像金字塔使数据大小减少100倍。所以它完全适合在内存中。这压缩数据同样有助于法线图(normal map)的着色. 当观察者接近表面,我们通过分形噪声位移(fractal noise displacement)合成格网层次,比存储着的地形解压缩后要好。合成和normal-map计算是不断进行,因此使得我们能够以60帧每秒的速度交互飞行。
关键字(Keywords):细节层次控制(level-of-detail control)、地形压缩和综合(terrain compression and synthesis)
1、简介(Introduction)
地形几何是室外场景的一个重要组成部分,作为电影、虚拟环境、cartography、和游戏的实例。特别是对于室外游戏,包括飞行模拟、赛车模拟和大量多人游戏。在这篇文章,我们关注如何实时渲染地形高程。
大规模地形高度图包括十亿多的地形采样,离直接进行交互渲染还有很远的距离。此外,由于从象素抽样的未过滤的多对一映射,渲染统一的密集的三角形会导致混淆(aliasing)效果,正像没有多重映射(mipmap)的纹理。 [Williams 1983]。所以细节层次(LOD)控制对于调整地形tessellation是必须的,常以观察的参数作为因子。
在地形细节层次简化(LOD)方面已经有大量的研究,在第二节中我们将会回顾,过往的方案调整细分不但基于观察距离,还基于具体的地形几何。根据直觉我们可以看出,平坦的区域安排大的三角形,会带来更小三角形的不规则网格渲染。但是,这样的框架有几个缺点。细分准则和细分操作必须预计算,并且消耗额外的内存。数据结构设计会涉及带有不在同一个缓冲的随机存储访问。改变tessellation需要更慢的立即模式渲染,当对静态区域进行加速时会干扰时间的连续性。为了维持一致的帧速,细分阙值必须随着地形的起伏不平而改变。最终,表面着色需要纹理图形,它们分开存储用在整个不同的LOD结构上。
渲染吞吐量在现在的GPU上已经达到 100M 个三角形每秒,足够以视频速度基于象素大小地填充整个帧缓冲(framebuffer)。此外,顶点处理速度不断增加,接近象素处理速度,所以我们预测好的LOD策略不再是本质的,我们取而代之的是寻找所有三角形基于象素的地形的统一的屏幕。关键是开发一套LOD框架能够优化图形流水线的倚赖。
我们的贡献是geometry clipmap,它能以一系列以观察者为中心的嵌套网格加速地形渲染。这些网格而不同的power-of-two分辨率过滤的形式展现。并且以顶点缓冲的形式存储再显存。当观察者移动时,clipmap层次改变并且不断重新填充数据。
这个方法于与纹理映射中的图像LOD处理很类似。为了避免空间混淆(spatial aliasing),图像预过滤到一个power-of-two 网格的mipmap(多重映射)金子塔[Williams 1983]。Mipmap层次被渲染的每一个象素是屏幕空间参数微分的函数,倚赖于观察参数,不倚赖于图像内容。纹理裁剪图加速(cache)视点倚赖的Mipmap金字塔的子集 [Tanner et al 1998]。快速不断的纹理clipmap更新允许探索巨大的图像。
我们的几何裁剪图(geometry clipmaps)灵感来源于纹理裁剪图(texture clipmaps),但有一些关键的不同点。纹理裁剪图计算每个象素的LOD基于屏幕空间投影几何,但是对于地形,屏幕空间几何直到地形LOD选择前都不能获得——循环倚赖。更重要的是,每个象素的LOD选择会让保持网格密不透水和时间上的平滑变得困难。
取而代之,我们在世界空间基于观察距离选择LOD,使用一系列关于视点的嵌套的矩形区域。我们创建渐变区域以平滑和混合层次之间,使用0面积三角形缝补层次边界避免T-junctions。LOD过渡方案允许clipmap层次独立过渡,并且让各个层次裁剪胜于invalidated atomically像[Tanner et al 1998]表述。并且,我们应用一样的方案到纹理图像,获得统一的LOD框架对于几何和图像,并且不像纹理裁剪图,它不需要特别的硬件。
Geometry clipmaps提供了一系列的优点对于过往的地形LOD方案:
l 简单(Simplicity). 没有在基于指针/索引的结构上的不规则移动,并且没有细化倚赖的跟踪(tracking of refinement dependencies)。
l 优化的渲染吞吐量. Clipmap顶点存储在显示内存之中,并且它们的规则结构允许我们以带有优化顶点缓冲再用(with optimal vertex-cache reuse)的triangle-strip索引方式渲染。
l 可见的连贯性(Visual continuity ) 分别在顶点程序和象素程序使用一些指令,内部的层的渐变区域对于几何和纹理均提供空间和时间上的连贯性。
l 稳定的渲染(Steady rendering) 由于tessellation完全独立,不倚赖于地形粗糙度,无需参数用于动态调整,所以使得渲染速度接近常数。
• Immediate complexity throttling. 正因为有固定的clipmap大小,我们能够收缩渲染区域以减少渲染负荷。Tanner等[1998]使用一种近似的想法控制纹理裁剪图的更新带宽。
• Graceful degradation. 当观察者迅速移动的时候,更新带宽(重新填充clipmap)会变为瓶颈问题。就像在纹理裁剪图,我们更新尽可能多的层次在一次预算当中,这带来的结果是快速移动使地形丢失了它的高频细节。
l 表面着色(Surface shading). 法线图(Normal maps)在飞行时从几何体上方计算,并且使用一样的LOD结构作为地形几何。
地形几何同样提供两个新的运行时功能:
l 压缩(Compression).既然不单裁剪图需要扩展到顶点缓冲,剩余的地形金字塔也能够以压缩形式存储。我们通过二维的图像编码压缩金字塔各层之间差值(residuals),高的数据一致性允许我们压缩比达到60%—100%. 存储整个地形在内存中以避免磁盘分页停顿。
l 合成(Synthesis) 简单的规则格网结构允许我们on-the-fly地形合成,以便于粗糙的几何部分能够通过程序上生成细节得到增强。我们示范了简单的分形噪音,它可能在不久的将来能由GPU自己实现。
局限性(Limitations)
渲染网格比过往的LOD方案复杂。本质地,我们总是假设最差情况下的地形,带有同一的细节(每个地方有所有的频率),因此不利于局部调整。另一方面,网格是规则的,能够分配到显存。因而我们瞄准这种差的情况优化渲染渲染。
另外一个限制是地形被假设为像第9节所描述那样有边界频谱密度。举个例子,一个高的像针的特征地形会morph到观察范围比较迟。幸运的是,实际上地形是比较好和平滑的,不是十分常出现这种明显变化的问题。值得注意的是,建筑物、植物和其它对象被组合到环境上的,我们要使用其它LOD技术分别渲染
2. 过往的地形LOD技术(Previous terrain LOD techniques)
地形LOD算法使用层次网格细分合并操作以调整表面tessellation。算法能够根据结构分为下面这些:
l 不规则网格(Irregular meshes)
l 二叉树(Bin-tree hierarchies)
l 二叉区域(Bin-tree regions)
l 拼接块(Tiled blocks)
(4种过往的LOD方案详细介绍暂略)
理想化地,视点倚赖的LOD算法适应细化(分割)和粗糙化(合并)网格基于屏幕几何误差,几何误差被定义为以象素表示的网格和原地形点的距离。屏幕误差联合考虑影响来自下面个几个:(1)观察者距离(2)表面方位(3) 表面几何。既然表面方位很少提供重要的LOD影响,很多方案选择忽略它。一个通常的细分准则[Blow 2000]是每个顶点存储一个半径定义一个包围球。预计算半径编码了局部的表面近似误差,如果视点进入包围球则顶点的邻接顶点被细分。
几何裁剪图相当不同于过往这些工作。细分层次基于观察者为中心的规则格网,geomorph提供内部的连续性。这些细分准则仍然考虑观察者距离,但忽略局部几何。如:所有顶点共享一样的椭球半径。
视点倚赖的位移贴图映射(View-dependent displacement mapping)
地形能够被看成一个平坦的几何面上的位移图。一些最近的文献提出了硬件相关的方案用于调整位移贴图的tessellation [Gumhold and Hüttner 1999; Doggett and Hirche 2000; Moule and McCool 2002]。到目前为止,这些方案仅仅在简单的格网上模拟,并且他们假设整个格网是能容纳在内存的。
纹理(Textures) 到目前为止,没有很多工作关于如何处理伴随地形的巨大纹理图除了关于纹理裁剪图的[Tanner et al 1998],标准的方法是纹理拼接,还有更普通的纹理层次引入方案,如Döllner等[2000]。
根据我们的知识,没有过往的地形LOD技术能够达到有较大的压缩比和实现地形合成。
3. Geometry clipmap 概述
几何裁剪图(geometry clipmap),通过一系列m个层次构成一个地形金字塔, 以2n分辨率区域嵌套形式展现,如文章首部图1。每一个层次包含n×n个顶点的数组,存储在显存中的顶点缓冲。为了能够不断地进行有效更新,数组以环形(toroidally)方式编码,通过在x和y上的求模(mod)操作形成环形编码。每个顶点包含(x, y, z, zc)坐标,zc 是下一个粗糙层次的高度值(x, y),用于几何渐变。(6.2节)。
图2:geometry clipmap各区域定义
Clipmap regions
对于每一个clipmap层次,我们定义一系列矩形区域(见图2)。裁剪区域(clip region)是世界空间范围内每一层存储的n×n规则格网数据。活动区域(active region)是期望渲染的区域,特定为一个n×n区域位于观察者中心。当观察者移动时,我们通过裁剪区域(clip region)更新每一层匹配期望的活动区域的clipmap。但是,这样的更新在快速移动时是很耗费的,我们让裁剪区域(clip region)落后于观察者,并且裁剪活动区域(active region)到现有数据范围,如图2。最终,渲染区域(render region)被设计成空心的(图中绿色部分),它的外部边界是l层活动区域(active_region(l)),内部边界是l+1层活动区域(active_region(l+1))。
对于最好的m层,m+1层活动区域(active_region(m+1)),被定义为空,即最高层为实心方形区域。活动和裁剪区域在观察参数改变时更新,如第4节和第5节描述。
纹理(Vertex Shader Texture)
每一个clipmap层次同样包含关联的纹理图像。我们为每一个用到的表面存储一个8位每通道(8-bit-per-channel)的法线纹理图像,这比每个顶点(per-vertex)存储法线有效。对于可靠的着色,法线图(normal map)有相对于几何结构两倍的分辨率,即为几何格网两倍大小,因为一个顶点一个法线太模糊了 [Vlachos et al 2001]。法线图 (normal map)通过地形几何在clipmap更新的时候计算。额外的图像,如地形每个顶点的颜色属性同样可以存储为不同的分辨率。就像顶点数组,纹理也是以环形编址(toroidally)的方式以有效地更新。在现代GPU中,通常以Vertex Shader Texture Fetch(在顶点着色程序段访问纹理)实现。
每一帧的算法(Per-frame algorithm)
下面步骤是每一帧都要做的:
1. 检测期望的活动区域 (第4部分).
2. 更新地形几何 (第5部分).
3. 裁剪活动区域到裁剪区域(clip regions),并且渲染(第6部分)
4. 期望的活动区域运动的计算(Computation of desired active regions motion)
视点倚赖的refinement,通过clipmap 中每一层的活动区域选择被检测。我们通过一个简单的策略,对于在世界空间中格网间距为 的每一层L,我们让期望的活动区域为以视点坐标(x,y)为中心的n×n区域。换句话,期望的clipmap被定位到观察者中心,我们希望渲染每一层的全部区域。
让我们考虑屏幕空间三角形大小和clipmap大小n之间的关系。我们假设地形有很小的坡度,所以每一个三角形近似一个大小为 的直角三角形。(我们在第九节提供一个大致的误差分析)
对于任意的可见的世界空间的点,屏幕大小反比例于屏幕空间的深度。如果视线方向是水平的,屏幕深度可以在XY平面量度。观察者坐落于l层渲染区域(render_region(l))的中心,它有n× 的外边界大小,以及n× /2的内边界大小。对于90度宽的视野,屏幕空间平均深度(遍及各个方向)大约0.4×n× 。所以近似的屏幕空间以象素表示的三角形大小s如下:
,
W是窗口大小, ,我们定义W=640象素, =90度,我们获得clipmap大小n=255的好结果。这符合一个屏幕空间三角形3象素的大小。所以我们的法线图(normal maps)以两倍于地形几何格网的分辨率存储。这给我们近似1.5象素每个纹理抽样(sample),对于纹理抽样设置来说是合情合理的。
当我们的视线方向不是水平时,渲染区域l(render_region(l))的屏幕深度大于上面期望的0.4×n× ,并且因此屏幕空间三角形变得小于s,如果直接从地形上空向下俯视,三角形大小是很小的,并且明显变得混淆难以分辩。解决方法是不渲染不必要的好的精度的层次。特别地,我们计算观察者处于地形上方的的高度通过访问有效的最好的clipmap层次。对于每一个层次l,如果观察者高度大于0.4×n× 我们把活动区域设置为空。
把活动区域简单定位于观察者中心的缺点是:当视觉变窄时clipmap大小n必须增大。解决的方法是:相对于视锥调整clipmap的位置和大小。这里我们采取了上述不考虑视觉变窄的简单替代方案,选择以观察者为中心的区域,因为它们能够让观察立即围绕当前视点旋转。这已经能够满足多数应用的需求(比如飞行模拟器通过可以转换遥控杆让用户观察各个方向)。其它的问题我们可以倚赖于视锥消隐避免渲染观察范围以外的地形(6.4节)。
综上所述,活动区域定义为以视点坐标(x,y)为中心的n×n区域,格网间距为 ,如果观察者高度大于0.4×n× 我们把活动区域设置为空。
5. 几何裁剪图更新(Geometry clipmap update)
当期望的活动区域随着观察者的运动而改变时,裁剪区域(clip regions)应该同样跟着改变。注意到我们采用上面提及的环形方式编址,我们在改变一个层次时不必要复制旧的数据,而是简单填充新的L形暴露的区域。数据有两种来源:直接从地形压缩数据中解压或者通过地形综合程序合成。(见第7和第8部分)。通常,粗糙的层次从地形压缩数据中解压缩,好的层次通过分形综合得到。
无论通过解压缩或者合成更新clipmap,我们以插入细分的方案通过粗糙的层次预测好的层次几何。我们选择著名的四点细分曲线(four-point subdivision interpolant)的张量积(tensor-product)方案 [Kobbelt 1996],它有(–1/16, 9/16, 9/16, –1/16) 掩码权重(mask weight) [Dyn et al 1987] 。这种采样过滤器(upsampling filter)U具有期望的C1 平滑度属性。
二者选一的更新方案将会是预测未来当裁剪区域改变时观察者的运动,以减少频繁的更新动作。由于我们能够在小的区域有效地执行解压缩和合成,更新粒度不是当前重要的因素。
当观察者快速运动,更新全部层次的过程变得过多和漫长。就像在纹理裁剪图(texture clipmaps),我们更新各个层次以从粗糙到好(coarse-to-fine )的顺序,在达到处理预算时停止。我们选择以更新的采样(updated samples)数量超过n2时停止,因为在未更新好的层次的裁剪区域(clip regions)是落后的,它们逐渐地裁剪关联的活动区域,直到它们为空。这样做的后果是接近观察者的快速运动丢失了高精度细节。一个有趣的结果是渲染负荷事实上降低了当观察者运动时。
我们在裁剪区域(clipmap regions)定义下面约束:
1. l+1层裁剪区域属于l层裁剪区域,通过一个分级的距离表示,我们需要裁剪区域嵌套以进行从粗糙到好的地形几何预测。预测需要在各个方面维持一个网格。
2. l+1活动区域属于l层活动区域,渲染数据必须是呈现的在clipmap中的数据的一个子集。
3. l层活动区域的周长必须取决于连续的顶点,以构成一个在比较粗糙的l-1层的密封的边界。
4. l+1层活动区域属于l层活动区域,渲染区域必须至少两个格网单元宽度以允许在两个层次之间两许的渐变。
6. 几何裁剪图渲染(Geometry clipmap rendering)
6.1 基本的渲染算法(Basic rendering algorithm)
给出期望的活动区域,我们通过下面算法渲染地形:
//裁剪活动区域 从粗糙到好顺序遍历每一个层次(foreach level in coarse-to-fine order) { 裁剪l层活动区域到l层裁剪区域(Crop active_region(l) to clip_region(l) ) 裁剪l层活动区域到l-1层活动区域(Crop active_region(l) to active_region(l-1) ) }
//渲染各个层次 从好到粗糙顺序遍历每一个层次 { L层渲染区域=l层活动区域-(l+1)层活动区域(render_region(l)=active_region(l)–active_region(l+1) 渲染该渲染区域(Render render_region(l) ) }
|
从第5节我们了解到,活动区域被裁剪为裁剪区域,并且比较粗糙的活动区域满足约束条件2-4。注意到,如果一个活动区域为空,则构造完全的活动区域l,l>k同样为空。这相当普遍对于好的层次是空的。活动区域,由于它们的裁剪区域已经在时间内被更新了(如观察者运动得很快),或者由于好的层次是无必要的(如观察者远高于地形表面观察地形)。
既然好的层次是解决观察者的,我们渲染各层从好到粗糙的顺序以利用硬件遮挡消隐。L层渲染区域被分割为4各矩形区域,它们分别通过triangle strips渲染,如图3。最多的strip长度为顶点优化而选择 [Hoppe 1999] ,并且三角形带(triangle strips)分组在一起以实现大批量渲染(batch)。连续的规则格网访存在显存层次中的各层能够很好处理。当前,二维的环形访问(toroidal access)需要CPU反复计算顶点索引每帧,但是,这很快会得到解决在以后。
图3:一个渲染区域的三角带(triangle strip)生成图示(在实际中,三角带达到20个三角形长度)
6.2渐变区域为了可视的连贯性(Transition regions for visual continuity)
上面提及的简单算法描述,由于2的幂(power-of-two mismatch)在边界上的不匹配,存在不同渲染区域之间的裂缝。为了除去裂缝并且提供时间上的连续性。我们让 每层渲染区域外边界附近的几何网格morph 以便于让几何网格过渡到较为粗糙的l-1层。Morph是相对于视点(vx,vy)的地形顶点的空间网格坐标(x,y)的函数。所以这种渐变不是基于时间的,但能代替跟踪连续的观察者位置。
通过实现,我们发现一次渐变的宽度约为10n个网格单元时效果很好,如果w更加小,层次边界会变得明显,如果W比较大,好的细节会存在不必要的丢失。如果好的l+1层活动区域太接近,我们计算w = min(10n, min_width(l)), 容易看出,min_width(l)最少为2 (参见图4)。
想起前面提到的,每个顶点存储为(x, y, z, zc)向量,zc 是下一个l-1粗糙层的高度,我们从下面的公式活动morphed后的高度:
混合参数 , 如下:
可以类似计算得到。这里的 指示在区域L的格网中的视点的连续的坐标,xmin 和xmax 为以整数表示的活动区域L的范围。期望的熟悉是α的估值为0除非渐变区域在边界上直线地倾斜到1。这样的评估大约需要十条GPU顶点程序,看来这会添加渲染负荷。
T-junction removal. 尽管几何渐变能够避免裂缝,但是边界上的T-junctions仍然由于丢失了象素而存在。为了缝合相邻的层以形成密不透水的网格,我们使用 在渲染边界上渲染o面积三角形 的简单简介方案。
图4:边界外附近的渐变区域,让l层平滑地和比较粗糙的l-1层混合