使用基于GPU的Geometry Clipmap渲染地形

来源:互联网 发布:macbook编程软件 编辑:程序博客网 时间:2024/05/18 23:28

使用基于GPU的Geometry Clipmap渲染地形(上)

使用基于GPU的Geometry Clipmap渲染地形(Terrain Rendering Using GPU-Based Geometry Clipmaps)(上)

翻译:clayman
clayman_joe@yahoo.com.cn
仅供个人学习使用,勿用于任何商业用途,转载请注明作者^_^

注:这篇文章翻译的是《GPU Gem2》中第二章的内容,老早就翻译的东西了,但因为翻的不太好,一直放着,找不到原版的PDF文档,截图有些是在网上找的,有些是我自己做的,大家凑活看了,基本和原图还是差不多的。《GPU Gem2》中的内容实在很难,本人水平有限,有错的地方还请大家指正。


          Geometry clipmap是用于渲染地形LOD的新方法。它把地形缓存在一组嵌套的规则网格中,同时在观察点移动时,不断更新这些数据。和之前不规则网格(irregular-mesh)技术相比,规则的网格有很多优点:简洁的数据结构、平滑的视觉效果,稳定的渲染速率,合适的分级(graceful degradation),高效的压缩,以及运行时的细节合成(synthesis)。这里,我们将描述一种通过顶点纹理(vertes texture)实现的几何clipmap。把地形几何体作为图片来处理,几乎所有计算都能通过GPU来完成,从而减少CPU的负担。

1.1 Geometry clipmap简述
         在大型户外环境中,处理地形数据将会占用大量的储存空间以及渲染带宽。我们已经开发了多种LOD技术来处理地形。但是,大多数技术需要在运行时创建和修改mesh数据(顶点和索引缓冲),而这些操作对当前的图形处理技术(graphics architecture)来说代价是很大的。此外,不规则的网格通常需要使用CPU来处理,而在诸如游戏之类的应用中,CPU本来就不够用了。
         Gemetry clipmap框架中(Losasso and Hoppe 2004)把地形作为一张2D的高度图(elevation image)来处理,并预先把它过滤为一张包含L层的mipmap金字塔。

处理复杂地形时,完整的金字塔可能对内存来说太大了。Geometry clipmap结构对每一层进行窗口大小为n x n的采样并缓存,这和Tanner 1998介绍的texture clipmap方法有些类似。对观察者来说,这些窗口相当于一组嵌套规则的网格。注意,高细节层次所占的空间总是比较粗糙的层次少。这样的目的是保证在屏幕空间,所有三角形都统一大小(the aim is to maintain trangles that are uniformly sized in screen space)。如果clipmap大小为 n = 255, 那么在1024 x 768的分辨率下,每个三角形大约是5个像素。

         只有最高的层次(L-1级)渲染为完整的方形网格。其他层次都渲染为空心环,因为空心部分已经由较高层次填充了。
         当观察者移动时,clipmap窗口也作相应的改变,同时更新数据。为了保证高效的持续更新,以一种环形的方法来访问clipmap每一层的窗口,也就是说使用2D环绕寻址(the clipmap window in each level is accessed toroidally, that is ,with 2D wraparound addressing)。
         其中,挑战之一就是如何隐藏相邻层次之间的边界,同时,保证一个完美的网格,避免临时的撕裂效果。Geometry clipmap的嵌套网格结构提供了一种简单的解决方案。其中,关键的思想就是每层在靠近外层边界的地方引入一个交换区域(transition region),这样,几何体和纹理都能平滑的通过插值过渡到下一个粗燥级别。使用顶点和像素着色器,可以分别高效的实现这些交换区域。

         Geometry clipmap的嵌套网格结构同样能实现高效的压缩以及合成。它可以通过对较粗糙的层次的数据进行upsample(提高取样率),预测(prediction)每一层的高度数据。因此,我们只需要储存多余的细节信息并合成到这个预测的信号上就可以了。

1.2 Overview of GPU Implementation
         Geometry cliipmap最初的实现方法把每一层数据当作普通的顶点缓冲。由于当前的GPU缺乏修改顶点缓冲数据的能力,所以这种实现在更新数据和渲染时都需要CPU参与。而这里,我们将使用顶点纹理来实现geometry clipmap。这样做是有益的,因为clipmap 2D的窗口网格数据更适合保存为一张2D纹理,而不是通过人工线形化处理保存为1D的顶点缓冲。由于clipmap包含L层,每一层又包含了n x n的几何采样,我们将把采样的(c,y,z)信息分为两部分:
* (x,y)坐标储存为常量顶点数据
*Z坐标储存为一个单通道(single-channel)的2D纹理——高度图(elevation map)。为每层都定义一个n x n的高度图。这些纹理都将在clipmap层次根据观察点变换时更新。
         由于clipmap的层次是统一的2D网格,(x,y)坐标也是规则的,同时,变换和比例也是常量。因次,我们定义了一组只读的顶点和索引缓冲,用来表示2D“footprints”,同时,在每个层次都重复使用这些footprints。
         顶点通过把高度图作为顶点纹理来采样,获得高度值。在顶点着色器中访问纹理是DirectX 9 Shader Modle 3.0的新特性,NVIDIA Geforce6 系列的GPU都支持这个功能。把高度数据保存为图片,可以直接使用GPU的光栅管道(rasterization pipeline)来进行处理。对于需要进行合成操作的地方,所有运行时的计算(高度图的upsample,地形细节合成,法线计算,以及渲染)都完全由GPU来执行,这样就可以留出宝贵的CPU资源)。对压缩地形来说,就需要CPU不停的为图形卡解压和更新数据。

1.2.1 数据结构
         总的来说,主要的数据结构如下。我们预先定义一组顶点和索引缓冲常量,为clipmap的网格(x,y)编码。对0…..L-1中的每一层,分配一张高度图(单通道浮点2D纹理 1-channel floating-point 2D texture)和一张法线图(4通道 8-bit 2D纹理)。以上所有数据结构都保存在显存中。

1.2.2 Clipmap的尺寸
         由于每层的外边界都必须覆盖在下一个粗糙层次的网格上,网格尺寸n必须是奇数。当纹理尺寸是2的幂时,硬件可以进行优化,所以我们选择n = 2^k-1,保留一行一列作为未使用的纹理。我们的例子中大多数情况下使用n = 255。
         选择 n = 2^k-1还有一个优点就是较好的层次永远不会位于下一个粗糙层次的中心(the finer level is never exactly centered with respect to its parent next-coarser level)。换句话说,就是对于下一个层次,它在x或y方向总有一个网格单位的偏移,具体偏向那个位置则根据观察点的位置来决定。事实上,当下一个粗糙层次不变时,应该允许较好的层次可以改变,因此,较好的层次有时就必须进行偏移。当然,我们也可以选择n = 2^k+3作为网格尺寸,来达到中心对齐的效果,但是这样仍然需要处理中心偏移的情况,导致最后的结果过于复杂。

1.3 渲染

1.3.1 有效层(Active Levels )
         虽然我们为clipmap分配了L层,但是通常只渲染(和更新)一组有效的层次0……L’-1,L’的值基于观察点的高度来计算。这样做的目的是,当观察点足够高时,clipmap中最好的层次就不必渲染了。特别的,我们把网格范围小于2.5h的层次作为无效层,这里,h表示观察点到地形的垂直距离。由于地形数据都储存在显存中,计算h将导致重新读取最好的有效层高度纹理图采样点数据。这个重新读取的工作将带来一些小小的额外开销,因此,每过几帧检测一次。当然,需要把L’-1层渲染为一个完全的方形而不是空心环。
         Losasso和Hoppe2004描述的方法中,对于在观察者移动时没有完全更新的层进行了裁剪(cropping)。为了简化GPU的实现,我们放弃了这个特性。我们假设每个层是完全更新的,或者完全无效。

1.3.2 顶点和索引缓冲

         正如之前所说的,我们把网格的(x,y)值作为顶点数据,同时,z方向的高度值保存为单通道的浮点纹理。为每层的环定义一个单独的顶点缓冲来保存(x,y)数据是一种有效的方法。但是,为了减少内存空间和实现可视区域裁剪,我们把环分为一些小的footprint片。

         大部分环都由12个m x m的渲染单元块(block)组成,如图中的灰色区域,这里m = (n + 1) / 4。因为2D网格是规则的,同一个层中的(x,y)都进行同样的变换。此外,不同层次之间也可以使用统一的缩放值。因此,使用一个只读的顶点和索引缓冲,以及一些额外的参数,就可以让vertex shader对每个渲染单元块进行缩放和变换,完成所有渲染 。
         当clipmap大小为255时,每个规则的渲染单元块包含64x64个顶点。使用顶点缓冲来保存这些三角形带的索引。为了减少储存空间,将使用16位的索引值作为索引缓冲,这样,渲染单元块的m最大值就为255,同时,clipmap中n的最大值为1023。
         但是12个渲染单元块的大小并不能完全覆盖整个环。我们使用一些小的2D footprint来填充这些裂缝。注意,这些额外区域的大小和渲染单元块相比应该是很小的,就像上图所示的那样。首先,环每条边的中间都有 (n – 1) – ((m -1) x 4) = 2个方格的裂缝,图中黄色部分,可以使用4个m x 3的固定区域来填充他。我们把这些区域编码为一个顶点和索引缓冲,并且对所有层复用这个缓冲。第二,在内层环的两条边界处会产生一个方格大小的裂缝,图中蓝色部分,以满足较好层次中心偏移的效果。这条L形的裂缝可能出现在四个位置(左上,左下,右上,右下),我们将使用4个顶点以及一个索引缓冲来填充这条内部的裂缝,使用对所有层次复用这几个缓冲。
         此外,还需要在环的外边界渲染一系列退化(degenerate)三角形(图中橙色的部分)。为了避免T-junction效果,这些0区域(zero-area)三角形是必须的。最后,使用4个额外的渲染单元块以及一个L形区域填满整个环。
         由于每个footprint(x,y)都为局部坐标,所以,不需要32-bit的精度,使用D3DDECLTYPE_SHORT2类型就可以了,这样每个象素只需要4 bit。将来,我们甚至可以在每个m x m大小的小的块内使用顶点索引i来计算这些坐标(x,y),比如(fmod( i , m) , floor (i/m))。

1.3.3 View Frustum Culling
         我们以块为最小单元在CPU上进行view frustum culling计算。每个块都扩张到[ Zmin, Z max]的范围内,并且在三维空间内由可视平截体进行分割。只有在分割快不为空时,才对它进行渲染。根据观察点的位置,对于90的观察范围来说,可以把渲染负载减少到原来的1/2 ~ 1/3之间,如下图所示。

1.3.4 关于DrawPrimitive调用
         对于每一层来说,我们余姚调用14次DrawPrimitive(DP)方法:12个块每个一次,为footprint调用一次,为剩下的三角形再调用一次。但是,在视平截体裁减之后,平均每帧只需要渲染12个块中的4个。而对于最好的层来说,则需要多调用5次来填充中间的空间。因此,总的来说,平均每帧需要调用6L+5(当L=11时为71)次DP方法。更进一步,使用Geometry Instancing技术把每个层次中的所有大块作为一个统一的整体调用一次DP,则可以把DP的次数减少到 3L + 2 (35)


      (未完待续,下一部分将给出具体的代码实现)

使用基于GPUGeometry Clipmap渲染地形(Terrain Rendering Using GPU-Based Geometry Clipmaps)(下)

 

翻译:clayman
clayman_joe@yahoo.com.cn
仅供个人学习使用,勿用于任何商业用途,转载请注明作者^_^

1.3.5 Vertex Shader

         我们使用同一个vertex shader来渲染之前描述的所有2D footprint。首先,对于给定的footprint坐标(x,y)来说,shader通过简单的缩放和变换来计算它的世界坐标(x,y)。接下来,从顶点纹理中读取高度值z。这里不需要任何过滤器,因为顶点和纹理采样是一一对应的。

         为了平滑的过渡,vertex shader将对两个不同层次边界的顶点进行几何混合(blend)。根据顶点(x,y)相对于观察点(VxVy)的位置来计算混合参数alpha。这里:

alpha = max( alphax,alphay):

alphax = clamp(( | x – Vx ) – ( (n-1)/2 – w – 1)) /w , 0, 1)

         使用类似的方法计算alphay

         这里,所有的坐标都以clipmap网格范围内[ 0….n-1]之间的值来表示,w是交换区域的宽度(这里选择了 w= n/10)。我们希望除了交换区域外,alpha值都为0,而在交换区域内,值线形的从0增加到1,当达到外围的层次时值为1。下图的蓝色部分即为交换区域。

 

         对于几何混合来说,我们对同一位置(x,y)在不同层次之间的高度值Zf(较好层次的高度值)和Zc(教粗糙层次的高度值)之间进行线形插值。

Z’ = ( 1 – alpha)Zf + alpha * Zc

         一般情况下,采样点位于较粗糙网格的边缘,Zc则通过对较粗糙层次边缘两个采样点的均值来计算。我们可以在运行时来完成这一步计算,但这将会导致3次顶点纹理查找(Zf一次,Zc = Zc1 + Zc2/2 两次),而对于现在的显卡来说,读取顶点纹理的代价还是很大的。

         因此,我们把计算Zc作为更新clipmap的一部分来实现,把ZfZc打包为同一个单通道的浮点纹理(pack both Zf and Zc into the same 1-channel floation-point texture)。我们把Zf作为浮点数的整数部分,而差值Zd = Zc – Zf则保存为小数部分。这个打包的步骤将在pixar shaderupsampling的时候完成(详见1.4.1)。

         以下是实现clipmap渲染的vertex shader的部分HLSL代码:

struct OUTPUT

{

         vector pos           : POSITON;

         float2 uv              : TEXCOORD0; //coordinates for normal-map lookup

         float  z               : TEXCOORD1; //coordinates for elevation-map lookup

         float alpha           : TEXCOORD2; //transition blend on normal map

};

 

uniform float4 ScaleFactor, FineBlockOrig;

uniform float2 ViewerPos,AlphaOffse,OneOverWidth;

uniform float  zScaleFactor,zTexScaleFactor;

uniform matrix WorldWiewProjMatrix;

 

uniform sampler ElevationSampler = sampler_state            // fine level height sampler

{

    Texture   = (fineLevelTexture);

    MipFilter = None;

    MinFilter = Point;

    MagFilter = Point;

    AddressU  = Wrap;

    AddressV  = Wrap;

};

 

OUTPUT RenderVS(float2 gridPos: TEXCOORD0)

{

         OUTPUT output;

         //convert from grid xy to workld xy coordinates

         //ScaleFactor.xy:grid spacing of current level

         //ScaleFactor.zw:origin fo current block within world

         float2 worldPos = gridPos*ScaleFactor.xy + ScaleFactor.zw;

         //compute coordinates for vertex texture

         //FineBlockOrig.xy: 1/(w,h) of textrue

         //FineBlockOrig.zw: origin of block in texture

         float2 uv = gridPos * FineBlockOrig.xy + FineBlockOrig.zw;

         //sample the vertex texture

         float zf_zd = tex2Dlod(ElevationSampler, float4(uv, 0, 1));         float zf_zd = tex2Dlod(ElevationSampler,float4(uv,0,1));

         //unpack to obtain zf and zd = (zc-zf)

         //zf is elevation value in current(fine) level

         //zc is elevation value in coarser level

         float zf = floor(zf_zd);

         float zd = frac(zf_zd) * 512 - 256; // zd = zc - zf

         //computer alpha (transition parameter) and blend elevation

         float2 alpha = clamp((abs(worldPos - ViewerPos) - AlphaOffset)* OneOverWidth , 0, 1);

         alpha.x = max(alpha.x,alphal.y);

         float z = zf + alpha.x * zd;

         z = z * ZScaleFactor;

        

         output.uv = uv;

         output.z = z * ZtexScaleFactor;

         output.alpha = alpha.x;

         return output;

}

 

1.3.6 The Pixar Shader

         Pixar shader访问法线图(normal map)并对表面进行着色。我们让法线图的分辨率为几何地形的两倍,因为每个顶点一条法线效果是很模糊的。当更新clipmap时,我们将逐渐计算法线图(详见1.4.3)。

         为了平滑的着色过渡,着色器查找当前层次和下一个较粗糙层次的法线值,之后使用vertex shader中计算的alpha值进行混合。一般情况下,这将导致2次纹理查找。因此,我们把两个法线值以(Nx , Ny , Ncx , Ncy)的形式放到一张4通道,每通道8bit的纹理中,这里假设Nz = 1Ncz = 1。因此在读取之后需要重新归一化(renormalize)。

         所上的颜色通过读取一张基于z方向(z-based)的1D纹理贴图来获得。

         以下代码是实现clipmap renderingpixar shader的部分HLSL代码(UpsampleZcoarser函数详见附件):

//paramters uv,alpha,and z are interpolated form vertex shader.

//Two texture samplers have min amd mag filters set to linear:

//NormalMapSampler: 2D texture containing normal map

//ZBasedColorSampler: 1D texture containing elevation-based color

 

uniform float3 LightDirection;

 

//pixar shader for rendering the geometry clipmap

float4 RenderPS(float2 uv     : TEXCOORD0,

                                     float z                  : TEXCOORD1,

                                     float alpha  : TEXCOORD2) : COLOR

{

         float4 normal_fc = text2D(NormalMapSampler,uv);

         //normal_fc.xy contains normal at current(fine) level

         //normal_fc.zw contains normal at coarser level

         //blend normals using alpha computed in vertex shader

         float3 normal = float3((1-alpha) * normal_fc.xy + alpha * (normal_fc.zw),1);

         //unpack coordinates from[0,1] to [-1,1]range,and renormalize

         normal = normalize(normal * 2 -1);

         //computer simple diffuse lighting

         float s = clamp(dot(normal,LightDirection),0,1);

         //assign terrain color based on its elevation

         return s * tex1D(ZbasedColorSampler,z);

}

 

1.4 更新

         当观察者在场景中移动时,clipmap每层中的窗口都必须进行变换,以保证观察点位于窗口的中央,因此,我们必须有根据的来更新这些窗口。由于观察者的移动是连续的,因此每帧基本上只需要为窗口更新一个L形的区域。另外,对较粗糙的层次来说,相对于观察者移动的距离,它所改变的位置是成指数减少的(the relative motion of the viewer within the windows decreases exponentially at coarser levels),因此,很少需要进行更新。

         我们以从粗糙层到较好层的顺序来更新有效的clipmap层。还记得每个clipmap层都储存了两张纹理吗:一张单通道的浮点高度图,一张4通道,每通道8bit的法线图。在更新的过程中,我们通过使用pixel shader进行渲染的方法来对这些纹理区域进行修改。为了避免改变已有的数据,所有的访问操作都是以一种环形(toroidal)的方式来进行,如下图所示:

 

(红色为进行更新的区域,绿色箭头为观察者移动的方向)

我们使用环绕寻址(wraparound addressing)的方式来定位clipmap窗口在纹理图中的位置。在这种环形的访问方式下,L形的更新区域通常变为一个十字形的区域,如上图所示。通过渲染2个矩形来组成这个渲染区域。需要注意的是如果更新区域跨越了纹理的边界,则有可能需要34个矩形。

 

1.4.1 Upsampling

         我们使用一种插值细分(interpolatory subdivison)的方法,通过较混乱的层次来预测较好层次的几何体。这里将使用众所周知的四点插值细分曲线方法的张量积版本, mask weights( -1/16, 9/16,9/16,-1/16)We use the tensor-product version of the well-known four-point subdivision curve interpolant ,which has mask weights ( -1/16, 9/16,9/16,-1/16))。这种upsampling过滤器恰好可以达到我们所希望的C1平滑效果。

         根据采样点在网格中的位置(偶数-偶数,偶数-奇数,奇数-偶数,奇数-奇数),选则不同的mask。对一个偶数-偶数的像素来说,只需对纹理进行一次查找,因为采样是通过插值计算得来的;而对一个奇数-奇数的像素来说,则需要对纹理进行4x4次查找;另外的两种情况均需要4次查找。对于CPU来说,可以用一个if块来方便的选择所用的mask值。但是,分支语句对于pixar shader来说代价是很大的。因此,我们总是进行16次纹理查找,另外,通过对一张2x2纹理的查找来选择不同的mask值。

         以下是部分upsampling shaderHLSL代码实现:

//residualSampler: 2D texture containing residuals,which can

//be either decompressed data or synthesized noise

//p_uv:coordinates of the grid sample

uniform float2 Scale;

//pixel shader for updating the elevation map

float4 UpsamplePS(float2 p_uv : TEXCOORD0) : COLOR

{

         float2 uv = floor(p_uv);

         //the Updample function samples the coarser elevation map

         //using a linear interpolatory filter with 4x4 taps

         //(depending on the even/odd configuration of location uv,

         //it applies 1 of 4 possible masks)

         float z_predicted = Upsampler(uv);        //detail omitted here

         //add the residual to get the actual elevation

         float residual = tex2D(residualSampler,p_uv * Scale);

         float zf = z_predicted + residual;

         //zf should always be an integer,since ite gets packed

         //into the integer component of the floating-point texture

         zf = floor(zf);

         //compute zc byu linearlyu interploating the vertices of the

         //coarse-grid edge on which the sample p_uv lies

         float zc = ZCoarser(uv);

         float zd = zc - zf;

         //pack the signed difference zd into the fractional componnent

         float zf_zd = zd + (zd + 256)/512;

         return float4(zf_zd,0,0,0);

}

        

1.4.2 Residuals

         可以通过解压数据或合成的方法获得residuals值,并把它添加到使用upsampling方法获得的较粗糙的数据中。

         对于使用压缩数据的方法,我们通过图片压缩技术来对residuals数据进行编码。我们使用了一种称为PTC的有损(lossy)图片编码方式,因为它支持一种高效的region-of-interest解码(Malvar 2000)。通过CPU来进行解压缩,并且把结果储存为一张residual纹理图。

         而对另一种方法来说,我们使用不相关高斯噪声(uncorrelated Gaussian noise)(Fournier et al. 1982)作为residuals值,来合成地形的细节(fractal detail)。GPU读取一小张预先计算好的包含了高斯噪声的2D纹理来完成合成,这个过程执行起来是相当快的。另外,把纹理环绕起来,扩展为一张无限大的高斯噪声纹理,同时,对纹理坐标进行一点点放大,来消除规则的周期效果(we enable texture wrapping to extend the Gausian texture infinitely,and apply a small magnification to the texture coordinates to break the regular periodicity)。跨越不同层次的噪声叠加在一起,因此对地形产生的起伏效果不会显示出任何模式性,如下图所示:

 

 

 

1.4.3 法线图(Normal Map

         着色器使用当前层的高度图来为这一层更新法线图。计算两个grid-aligned的切线的叉积来获得当前法线。此外,还对较粗糙的层次进行一次纹查找,获得它的所有法线值。最后,把(Nx, Ny, Ncx, Ncy)打包为一张4通道的纹理。代码详见附件。

 

1.5 结果和讨论

         我们主要的数据是一张216000 * 93600的美国地形高度图,其水平分辨率为30,垂直分辨率为1。进行了100倍以上的压缩之后,只需要355MB的内存就可以容纳它。我们在P4 2.4G1G内存,NIVIDIA Geforce6800 GT的系统上,以1024 * 768的分辨率对地形进行了渲染。

渲染率:L=11,网格大小n=255,使用了裁减的情况下,获得了每秒130帧的效果,每秒渲染了6千万三角形。使用顶点纹理查找是系统的瓶颈,在移除了查找之后,渲染率提高到了185frames/sec (对n=127的情况来说,使用了顶点纹理也可以达到298 frames/sec.

                                                                 Previous Implementation                                  GPU-Based Implementation

Upsampling                                                               3ms                                                                    1.0ms

Decompression(on CPU)                                           8ms                                                                    8ms

Synthesis                                                                    3ms                                                                    ~0ms

Normal-Map Computation                                        11ms                                                                  0.6ms

更新率:下表显示了更新过程中每个步骤所需要的时间,这里所统计的时间是最坏情况即每次更新整个层所需的时间。在CPU上完成的解压缩显然是系统的瓶颈。

最终的帧数:对于使用解压方式的地形来说,当观察着移动时,帧数大约在87 frames/sec,对于使用合成方法的来说,则是120 frames/sec

 

1.6 总结和改进

         我们描述了一个使用GPU实现geometry clipmap的框架。几乎把所有显示几何图形的过程都使用GPU来完成,从而减少CPU的负载。

1.6.1 顶点纹理

         Geometry clipmap通过一种非常特别也是受限制的方法来使用vertex texture:实质上每个texels是以光栅扫描(raster-scan)的顺序来访问,和顶点是一种一一对应的关系。因此,我们希望以后的访问机制可以提高渲染效率。

1.6.1 取消法线图

         通过访问高度图中四个过滤之后的采样(filtered samplers)点,应该可以直接在pixel shader中计算法线。目前,顶点纹理查找只能使用32-bit的浮点图片,因此不能进行高效的双线形过滤(bilinear filtering.

1.6.3 Memory-free Terrain Synthesis

         GPU上合成地形细节是如此之快,因此我们可以考虑每一帧都重建clipmap层次,从而节省显存。我们分配2张纹理T1T2,然后在他们之间相互转换。把T1初始化为较粗糙的几何体作为合成过程的种子。首先,通过pixar shader的更新产生源纹理T1,接下来通过upsample和合成,创建一个较好的层次,并把它保存为T2。然后,把T1作为顶点纹理,渲染他所在的clipmap环。之后,交换两张纹理的规则并重复整个过程,直到所有层都完成合成以及渲染。最初的尝试是很有前途的,在L=9的时帧率约59 frames/sec

1.7 参考

 

 

        略

 

点击这里下载完整的PDF文档和源码^_^