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