在阅读了Introduction to 3D Game Programming with DirectX 9.0后,我试着实现一个简易的3D场景。阅读这本的细节就不多说了,这里回顾下实现3D场景的整个过程。
代码以书本十三章的代码Terrain为基础。辅以:摄像机类、D3D通用框架、公告牌、.x文件、道路、天空盒几个模块。
其中地形的部分代码还参考了Focus on terrain书的代码。
整个回顾过程大概分7块。基本的windows编程基础就不多说了。
一 地形
1.1地形网格确定
地形一般由三角形网格构成(D3D好像没有四边形),采用高度图来保存地形数据。顶点的x与z值相当于drw文件的坐标,而y(高度)相当于drw文件该坐标的value。根据drw点的数量可以构建N*N个顶点数组。
图1.1程序里用到的地形高度图,可以看到地形的基本轮廓。色彩越白,地势越高。
1.2、构建三角形的顶点及索引数据
地形由一个个三角形面构成,可利用顶点索引构建三角形,三角形从左往右构建。
A |----- | B
| / |
C |/___ | D
先构建顶点数据:
float uCoordIncrementSize = 1.0f / (float)_numCellsPerRow;
float vCoordIncrementSize = 1.0f / (float)_numCellsPerCol;
TerrainVertex* v = 0;
_vb->Lock(0, 0, (void**)&v, 0);
for(int i = 0; i <_numVertsPerRow; i++)
{
for(int j = 0; j < _numVertsPerCol; j++)
{
int index = i * _numVertsPerRow + j;
v[index] = TerrainVertex(
(float)j*_cellSpacing + startX,
(float)_heightmap[index],
(float)i*_cellSpacing + startZ,
0.0f,
1.0f,
0.0f,
(float)j * uCoordIncrementSize,
(float)i * vCoordIncrementSize);
}
}
_vb->Unlock();
再构建三角形索引:
WORD* indices = 0;
_ib->Lock(0, 0, (void**)&indices, 0);
int baseIndex = 0;
for(int i = 0; i < _numCellsPerCol; i++)
{
for(int j = 0; j < _numCellsPerRow; j++)
{
indices[baseIndex] = i * _numVertsPerRow + j;
indices[baseIndex + 1] = (i+1) * _numVertsPerRow + j;
indices[baseIndex + 2] = i * _numVertsPerRow + j +1;
indices[baseIndex + 3] = (i+1) * _numVertsPerRow + j;
indices[baseIndex + 4] = (i+1) * _numVertsPerRow + j + 1;
indices[baseIndex + 5] = i * _numVertsPerRow + j + 1;
baseIndex += 6;
}
}
_ib->Unlock();
1.3 渲染地形
在初始化时,需要载入一个纹理给地形使用。
渲染地形时,先设置渲染的纹理,再用DrawIndexedPrimitive函数渲染顶点索引。效果如下:
图1.2 使用Device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);可消去网格信息。
此图假设高度值都一样,所以效果为平原。
绘制三角形时需要注意,顶点的方向,D3D种顺时针方向为正,与OPENGL相反,如果此处顶点方向为逆时针,是看不到地形的,只有将视角移到背面,地形才可见。这时需要注意的地方。另外,一次性渲染的三角形面数是有限制的。解决方法是对三角形进行分组渲染,或者使用较小的地形数据。