Terrain 读书笔记:Chapter 6

<Real Time 3D Terrain Engines Using C++ And DX9>

Chapter 6. Basic Terrain Geometry

终于一路读到了第六章,读过了这章以后,就应该可以会写简单的Terrain了吧,期待ing……


Height Maps as Terrain Input Data

地形,有高有低。如何能确定一个点的高低呢?最简单的方法是Height Map。

说白了,就是每个点都用一个值表示,这个值是输入进去的。这个值是一个高度,所以叫Height。一个点对应一个值,所以是Map。

这个Height Map通常是用Bitmap图片来输入的。怎么输入呢?用灰度图。白代表高,黑代表低。比如高度取值是[0,255],那么一个点对应图片上的一个像素,这个像素的灰度范围也是[0,255],简单又有效。

还有软件可以把真实的地形转成Bitmap的灰度图。


Procedural Height

如果Height Map不能满足需要的话,我们就应该尝试着用程序去生成高度。

最简单的方法是这样的:随机生成每一个点的height,然后再处理,直到地形看起来平滑为止。这个“看起来平滑”就是相邻的两个点的高度差在某一个阈值以内。

这种方法被Snook戏称为“猴子画法”。因为它看上去就如同让一只猴子去画,然后我们去修正。

还有很多简单而有效的方法可以可以做到直接生成平滑的地形。


Midpoint Displacement

中间点法是一个递归的方法。虽然也是随机算法,但是却可以生成平滑的地形(当roughness设置的恰当的时候)。

一个方的地形,四个角ABCD,他们的高度是随机生成的。有一个变量delta,初始是MaxHeight/2,在这里就是128了(因为地图高度范围是0-255)。

于是,在四个边的中点和正方形的中心这5个点的高度有如下计算公式:

Height = Base Value + [Offset]

Base Value是什么?就是相邻的两个角的高度平均值(中心是四个角的平均值)。

那Offset呢?Offset是随机的,范围就是[-delta,delta]。

然后,进入下一步。因为刚刚那5个点已经把大的方形分成了四个小的方形。那么接下来就递归去求每一个小的方形的内部的高度。计算方法同前(四个角也是已经确定了)。不过有一点值得注意的是,delta需要修正:

delta = delta * roughness

这个roughness是一个0-1之间的因子,就是粗糙因子。这个值约大,地形越粗糙。一般来说,取0.5就可以了。


Perlin Noise

Perlin噪声这个东西很久以前就听过了,这次居然在这里遇到了。这个东西是由Ken Perlin在1983年的时候发明的。由于Perlin噪声在图形学影响,Ken Perlin居然在1997年被发了一个奥斯卡奖,汗 -_-|||

对于Perlin噪声来说,可以适用于任何维度的空间,不过由于在这里Perlin噪声主要是用来生成纹理,所以就只讨论2D的了。

创建Perlin噪声的方法如下:

1)比如地形是一个n*n的Grid,那么每行就有n+1个vertex,一共有n+1列了。对于每一个vertex,我们在上面生成一个2D的随机向量。

这里有种很有效率的方法。首先,做个向量表,一共256个,均匀分布成一个圆周。然后随机从里面选一个就可以了。

2)然后,就把目标集中在每一个Grid的内部了。对于Grid内部的任意一个Pixel,四个Corner都有一个向量指向它,这样每个Corner上就有2个向量了。再对每一个Corner上的两个向量求点积,把这个值记作这个Corner的Height。(注意,并不是这个Corner所对应的Pixel的Height,这个Height仅仅是一个中间变量而已)

3)再把目标集中在左上角的那个指向Pixel的向量。那个可以说是这个Pixel相对于这个Grid的坐标了,记为x和y的话,那么现在需要计算一些量:

SX = 6 * x^5 - 15 * x^4 + 10 * x^3;
SY = 6 * y^5 - 15 * y^4 + 10 * y^3;

对于这个需要说明一下,最早Perlin的论文中提出的公式是 w = 3 * t^2 - 2 * t^3。这个公式运算起来要比现在所用的这个快,但是人工的痕迹很重。所以后来Perlin改进了这个公式,采用了现在的这个。如果想追求速度的话,可以换成快的这个。

4)成功就在不远处了。最后,只要做3个线性插值,就OK了(其中四个Corner的Height分别为h0 - h3):

avgX0 = h0 + (SX*(h2 - h0))
avgX1 = h1 + (SX*(h3 - h1))
result = avgX0  + (SY*(avgX1 - avgX0))

这个result当然就是最后的结果了,也就是这个点所对应的那个高度了。

具体的还是看书上的代码吧,一看即懂。另外,还有一种方法,就是用一个比率去缩放Texture,然后再把他们加起来,就可以得到新的Texture了,这样还可以降低人工的痕迹呢。


Processing Height Map Data

得把灰度图变成高度,才可以建立地形,也就是说,得先处理一下数据。

每个面都是三角形,每两个三角形组成一个Grid,再由Grid组成Terrain。

除了坐标外,还有一个法线的问题。对于每个面,很容易求得它的法线(任意两个边做叉积),那么vertex的法线呢?想让地形看起来平滑的话,就得把一个vertex周围的面的法线求一个平均值,这样,地形看起来就平滑了。

听说D3DX提供了一个函数把灰度图里面的法线都求出来,叫D3DXComputerNormalMap,要是真的的话那可太方便了。


Terrain Geometry Base Classes

主要就是两个类—— cTerrain 和 cTerrainSection。

cTerrain是由cTerrainSection拼起来的,这样做是为了便于管理。比如cTerrain是256x256的,而cTerrainSection是64x64的,那么一共就需要64个Section了。cTerrainSection继承于cSceneObjects的,所以可以放到render queue中,和其他物体一样可以进行空间管理。不过和cSceneObjects有区别的是,cTerrainSection并不储存IB和VB的信息,这些都是存在cTerrain里面的,cTerrainSection只是拿来用罢了。


Terrain Geometry Index Buffers

这里是用三角带(Triangle Strip)来作Index的。其中用到了一个小技巧,那就是退化三角形(degenerate triangle)。退化三角形是一种面积为0的三角形,它的三个顶点中有两个是同一个顶点。这个三角形是用来连接三角带的,这样就可以把行的三角带变成面的三角带了。

生成方法如下:比如要生成一个m*n的Grid,那么一共需要(m+1)*(n+1)个vertex。设一下:

xVerts = n + 1
yVerts = m + 1

然后需要计算一共有多少个三角带、每个要多少个索引和索引的总数:

Total_Strips = yVerts - 1
Total_Indexs_Per_Strip = xVerts * 2
Total_Indexes = (Total_Indexs_Per_Strip * Total_Strips) + (Total_Strips * 2) - 2

这里需要注意的是Total_Indexes,前面的Total_Indexs_Per_Strip * Total_Strips好理解,后面的那个其实应该是(Total_Strips - 1) * 2,这个就是退化三角形了,一行有2个退化三角形来连接。

初始化几个变量:

index,是一个数组,用来做index用的。
Start_Vert = 0
Line_Step,就是每行的Vertex的个数

然后:

for(j = 0; j < Total_Strips; ++j)
{
    Vert = Start_Vert;
    for(k = 0; k < xVerts; ++k)
    {
        *(index++) = Vert;
        *(index++) = Vert + Line_Step;
    }
    Start_Vert += Line_Step;

    // 加入退化三角形
    if(j + 1 < Total_Strips)
    {
        *(index++) = (Vert - 1) + Line_Step;
        *(index++) = Start_Vert;
    }
}

我没有写书中的xStep和yStep,书上说学ROAM的时候才会用到,先不管它……

后面的几节就都是相关的Code了。


这章是实现地形的基础,而且还学到了Perlin Noise,挺开心。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值