之前的一篇文章实现的是完全基于四叉树的动态地形渲染,虽然感觉那种方案是最优美的方案,假设CPU和GPU速度上没有差别的话,那种方案应该是最佳的了。但是现实中CPU速度还是比GPU慢不少的,因此,参考了这篇文章:
http://nvidia.e-works.net.cn/document/200908/article8938_2.htm 并按其思路实现了分块的地形LOD算法,整体思路感觉比之前的完全动态地形渲染还要简单些。
1、文章中的裂缝消除部分提到了要把地形块分成5个部分,实际上只需要分成3个部分(中间独立部分、上或下,左或右)就可以了。
2、另外文章中提到的顶高度点渐变时的插值,好像需要实时频繁改变顶点数据,感觉对效率会影响很大,目前还不知道应该怎么处理好些,请教各位高手有没有具体实现思路。。。
3、还有文章提到的采用Triangle strip可以减少索引数目,也没有实现(偷懒),用四边形代替....
目前测试程序中该地形的块大小是33*33,总大小是1057*1057(即32*32个块),块距离lod比例是4,(即包围球半径 除以 中心到相机距离 再除以 4,如果值小于1就采用全分辨率,1--2是第2级分辨率.....直到一个块只用一个4边形表示的最低分辨率)
看起来效果好像还不如之前的完全动态LOD地形,可能还需要调整好每个块大小、距离比例等参数。
地形块之间的裂缝消除:
相关代码
1#pragma once
2
3#include "VertexBuffer.h"
4#include "SceneObject.h"
5#include <hash_map>
6#include "Shader.h"
7
8/**/////////////////////////////////////////////////////////////////////////// 9#include "TGAFile.h"
10/**/////////////////////////////////////////////////////////////////////////// 11
12namespace C_Y
13{
14 /**//*
15 每个地形块长度:2^n+1,每个地形块顶点数 (2^n+2)*(2^n+2)
16
17 地形块数量:2^m * 2^m
18
19 地形总顶点数满足:(2^n+1) * 2^m +1 的平方
20 */
21 class CY_GeoMipMapTerrain
22 {
23 struct CY_LodBlock
24 {
25 CY_VertexBuffer m_VertexBuffer; //顶点buffer
26
27 CY_Vector3f m_CenterPos; //中心位置
28 float m_BoundingRadius; //包围球半径
29 };
30 struct CY_QuadTreeNode
31 {
32 CY_LodBlock *m_LeafBlock; //叶结点
33 CY_QuadTreeNode *m_Down; //相邻的下结点
34 CY_QuadTreeNode *m_Right; //相邻的右结点
35
36 CY_QuadTreeNode *m_UpLeft; //子结点
37 CY_QuadTreeNode *m_UpRight;
38 CY_QuadTreeNode *m_DownLeft;
39 CY_QuadTreeNode *m_DownRight;
40
41 CY_Vector3f m_CenterPos; //中心位置
42 float m_BoundingRadius; //包围球半径
43
44 int m_StartX; //索引所有顶点中,起始的顶点位置X
45 int m_EndX;
46 int m_StartY;
47 int m_EndY;
48
49 int m_SelfLevel; //lod级别,负数表示不渲染,level越低越精细
50
51 CY_QuadTreeNode():m_LeafBlock(0),m_Down(0),m_Right(0),m_UpLeft(0),m_UpRight(0),m_DownLeft(0),m_DownRight(0){}
52 ~CY_QuadTreeNode()
53 {
54 if(m_LeafBlock)
55 delete m_LeafBlock;
56 if(m_UpLeft)
57 delete m_UpLeft;
58 if(m_UpRight)
59 delete m_UpRight;
60 if(m_DownRight)
61 delete m_DownRight;
62 if(m_DownLeft)
63 delete m_DownLeft;
64 }
65 void GenRender(CY_Camera*&cam,std::vector<CY_QuadTreeNode*>&renderlist,const int&maxlevel);
66 CY_Vector3f GetCenterPos(); //逐级获得中心点
67 float GetBoundingRadius(); //逐级获得包围球半径
68 };
69 //---------------------------------------------------------------------------------------------
70
71 int m_BlockSize; //每个块的边长是多少格,必须是 2^n+1
72 int m_BlockNum; //总的块数量的边长是多少块,宽和高一致,值必须是 2^m
73
74 int m_TotalSize; //总边长是多少个点(边上的顶点数),值必须是 m_BlockSize*m_BlockNum+1
75
76 CY_Vector3f m_UnitSize; //深、宽、高的比例
77 float m_DistanceRate; //Lod优化程度
78
79 CY_QuadTreeNode *m_Root; //根结点
80 CY_QuadTreeNode **m_AllLeaf; //所有叶节点,按照地形的排列进行排序
81
82 GLuint *m_LodBufferIndex; //所有lod下的顶点索引缓冲,
83 int *m_LodIndexCount; //缓冲的index数量
84 int m_LodBufferCount;
85
86 GLuint m_RightEdgeIndex; //右边缘的裂缝消除
87 unsigned *m_Right_1_Index;
88 unsigned *m_Right_Index;
89
90 GLuint m_DownEdgeIndex; //下边缘的裂缝消除
91 unsigned *m_Down_1_Index;
92 unsigned *m_Down_Index;
93
94 void InitIndexBuffer();
95 void InitNode(CY_QuadTreeNode *&currNode,int Blocklength,int startX,int startY,int endX,int endY,CTargaImage&image);//处理的node,node的大小,node的起始、结束索引的顶点
96 void InitLeaf(CY_LodBlock *&LeafBlock,int startX,int startY,int endX,int endY,CTargaImage&image);//处理叶子的block
97 void InitAdjLeaf();
98 public:
99 CY_GeoMipMapTerrain();
100 ~CY_GeoMipMapTerrain();
101
102 void LoadTerrain(const std::wstring&);
103 void Render(const CY_EffectPassKind&pass);
104 };
105}
1#include "GeoMipMapTerrain.h"
2#include "SceneManager.h"
3/**/////////////////////////////////////////////////////////////////////////// 4#include "TGAFile.h"
5#include "MaterialSystem.h"
6#include "LogManager.h"
7/**/////////////////////////////////////////////////////////////////////////// 8
9namespace C_Y
10{
11 void CY_GeoMipMapTerrain::CY_QuadTreeNode::GenRender(CY_Camera*&cam,std::vector<CY_QuadTreeNode*>&renderlist,const int&maxlevel)
12 {
13 if(m_LeafBlock)
14 {
15 m_LeafBlock->m_VertexBuffer.PrepareThisGeometryChunk();
16 m_SelfLevel=(cam->GetWorldPos()-m_CenterPos).QuickLength()/(m_BoundingRadius*4);
17 if(m_SelfLevel>=maxlevel)
18 m_SelfLevel=maxlevel-1;
19 renderlist.push_back(this);
20 }
21 else
22 {
23 if(cam->GetFrustum()->SphereInFrustum(m_UpLeft->m_CenterPos,m_UpLeft->m_BoundingRadius))
24 m_UpLeft->GenRender(cam,renderlist,maxlevel);
25 if(cam->GetFrustum()->SphereInFrustum(m_UpRight->m_CenterPos,m_UpRight->m_BoundingRadius))
26 m_UpRight->GenRender(cam,renderlist,maxlevel);
27 if(cam->GetFrustum()->SphereInFrustum(m_DownLeft->m_CenterPos,m_DownLeft->m_BoundingRadius))
28 m_DownLeft->GenRender(cam,renderlist,maxlevel);
29 if(cam->GetFrustum()->SphereInFrustum(m_DownRight->m_CenterPos,m_DownRight->m_BoundingRadius))
30 m_DownRight->GenRender(cam,renderlist,maxlevel);
31 }
32 }
33 CY_Vector3f CY_GeoMipMapTerrain::CY_QuadTreeNode::GetCenterPos()
34 {
35 if(m_LeafBlock)
36 {
37 m_CenterPos=m_LeafBlock->m_CenterPos;
38 return m_CenterPos;
39 }
40 else
41 {
42 m_CenterPos=(m_UpLeft->GetCenterPos()+m_DownLeft->GetCenterPos()+m_UpRight->GetCenterPos()+m_DownRight->GetCenterPos())/4;
43 return m_CenterPos;
44 }
45 }
46 float CY_GeoMipMapTerrain::CY_QuadTreeNode::GetBoundingRadius()
47 {
48 if(m_LeafBlock)
49 {
50 m_BoundingRadius=m_LeafBlock->m_BoundingRadius;
51 return m_BoundingRadius;
52 }
53 else
54 {
55 m_BoundingRadius=0;
56 float currr=(m_CenterPos-m_UpLeft->m_CenterPos).Length()+m_UpLeft->GetBoundingRadius();
57 if(m_BoundingRadius<currr)
58 m_BoundingRadius=currr;
59 currr=(m_CenterPos-m_UpRight->m_CenterPos).Length()+m_UpRight->GetBoundingRadius();
60 if(m_BoundingRadius<currr)
61 m_BoundingRadius=currr;
62 currr=(m_CenterPos-m_DownLeft->m_CenterPos).Length()+m_DownLeft->GetBoundingRadius();
63 if(m_BoundingRadius<currr)
64 m_BoundingRadius=currr;
65 currr=(m_CenterPos-m_DownRight->m_CenterPos).Length()+m_DownRight->GetBoundingRadius();
66 if(m_BoundingRadius<currr)
67 m_BoundingRadius=currr;
68 return m_BoundingRadius;
69 }
70 }
71 //-----------------------------------------------------------------------------------------------------------------------
72 CY_GeoMipMapTerrain::CY_GeoMipMapTerrain()
73 {
74 m_Root=0;
75 m_AllLeaf=0;
76 m_LodBufferIndex=0;
77 m_LodIndexCount=0;
78
79 m_Right_1_Index=0;
80 m_Right_Index=0;
81 m_Down_1_Index=0;
82 m_Down_Index=0;
83 }
84 CY_GeoMipMapTerrain::~CY_GeoMipMapTerrain()
85 {
86 if(m_Root)
87 delete m_Root;
88 if(m_AllLeaf)
89 delete []m_AllLeaf;
90 if(m_LodBufferIndex)
91 {
92 glDeleteBuffers(m_LodBufferCount,m_LodBufferIndex);
93 delete []m_LodBufferIndex;
94 }
95 if(m_LodIndexCount)
96 delete []m_LodIndexCount;
97 if(m_Right_1_Index)
98 delete []m_Right_1_Index;
99 if(m_Right_Index)
100 delete []m_Right_Index;
101 if(m_Down_1_Index)
102 delete []m_Down_1_Index;
103 if(m_Down_Index)
104 delete []m_Down_Index;
105 }
106
107 void CY_GeoMipMapTerrain::InitIndexBuffer()
108 {
109 m_LodBufferCount=GetPow2Num(m_BlockSize-1)+1;
110 m_LodBufferIndex=new GLuint[m_LodBufferCount];
111 m_LodIndexCount=new int[m_LodBufferCount];
112
113 unsigned *AllIndex=new unsigned[(m_BlockSize-1)*(m_BlockSize-1)*4];
114 int step;
115 const int lineLength=m_BlockSize+1;
116 glGenBuffers(m_LodBufferCount,m_LodBufferIndex);
117 for(int i=0;i<m_LodBufferCount;++i) //计算索引值
118 {
119 step=1<<(m_LodBufferCount-i-1);
120 int n=0;
121 for(int y=0;y+step<m_BlockSize;y+=step)
122 {
123 for(int x=0;x+step<m_BlockSize;x+=step)
124 {
125 if((y+step)*lineLength+step>=(m_BlockSize+1)*(m_BlockSize+1))
126 {
127 bool success;
128 success=false;
129 }
130 AllIndex[n++]=y*lineLength+x+step;
131 AllIndex[n++]=y*lineLength+x;
132
133 AllIndex[n++]=(y+step)*lineLength+x;
134 AllIndex[n++]=(y+step)*lineLength+step+x;
135
136 }
137 }
138 if(n!=(m_BlockSize-1)*(m_BlockSize-1)*4)
139 {
140 bool success;
141 success=false;
142 }
143 m_LodIndexCount[m_LodBufferCount-1-i]=n;
144 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_LodBufferIndex[m_LodBufferCount-1-i]);//最高精度的地形从0起
145 glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned)*n,AllIndex,GL_STATIC_DRAW);
146 }
147 delete []AllIndex;
148 }
149 void CY_GeoMipMapTerrain::InitNode(CY_QuadTreeNode *&currNode,int Blocklength,int startX,int startY,int endX,int endY,CTargaImage&image)
150 {
151 currNode=new CY_QuadTreeNode();
152 currNode->m_StartX=startX;
153 currNode->m_StartY=startY;
154 currNode->m_EndX=endX;
155 currNode->m_EndY=endY;
156 if(Blocklength>1)
157 {
158 InitNode(currNode->m_UpLeft,Blocklength/2,startX,startY,(startX+endX)/2,(startY+endY)/2,image);
159 InitNode(currNode->m_UpRight,Blocklength/2,(startX+endX)/2,startY,endX,(startY+endY)/2,image);
160 InitNode(currNode->m_DownLeft,Blocklength/2,startX,(startY+endY)/2,(startX+endX)/2,endY,image);
161 InitNode(currNode->m_DownRight,Blocklength/2,(startX+endX)/2,(startY+endY)/2,endX,endY,image);
162 }
163 else
164 {
165 InitLeaf(currNode->m_LeafBlock,startX,startY,endX,endY,image);
166 int index=startX/m_BlockSize+startY/m_BlockSize*m_BlockNum;
167 if(index>m_BlockNum*m_BlockNum)
168 bool success=false;
169 m_AllLeaf[index]=currNode;
170 }
171 }
172
173 void CY_GeoMipMapTerrain::InitLeaf(CY_LodBlock *&LeafBlock,int startX,int startY,int endX,int endY,CTargaImage&image)
174 {
175 bool success=(endX-startX)==m_BlockSize;
176 success=(endY-startY)==m_BlockSize;
177
178 LeafBlock=new CY_LodBlock();
179
180 LeafBlock->m_VertexBuffer.SetUsableVerAttr(CY_VER_ATT_POS);
181 LeafBlock->m_VertexBuffer.CreateBuffer((m_BlockSize+1)*(m_BlockSize+1));
182
183 int n=0,temp,tmpindex;
184 CY_Vector3f TotalPos(0,0,0);
185 CY_Vector3f tempPos;
186 for(int y=startY;y<=endY;++y)
187 {
188 for(int x=startX;x<=endX;++x)
189 {
190 tmpindex=x+y*m_TotalSize; //当前点在图中的索引
191 temp=image.GetImage()[tmpindex*4+3]; //点alpha
192
193 tempPos.Set(x*m_UnitSize.x,temp*m_UnitSize.z,y*m_UnitSize.y);
194 TotalPos+=tempPos;
195 LeafBlock->m_VertexBuffer.GetVertex(n)->m_Pos=tempPos;
196 ++n;
197 }
198 }
199 success=n==LeafBlock->m_VertexBuffer.GetVertexCount();
200 LeafBlock->m_VertexBuffer.GenVertexBuffer();
201
202 LeafBlock->m_CenterPos=TotalPos/n; //得到这个地形块的中点
203
204 float r=0;
205 LeafBlock->m_BoundingRadius=0;
206 for(int i=0;i<n;++i) //得到这个地形块的包围球半径
207 {
208 r=(LeafBlock->m_VertexBuffer.GetVertex(i)->m_Pos - LeafBlock->m_CenterPos).Length();
209 if(r>LeafBlock->m_BoundingRadius)
210 LeafBlock->m_BoundingRadius=r;
211 }
212 }
213 void CY_GeoMipMapTerrain::InitAdjLeaf()
214 {
215 int n=m_BlockNum*m_BlockNum;
216 for(int i=0;i<n;++i)
217 {
218 if(i%m_BlockNum!=m_BlockNum-1)
219 m_AllLeaf[i]->m_Right=m_AllLeaf[i+1];
220 if(i/m_BlockNum<m_BlockNum-1)
221 m_AllLeaf[i]->m_Down=m_AllLeaf[i+m_BlockNum];
222 }
223 }
224
225 void CY_GeoMipMapTerrain::LoadTerrain(const std::wstring&file)
226 {
227 CTargaImage image;
228 image.Load(file);
229 //-----------------------------------设置参数
230 m_BlockSize=33;
231 m_UnitSize.Set(1.0f,1.0f,0.3f);
232 m_DistanceRate=3.0f;
233
234 m_BlockNum=(image.GetWidth()-1)/m_BlockSize;
235 m_AllLeaf=new CY_QuadTreeNode*[m_BlockNum*m_BlockNum];
236
237 m_TotalSize=image.GetWidth();
238 //------------------------------------产生tree结构和block
239 InitNode(m_Root,m_BlockNum,0,0,m_TotalSize-1,m_TotalSize-1,image);
240 //------------------------------------产生block的右、下近邻block
241 InitAdjLeaf();
242 //------------------------------------产生索引缓冲
243 InitIndexBuffer();
244
245 int i=0;
246 m_Right_1_Index=new unsigned[m_BlockSize];
247 for(;i<m_BlockSize;++i) //右内边缘的点
248 {
249 int temp=(m_BlockSize+1)*i+(m_BlockSize-1);
250 m_Right_1_Index[i]=temp;
251 }
252
253 m_Right_Index=new unsigned[m_BlockSize+1];
254 for(i=0;i<=m_BlockSize;++i) //右外边缘的点
255 {
256 int temp=(m_BlockSize+1)*(i+1)-1;
257 m_Right_Index[i]=temp;
258 }
259
260 m_Down_1_Index=new unsigned[m_BlockSize];
261 for(i=0;i<m_BlockSize;++i) //下内边缘的点
262 m_Down_1_Index[i]=(m_BlockSize+1)*(m_BlockSize-1)+i;
263
264 m_Down_Index=new unsigned[m_BlockSize+1];
265 for(i=0;i<=m_BlockSize;++i) //下外边缘的点
266 m_Down_Index[i]=m_BlockSize*(m_BlockSize+1)+i;
267
268 int edgesize=6*(m_BlockSize-1)+3;
269 unsigned *temp=new unsigned[edgesize];
270 for(i=0;i<edgesize;++i)
271 temp[i]=0;
272
273 temp[0]=m_BlockSize*m_BlockSize+m_BlockSize-1;
274 temp[1]=m_BlockSize*m_BlockSize+m_BlockSize-2;
275 temp[2]=(m_BlockSize+1)*(m_BlockSize+1)-1;
276
277
278 glGenBuffers(1,&m_RightEdgeIndex);
279 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_RightEdgeIndex);
280 glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned)*edgesize,temp,GL_STREAM_DRAW);//设置了初始大小,最右下的三角形索引
281
282
283 temp[0]=m_BlockSize*m_BlockSize+m_BlockSize-2;
284 temp[1]=(m_BlockSize+1)*(m_BlockSize+1)-1;
285 temp[2]=(m_BlockSize+1)*(m_BlockSize+1)-2;
286
287 glGenBuffers(1,&m_DownEdgeIndex);
288 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_DownEdgeIndex);
289 glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned)*edgesize,temp,GL_STREAM_DRAW);//设置了最右下的三角形索引
290 //------------------------------------产生包围球
291 m_Root->GetCenterPos();
292 m_Root->GetBoundingRadius();
293 }
294 void CY_GeoMipMapTerrain::Render(const CY_EffectPassKind&pass)
295 {
296 /**///////////////////////////////////////////////////////////////////////////
297 CY_EffectBase *e=CY_MaterialSystem::GetInstance()->GetEffectByName("terrainEff");
298 if(e)
299 e->PrepareEffectChunk(pass);
300
301
302 CY_Camera *camera=CY_SceneManager::GetInstance()->GetActiveCamera();
303
304 int i=0,temp;
305 for(;i<m_BlockNum*m_BlockNum;++i)
306 m_AllLeaf[i]->m_SelfLevel=-1;
307
308 std::vector<CY_QuadTreeNode*> AllRenderBlock;
309 m_Root->GenRender(camera,AllRenderBlock,m_LodBufferCount);
310
311 for(i=0;i<(int)AllRenderBlock.size();++i)
312 {
313 AllRenderBlock[i]->m_LeafBlock->m_VertexBuffer.PrepareThisGeometryChunk();
314 temp=AllRenderBlock[i]->m_SelfLevel;
315 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_LodBufferIndex[temp]);
316 glDrawElements(GL_QUADS,m_LodIndexCount[temp],GL_UNSIGNED_INT,0);
317
318
319 unsigned *tempIndex=new unsigned[6*(m_BlockSize-1)];
320 int r1step=(m_BlockSize-1)>>(m_LodBufferCount-1-AllRenderBlock[i]->m_SelfLevel);
321 //消除右裂缝
322 if(AllRenderBlock[i]->m_Right &&AllRenderBlock[i]->m_Right->m_SelfLevel>=0)
323 {
324 int n=0;
325 int rstep=(m_BlockSize-1)>>(m_LodBufferCount-1-AllRenderBlock[i]->m_Right->m_SelfLevel);
326
327 int currR1=0,currR=0;
328 while(currR<m_BlockSize-1 || currR1<m_BlockSize-1)
329 {
330 if(currR<currR1)
331 {
332 tempIndex[n++]=m_Right_Index[currR];
333 tempIndex[n++]=m_Right_1_Index[currR1];
334 currR+=rstep;
335 tempIndex[n++]=m_Right_Index[currR];
336 }
337 else
338 {
339 tempIndex[n++]=m_Right_Index[currR];
340 tempIndex[n++]=m_Right_1_Index[currR1];
341 currR1+=r1step;
342 tempIndex[n++]=m_Right_1_Index[currR1];
343 }
344 }
345
346 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_RightEdgeIndex);
347 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned)*3,sizeof(unsigned)*n,tempIndex);
348 glDrawElements(GL_TRIANGLES,3+n,GL_UNSIGNED_INT,0);
349 }
350 //消除下裂缝
351 if(AllRenderBlock[i]->m_Down && AllRenderBlock[i]->m_Down->m_SelfLevel>=0)
352 {
353 int n=0;
354 int rstep=(m_BlockSize-1)>>(m_LodBufferCount-1-AllRenderBlock[i]->m_Down->m_SelfLevel);
355
356 int currR1=0,currR=0;
357 while(currR<m_BlockSize-1 || currR1<m_BlockSize-1)
358 {
359 if(currR<currR1)
360 {
361 tempIndex[n++]=m_Down_1_Index[currR1];
362 tempIndex[n++]=m_Down_Index[currR];
363 currR+=rstep;
364 tempIndex[n++]=m_Down_Index[currR];
365 }
366 else
367 {
368 tempIndex[n++]=m_Down_1_Index[currR1];
369 tempIndex[n++]=m_Down_Index[currR];
370 currR1+=r1step;
371 tempIndex[n++]=m_Down_1_Index[currR1];
372 }
373 }
374
375
376 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,m_DownEdgeIndex);
377 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER,sizeof(unsigned)*3,sizeof(unsigned)*n,tempIndex);
378 glDrawElements(GL_TRIANGLES,3+n,GL_UNSIGNED_INT,0);
379 }
380 delete []tempIndex;
381 }
382
383 /**///////////////////////////////////////////////////////////////////////////
384 }
385};
posted on 2010-02-09 19:24
陈昱(CY) 阅读(1997)
评论(0) 编辑 收藏 引用 所属分类:
C++ 、
游戏编程 、
图形学