高度图
用高度图来描述地形中的丘陵和山谷,高度图其实就是一个数组,该数组每个元素都指定了地形方格中某一个特定顶点的高度值。通常将高度图视为一个矩阵,这样高度图中的元素就与地形栅格中的顶点一一对应。
高度图被保存在磁盘中,通常为其每个元素元素只分配一个字节存储空间,这样高度只能在区间
[0,255]内取值,但在实际应用中为了匹配3D世界的尺度,可能需要对高度值进行比例变换,这样就极可能超出上述区间。基于上述原因,当将高度数据加载到应用程序中时,我们重新分配一个整型或浮点型数组来存储这些高度值。
创建高度图
可由编程序生成、可用图像编辑软件来创建,一旦完成高度图的创建,我们需要将其保存为8位的RAW文件。RAW文件仅连续存储了图像中以字节为单位的每个像素的灰度值。这就使得这类文件的读取操作非常容易。您使用的图像编辑软件可能会询问您是否要为RAW文件增加一个文件头,请选择“否”.
加载RAW文件
RAW文件本质上是一个连续的字节存储块,其中将字节型向量复制到一个整型向量中,我们就可对高度值进行比例变换从而突破0~255的限制,该方法的唯一限制是所要读取的RAW文件中包含的字节数至少要与地形中的顶点总数一样多,所以,如果您要从一个256×256的RAW文件中读取数据,相应地您只能创建一个至多有256×256个顶点的地形。
bool Terrain::ReadRawFile(std::string fileName)
{
//RAW文件本质上是一个连续的字节存储块
//限制RAW文件尺寸必须>=地形的尺寸,一个128x128的RAW文件只能用于最多128x128的顶点
std::vector<BYTE> in(_numVertices);
std::ifstream inFile(fileName.c_str(), std::ios_base::binary);
if (!inFile)
return false;
inFile.read((char*)&in[0], in.size());
inFile.close();
_heightmap.resize(_numVertices);
for (int i = 0; i < in.size(); ++i)
_heightmap[i] = in[i];
return true;
}
创建地形几何信息
可通过指定每行和每列的顶点数以及单元间距(cell spacing)来定义地形的尺寸。这些值将作为Terrain类的构造函数的传入参数。此外,
我们对该类的构造函数还传入了与地形相关的设备指针、一个标识了存储高度图数据的文件的字符串,以及一个用于对高度图中各元素实施比例变换的高度比例因子
顶点的计算
为了计算三角形栅格的各顶点,我们只需自顶点start起,逐行生成每个顶点,保持相邻顶点的行列间隔均为单元间距(cell spacing),直至到达顶点end为止。这样就给出了x和z坐标的定义。y坐标则要查询所加载的高度图数据结构中的相应项。
为了计算纹理坐标,请参考图13.5,该图给出了一个简单的纹理图像,从中我们可以看出与地形中位于(i,j)的顶点相对应的纹理坐标(u,v)可由下述公式计算得到:
其中
索引的计算
为计算三角形栅格各顶点的索引,我们只需自图13.4的左上角起直至右下角,依次遍历每个方格,
并计算构成每个方格的三角面片的顶点索引。
计算的关键是推导出一个用于求出构成第i行、第j列的方格的两个面片的顶点索引的通用公式。借
助图13.6,我们可自行推导该公式,我们发现,对于处在(i,j)位置的方格
纹理映射
1.加载一个已创建好的纹理文件,然后再用于该纹理数据
2.创建一个“空”纹理,按顺序生成纹理数据,先用D3DXCreateTexture创建一个空纹理,然后再将顶层纹理锁定,自此开始遍历每个纹理元并对其上色。上色的依据是坐标方格所对应的近似高度。思路是:地形中海拔较低的部分上色为沙滩色,中等海拔的部分上色为绿色的丘陵颜色,高海拔的部分上色为雪山的颜色。我们用坐标方格中左上角顶点的高度值近似表示该方格的整体高度。
光照
为了给地形添加光照以增强真实感。由于前面已经计算好了地形纹理的颜色,现在我们只需计算地形中各部分在给定光源照射下应如何进行明暗调整的明暗因子(shade factor),不用Direct3D来添加光照而是自己手动计算。这样做主要是基于下述考虑:
- 手工计算由于无需存储顶点法向量,所以可以节省大量内存。
- 山于地形是静态的,而且光源一般也不发生移动,所以我们可以预先对光照进行计算,这样就节省了Direct3D实时照亮地形那部分计算时间。
- 手工计算方式使得我们获得了将相关数学知识付诸实践的机会,并有助于我们加深对基本的光照概念的理解以及熟悉Direct3D中的一些函数
我们在计算地形的明暗时用到的光照技术很基本也很常用,即漫射光光照(diffusing lighting)。给定
一个平行光源,我们用到达光源的方向(该光源发出的平行光的传播方向的反方向)来描述该平行光源。如果一组平行光线自空中沿着方向lightRaysDirection=(0,-1,0)向下照射,则到达光源的方向应与lightRaysDirection相反,即directionToLight=(0,1,0)。注意,光的方向向量应为单位向量。虽然指定光的出射方向好像更符合直觉,但指定到达光源的方向更适合漫射光光照的计算。
由图137可见,上述夹角越大,坐标方格的朝向偏离光源就越大,其所接收到的光照就越少。反之,上述夹角越小,坐标方格的朝向偏离光源就越小,