LOD对于初学者来说可能会感觉到有些复杂,其实做起来很容易。
首先我们谈一下为什么要用LOD技术:
1. 大规模地形仿真中,经常会使用到高空视角,这时只应用剔除技术所减少的图元数是有限的。一个普普通通的地形的图元数都要以百万计算。现在的pc机在绘制这样数量级的图元时,无法达到实时处理(而仿真中最重要的就是保证实时性)。
2. 在图形绘制中,较远的图形在屏幕中所占的像素往往只占几个或者一个像素,此时单个图元的大小对整个显示效果影响不大,这为应用LOD技术提供了客观条件。
3 . 在图形绘制时,通常会有一大部分图元是显示在视野之外的,这些图元的现实占用了大量时间,而且是完全没有必要的。通用的技术是采用四叉树对地形进行管理,绘制时通过四叉树裁剪,去掉不必要显示的图元。
下面说明一下整个实现过程:
1. 构建四叉树:使用递归的方法把地形数据分块(地形数据为高度图),当达到指定块大小的时候停止递归,创建叶节点显示数据。
1
// 创建四叉树
2
void
3
Terrain::buildQuadTree()
4

{
5
if (!mData)
6
{
7
return;
8
}
9
//int width =64;
10
//int height=64;
11
int block = 512;
12
int width =0;
13
int height=0;
14
int tem = mWidth;
15
while(tem>0)
16
{
17
width +=block;
18
tem-=block;
19
}
20
tem = mHeight;
21
while(tem>0)
22
{
23
height +=block;
24
tem-=block;
25
}
26
27
//width =64;
28
//height=64;
29
mQuadTree = new QuadTree(createQuadTreeVertex(0,0,width,height));//根节点为第0层
30
31
mQuadTree->mSonNode[0] = buildQuadTree(0, 0, width/2, height/2, 1);
32
mQuadTree->mSonNode[1] = buildQuadTree(width/2, 0, width/2, height/2, 1);
33
mQuadTree->mSonNode[2] = buildQuadTree(width/2, height/2, width/2, height/2, 1);
34
mQuadTree->mSonNode[3] = buildQuadTree(0, height/2, width/2, height/2, 1);
35
}
36
37
QuadTree*
38
Terrain::buildQuadTree(GLint xx, GLint zz, GLint width, GLint height,int level)
39

{
40
//只要高和宽中有一个小于快大小,就认为是叶节点,构造显示列表
41
if ((width<STEP_SIZE)||(height<STEP_SIZE))
42
{
43
xMessageBox("构建四叉树出问题","buildQuadTree");
44
return NULL;
45
}
46
if (((width>=STEP_SIZE)&&(width<BLOCKSIZE+1))||((height>=STEP_SIZE)&&(height<BLOCKSIZE+1)))
47
{
48
int temStep = STEP_SIZE;
49
int nums[3];
50
for (int i = 0;i<3;i++)
51
{
52
53
int x, y, z;
54
STEP_SIZE = mStepSize[i];
55
nums[i] = width*height/(STEP_SIZE*STEP_SIZE);
56
if (nums[i]==0)
57
{
58
nums[i] =1;
59
}
60
mListName[i] = glGenLists(1);
61
glNewList(mListName[i],GL_COMPILE);
62
if(mRender) // 选择渲染模式
63
//glBegin( GL_QUADS ); // 渲染为四边形
64
glBegin( GL_TRIANGLES ); // 渲染为四边形
65
else
66
glBegin( GL_LINES ); // 渲染为直线
67
for (int X = xx;X<width+xx;X+= STEP_SIZE)
68
for(int Y = zz ; Y< height+zz ; Y += STEP_SIZE)
69
{
70
if (STEP_SIZE == mStepSize[0]) //最简单的一层不用处理边界
71
{
72
//判断边界情况
73
if ((X == xx)||(X+STEP_SIZE) >=(width+xx)||(Y == zz)||(Y + STEP_SIZE)>=( height+zz))
74
{
75
continue; //边界处另作处理
76
}
77
}
78
// 绘制(x,y)处的顶点 -----------0
79
// 获得(x,y,z)坐标
80
x = X;
81
z = Y;
82
y = Height(x,z );
83
x*= SCALE;
84
z*= SCALE;
85
SetVertex(x,y,z);
86
87
//绘制(x+1,y)处的顶点 -----------1
88
x =( X + STEP_SIZE);
89
z = Y;
90
if (x>(width+xx))
91
{
92
x = width+xx;
93
}
94
y = Height(x, z );
95
x *= SCALE;
96
z *= SCALE;
97
SetVertex(x,y,z);
98
99
// 绘制(x+1,y+1)处的顶点 -------------- 2
100
x =( X + STEP_SIZE);
101
z =( Y + STEP_SIZE);
102
if (x>(width+xx))
103
{
104
x = width+xx;
105
}
106
if (z>(height+zz))
107
{
108
z = height+zz;
109
}
110
y = Height(x, z );
111
x *= SCALE;
112
z *= SCALE ;
113
SetVertex(x,y,z);
114
115
116
//-----------------------------------------------------------------------------------------------
117
118
// 绘制(x,y)处的顶点 ---------------------0
119
// 获得(x,y,z)坐标
120
x = X;
121
z = Y;
122
y = Height(x,z );
123
x*= SCALE;
124
z*= SCALE;
125
SetVertex(x,y,z);
126
127
// 绘制(x+1,y+1)处的顶点 ---------------- 2
128
x =( X + STEP_SIZE);
129
z =( Y + STEP_SIZE);
130
if (x>(width+xx))
131
{
132
x = width+xx;
133
}
134
if (z>(height+zz))
135
{
136
z = height+zz;
137
}
138
y = Height(x, z );
139
x *= SCALE;
140
z *= SCALE ;
141
SetVertex(x,y,z);
142
143
// 绘制(x,y+1)处的顶点 -------------------3
144
x = X;
145
z =( Y + STEP_SIZE);
146
if (z>(height+zz))
147
{
148
z = height+zz;
149
}
150
y = Height(x,z );
151
x *= SCALE;
152
z *= SCALE ;
153
SetVertex(x,y,z);
154
}
155
glEnd();
156
glColor3f(1.0f, 1.0f, 1.0f); // 重置颜色
157
glEndList();
158
159
160
}
161
GLuint edgeList[16]; //存储边界的显示列表
162
buildEdge(xx,zz,width,height,edgeList);
163
//返回一个叶结点
164
return new QuadTree(createQuadTreeVertex(xx,zz,width,height),nums,mListName,edgeList,true);
165
}else
166
{
167
QuadTree* tem = new QuadTree(createQuadTreeVertex(xx,zz,width,height)); //非叶节点,显示列表为0
168
169
tem->mSonNode[0] = buildQuadTree( xx, zz, width/2, height/2, level+1);
170
tem->mSonNode[1] = buildQuadTree( xx+width/2, zz, width/2, height/2, level+1);
171
tem->mSonNode[2] = buildQuadTree( xx+width/2, zz+ height/2, width/2, height/2, level+1);
172
tem->mSonNode[3] = buildQuadTree( xx,zz+ height/2, width/2, height/2, level+1);
173
return tem;
174
}
175
}
代码写的比较难看,见笑O(∩_∩)O
2. 边界地方需要特别处理。(代码写的比较丢人,就不拿出来了)
3. 根据视点距离块的远近,对四叉树进行裁剪绘制
* 场景剔除,裁减掉不必要显示的面。
* 根据距离选择块的细节显示层次。
注意事项 :
1. 分块大小要适中,本文采用64x64大小。(过大的话页面错误率太高,过小的话内存占用量很大,而且显示效率会降低)
2. 细节层次之间尺寸最好是2倍关系,否则边界处不好处理(也可能是我没想出什么好方法)。
测试结果:
测试环境:WindowsXP + core2duo CPU 2.0G + 2.0G内存 + NVS 135M显卡
地形大小:2048 x 3096
测试结果:图元显示数平均在20000左右,FPS稳定在60。
附效果图:
线框模式下:

正常模式下:

说明:本文采用的都是早已很成熟的技术,没有什么个人独创,发出来也只是跟大家交流交流,欢迎大家
批评指正。