LOD地形设计与实现
地形从一开始研究,到实现,分了好多个版本,重构了好多次……主要问题在于对LOD数据结构的不熟悉和对索引用法的不熟悉。经过一系列的研究与学习,总结了如下实现LOD的方法:
#动态填充索引——在裁剪过程中求出被渲染的地形块。
#分块实现地形,每块有一定的渲染格。
下面一步步写出实现过程。
1、定义数据结构,主要是地义地形的块并实现四叉树。
2、写好VertexBuffer和IndexBuffer,主要是一边渲染一边填充IndexBuffer,根据裁剪出来的地形块来填充。
3、写好LOD算法,主要是实现LOD的计算方法。
4、写好裂缝修补算法。花在该项时间最长最麻烦。
5、贴纹理写shader。
地形block数据结构如下:
class CTerrainBlock
{
public:
private:
float m_fCellSpacing; //地形网格长度
float m_fSphereHalf; //裁剪的半径
float m_fDeltaH; //高度差
int m_nLODLevel; //LOD层数
vector<CTerrainCell*> m_Child; //子结点
int m_dwSeq; //索引号,左上角点为索引
int m_nRealCell; //实际每行的渲染格数,根据LOD层数定
DWORD m_dwCellsPerRender; //每个渲染块的最小格数
vector<DWORD> m_vtCorner; //四个角的索引,顺序为从左上角开始,顺时针
int m_nLeftIndexCount; //每个渲染格增加的顶点数,一般是1或3
int m_nRightIndexCount;
int m_nBottomIndexCount;
int m_nTopIndexCount;
int m_nNeighbor[4]; //分别是上、右、下、左,必须是相同一层的
}
地形渲染的流程如下:
首先用一个递归函数计算出要渲染的block并计算出其LOD值,代码如下:
oid CTerrain::DrawTerrain(CTerrainBlock* pNode)
{
if (pNode == NULL)
{
return;
}
if (结点是要渲染的块)
{
if (在视锥体内) //判断是否在视锥体内
{
for (int i = 0; i < 4; i++)
{
DrawTerrain(pNode->GetChildNode(i));
}
}
}
else //最小格,直接插入队列
{
计算LOD值;
m_vtDraw.push_back(pNode);
}
}
整个渲染代码如下:
void CTerrain::Render()
{
DrawTerrain(m_pRoot);
修补裂缝;
填充索引;
设置shader;
设置顶点缓冲;
设置纹理;
DrawIndexedPrimitive();
}