在阅读了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相反,如果此处顶点方向为逆时针,是看不到地形的,只有将视角移到背面,地形才可见。这时需要注意的地方。另外,一次性渲染的三角形面数是有限制的。解决方法是对三角形进行分组渲染,或者使用较小的地形数据。