LOD對(duì)于初學(xué)者來(lái)說(shuō)可能會(huì)感覺(jué)到有些復(fù)雜,其實(shí)做起來(lái)很容易。
首先我們談一下為什么要用LOD技術(shù):
1. 大規(guī)模地形仿真中,經(jīng)常會(huì)使用到高空視角,這時(shí)只應(yīng)用剔除技術(shù)所減少的圖元數(shù)是有限的。一個(gè)普普通通的地形的圖元數(shù)都要以百萬(wàn)計(jì)算。現(xiàn)在的pc機(jī)在繪制這樣數(shù)量級(jí)的圖元時(shí),無(wú)法達(dá)到實(shí)時(shí)處理(而仿真中最重要的就是保證實(shí)時(shí)性)。
2. 在圖形繪制中,較遠(yuǎn)的圖形在屏幕中所占的像素往往只占幾個(gè)或者一個(gè)像素,此時(shí)單個(gè)圖元的大小對(duì)整個(gè)顯示效果影響不大,這為應(yīng)用LOD技術(shù)提供了客觀條件。
3 . 在圖形繪制時(shí),通常會(huì)有一大部分圖元是顯示在視野之外的,這些圖元的現(xiàn)實(shí)占用了大量時(shí)間,而且是完全沒(méi)有必要的。通用的技術(shù)是采用四叉樹(shù)對(duì)地形進(jìn)行管理,繪制時(shí)通過(guò)四叉樹(shù)裁剪,去掉不必要顯示的圖元。
下面說(shuō)明一下整個(gè)實(shí)現(xiàn)過(guò)程:
1. 構(gòu)建四叉樹(shù):使用遞歸的方法把地形數(shù)據(jù)分塊(地形數(shù)據(jù)為高度圖),當(dāng)達(dá)到指定塊大小的時(shí)候停止遞歸,創(chuàng)建葉節(jié)點(diǎn)顯示數(shù)據(jù)。
1
// 創(chuàng)建四叉樹(shù)
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));//根節(jié)點(diǎn)為第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
//只要高和寬中有一個(gè)小于快大小,就認(rèn)為是葉節(jié)點(diǎn),構(gòu)造顯示列表
41
if ((width<STEP_SIZE)||(height<STEP_SIZE))
42
{
43
xMessageBox("構(gòu)建四叉樹(shù)出問(wèn)題","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]) //最簡(jiǎn)單的一層不用處理邊界
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)處的頂點(diǎn) -----------0
79
// 獲得(x,y,z)坐標(biāo)
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)處的頂點(diǎn) -----------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)處的頂點(diǎn) -------------- 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)處的頂點(diǎn) ---------------------0
119
// 獲得(x,y,z)坐標(biāo)
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)處的頂點(diǎn) ---------------- 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)處的頂點(diǎn) -------------------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]; //存儲(chǔ)邊界的顯示列表
162
buildEdge(xx,zz,width,height,edgeList);
163
//返回一個(gè)葉結(jié)點(diǎn)
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)); //非葉節(jié)點(diǎn),顯示列表為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
}
代碼寫的比較難看,見(jiàn)笑O(∩_∩)O
2. 邊界地方需要特別處理。(代碼寫的比較丟人,就不拿出來(lái)了)
3. 根據(jù)視點(diǎn)距離塊的遠(yuǎn)近,對(duì)四叉樹(shù)進(jìn)行裁剪繪制
* 場(chǎng)景剔除,裁減掉不必要顯示的面。
* 根據(jù)距離選擇塊的細(xì)節(jié)顯示層次。
注意事項(xiàng) :
1. 分塊大小要適中,本文采用64x64大小。(過(guò)大的話頁(yè)面錯(cuò)誤率太高,過(guò)小的話內(nèi)存占用量很大,而且顯示效率會(huì)降低)
2. 細(xì)節(jié)層次之間尺寸最好是2倍關(guān)系,否則邊界處不好處理(也可能是我沒(méi)想出什么好方法)。
測(cè)試結(jié)果:
測(cè)試環(huán)境:WindowsXP + core2duo CPU 2.0G + 2.0G內(nèi)存 + NVS 135M顯卡
地形大小:2048 x 3096
測(cè)試結(jié)果:圖元顯示數(shù)平均在20000左右,F(xiàn)PS穩(wěn)定在60。
附效果圖:
線框模式下:

正常模式下:

說(shuō)明:本文采用的都是早已很成熟的技術(shù),沒(méi)有什么個(gè)人獨(dú)創(chuàng),發(fā)出來(lái)也只是跟大家交流交流,歡迎大家
批評(píng)指正。