|
|

楼主 |
发表于 2004-3-6 21:13:00
|
显示全部楼层
Re: 请教地形LOD的问题
地形渲染部分的代码:
#include "Terrain.h"
#include "gamehead.h"
#include "Frustum.h"
#include "text.h"
#include <time.h>
#include <LIMITS.H>
CTerrain::CTerrain()
{
m_pHeightMap = 0;
m_pFrustum = 0;
m_nMapSize = 0;
m_pText = 0;
m_pCamera = 0;
m_nScale = 8;
}
CTerrain::~CTerrain()
{
if(m_pHeightMap)
delete []m_pHeightMap;
}
#define EQ(x,y) (fabs(x-y)<0.0001?true:false)
void CTerrain::SetTextureCoord(float x, float z)
{
float u = (float)x / (float)m_nMapSize;
float v = -(float)z / (float)m_nMapSize;
// Give OpenGL the current terrain texture coordinate for our height map
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u, v);
// Give OpenGL the current detail texture coordinate for our height map
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, u, v);
}
//===================读取高度图========================
void CTerrain: oadRawFile(LPSTR strName)
{
FILE *pFile = NULL;
pFile = fopen( strName, "rb" );
fseek(pFile,0,SEEK_END);
long nLen = ftell(pFile);
rewind(pFile);
m_nMapSize = sqrt(nLen);
if ( pFile == NULL )
{
MessageBox(NULL, "Can't find the height map!", "Error", MB_OK);
return;
}
if(m_pHeightMap)
delete []m_pHeightMap;
m_pHeightMap = new BYTE[m_nMapSize*m_nMapSize];
if(!m_pHeightMap)
{
MessageBox(NULL, "Alloc memory error!", "Error", MB_OK);
return;
}
int nRead = fread( m_pHeightMap, 1, m_nMapSize*m_nMapSize, pFile );
int result = ferror( pFile );
if (result)
{
MessageBox(NULL, "Can't get data!", "Error", MB_OK);
}
fclose(pFile);
m_cFlag.Create(m_nMapSize,m_nMapSize);
m_nMapSize = m_nMapSize*m_nScale-1;
m_cFlag.m_nSclae = m_nScale;
}
void CTerrain::LoadTexture(LPSTR strName)
{
m_txt.LoadGLTextures(strName);
}
int CTerrain::Height(int X, int Y)
{
// Make sure we don't go past our array size
int x = int(X/m_nScale) % int(m_nMapSize/m_nScale); // Error check our x value
int y = int(Y/m_nScale) % int(m_nMapSize/m_nScale); // Error check our y value
if(x<0||y<0||x>=m_nMapSize/m_nScale||y>=m_nMapSize/m_nScale)
return 0;
if(!m_pHeightMap) return 0; // Make sure our data is valid
// Use the equation: index = (x + (y * arrayWidth) ) to find the current height
return m_nScale/2*m_pHeightMap[x + int(y * m_nMapSize/m_nScale)]; // Index into our height array and return the height
}
void CTerrain::Init()
{
// m_nScale = 2;
LoadRawFile("terrain\\terrain.raw");
LoadTexture("terrain\\terrain.bmp");
m_cTxtDetail.LoadGLTextures("terrain\\detail.bmp");
}
void CTerrain::Render()
{
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_txt.GetTxtID());
// Activate the second texture ID and bind the fog texture to it
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
// Here we turn on the COMBINE properties and increase our RGB
// gamma for the detail texture. 2 seems to work just right.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
// Bind the detail texture
glBindTexture(GL_TEXTURE_2D, m_cTxtDetail.GetTxtID());
// Now we want to enter the texture matrix. This will allow us
// to change the tiling of the detail texture.
glMatrixMode(GL_TEXTURE);
// Reset the current matrix and apply our chosen scale value
glLoadIdentity();
glScalef((float)32, (float)32, (float)32);
// Leave the texture matrix and set us back in the model view matrix
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
m_cFlag.Reset(); //细分标记数组清0(这里为了节约内存,一个网格用一个bit表示)
Update((m_nMapSize-1)/2,(m_nMapSize-1)/2,(m_nMapSize-1)/2,1);//在细分标记数组记录需要细分的网格
//根据细分标记数组递归渲染网格
int nCount = RenderQuad((m_nMapSize-1)/2,(m_nMapSize-1)/2,(m_nMapSize-1)/2);
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
// Turn the first multitexture pass off
glActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
char sPrint[MAX_PATH];
wsprintf(sPrint,"%d Triangles",nCount);
if(m_pText)
m_pText->EditItem(m_nTextID,0.0f,3.0f,0.0f,sPrint);
}
//nquad
bool CTerrain::IsVisible(int nXCenter,int nZCenter,int nSize)
{
CVector3 v0(nXCenter,Height(nXCenter,nZCenter),nZCenter);
CVector3 v1(nXCenter-nSize,Height(nXCenter-nSize,nZCenter-nSize),nZCenter-nSize);
CVector3 v2(nXCenter+nSize,Height(nXCenter+nSize,nZCenter-nSize),nZCenter-nSize);
CVector3 v3(nXCenter+nSize,Height(nXCenter+nSize,nZCenter+nSize),nZCenter+nSize);
CVector3 v4(nXCenter-nSize,Height(nXCenter-nSize,nZCenter+nSize),nZCenter+nSize);
CVector3 v1_bak = v1;
v1 = CrossProduct(v2-v0,v1-v0);
v2 = CrossProduct(v3-v0,v2-v0);
v3 = CrossProduct(v4-v0,v3-v0);
v4 = CrossProduct(v1_bak-v0,v4-v0);
v1 = Normalize(v1);
v2 = Normalize(v2);
v3 = Normalize(v3);
v4 = Normalize(v4);
CVector3 vView(v0-m_pCamera->GetPos());
if(DotProduct(v1,vView)>0)
if(DotProduct(v2,vView)>0)
if(DotProduct(v3,vView)>0)
if(DotProduct(v4,vView)>0)
return false;
return true;
}
//渲染网格的递归程序
//参数:
// nXCenter: 网格中心点坐的X值
// nZCenter: 网格中心点坐的Y值
// nSize:网格大小
//返回:渲染的三角形数量
//使用前提:m_cFlag需要细分数组要先调用Update函数记录好.
int CTerrain::RenderQuad(int nXCenter,int nZCenter,int nSize)
{
int nCount = 0;
int nH0 = Height(nXCenter,nZCenter);
int nH1 = Height(nXCenter-nSize,nZCenter-nSize);
int nH2 = Height(nXCenter+nSize,nZCenter-nSize);
int nH3 = Height(nXCenter+nSize,nZCenter+nSize);
int nH4 = Height(nXCenter-nSize,nZCenter+nSize);
//求网格5个点中高度最大和最小值
int nMax = nH0,nMin = nH0;
if(nMax<nH1)nMax = nH1;
if(nMax<nH2)nMax = nH2;
if(nMax<nH3)nMax = nH3;
if(nMax<nH4)nMax = nH4;
if(nMin>nH1)nMin = nH1;
if(nMin>nH2)nMin = nH2;
if(nMin>nH3)nMin = nH3;
if(nMin>nH4)nMin = nH4;
SIZE size;
size.cx = nSize;size.cy=(nMax-nMin)/2;
//如果此网格不在视野内,返回
if(!m_pFrustum->CubeInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,max(size.cx,size.cy)))
return 0;
if(m_cFlag.IsTrue(nXCenter,nZCenter))//需要细分,递归渲染4个子网格
{
nCount += RenderQuad(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2);
nCount += RenderQuad(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2);
nCount += RenderQuad(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2);
nCount += RenderQuad(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2);
}
else//渲染此网格
{
glBegin( GL_TRIANGLE_FAN );
SetTextureCoord(nXCenter,nZCenter); // center
glVertex3f(nXCenter, nH0, nZCenter);
SetTextureCoord(nXCenter-nSize,nZCenter-nSize); //left top
glVertex3f(nXCenter-nSize,nH1 , nZCenter-nSize);
RemedyTop(nXCenter,nZCenter,nSize);
SetTextureCoord(nXCenter+nSize,nZCenter-nSize); //right top
glVertex3f(nXCenter+nSize, nH2, nZCenter-nSize);
RemedyRight(nXCenter,nZCenter,nSize);
SetTextureCoord(nXCenter+nSize,nZCenter+nSize); //right bottom
glVertex3f(nXCenter+nSize, nH3, nZCenter+nSize);
RemedyBottom(nXCenter,nZCenter,nSize);
SetTextureCoord(nXCenter-nSize,nZCenter+nSize);//left bottom
glVertex3f(nXCenter-nSize, nH4, nZCenter+nSize);
RemedyLeft(nXCenter,nZCenter,nSize);
SetTextureCoord(nXCenter-nSize,nZCenter-nSize); //left top
glVertex3f(nXCenter-nSize, nH1, nZCenter-nSize);
glEnd();
nCount += 4;//这里为求简单,计算渲染的三角形数目并不是十分准确,比实际要小些
}
return nCount;
}
//计算误差(坡度)的函数
//参数:
// nXCenter: 网格中心点坐的X值
// nZCenter: 网格中心点坐的Y值
// nSize:网格大小
//返回: 误差大小
//说明:只是计算某些点细分与不细分的高度值差的和
inline int CTerrain::CalcError(int nXCenter,int nZCenter,int nSize)
{
int nError = 0;
int nH0 = Height(nXCenter,nZCenter);
int nH1 = Height(nXCenter-nSize,nZCenter-nSize);
int nH2 = Height(nXCenter+nSize,nZCenter-nSize);
int nH3 = Height(nXCenter+nSize,nZCenter+nSize);
int nH4 = Height(nXCenter-nSize,nZCenter+nSize);
nError += abs(Height(nXCenter,nZCenter-nSize)-(nH1+nH2)/2);
nError += abs(Height(nXCenter+nSize,nZCenter)-(nH2+nH3)/2);
nError += abs(Height(nXCenter,nZCenter+nSize)-(nH3+nH4)/2);
nError += abs(Height(nXCenter-nSize,nZCenter)-(nH4+nH1)/2);
nError +=abs(Height(nXCenter-nSize/2,nZCenter-nSize/2)-(nH0+nH1)/2);
nError +=abs(Height(nXCenter+nSize/2,nZCenter-nSize/2)-(nH0+nH2)/2);
nError +=abs(Height(nXCenter+nSize/2,nZCenter+nSize/2)-(nH0+nH3)/2);
nError +=abs(Height(nXCenter-nSize/2,nZCenter+nSize/2)-(nH0+nH4)/2);
return nError;
}
//更新细分记录数组的递归函数
//参数:
// nXCenter: 网格中心点坐的X值
// nZCenter: 网格中心点坐的Y值
// nSize:网格大小
// nLevel:当前的细分等级(其实可由nSize计算出来)
void CTerrain::Update(int nXCenter,int nZCenter,int nSize,int nLevel)
{
CVector3 vPos = m_pCamera->GetPos();
// int nSize = (m_nMapSize-1)/pow(2,nLevel);//由细节级度得到当前渲染矩形的大小
//求网格5个点中高度最大和最小值
int nMax = Height(nXCenter,nZCenter);
int nMin = nMax;
int nH1 = Height(nXCenter-nSize,nZCenter-nSize);
int nH2 = Height(nXCenter+nSize,nZCenter-nSize);
int nH3 = Height(nXCenter+nSize,nZCenter+nSize);
int nH4 = Height(nXCenter-nSize,nZCenter+nSize);
if(nMax<nH1)nMax = nH1;
if(nMax<nH2)nMax = nH2;
if(nMax<nH3)nMax = nH3;
if(nMax<nH4)nMax = nH4;
if(nMin>nH1)nMin = nH1;
if(nMin>nH2)nMin = nH2;
if(nMin>nH3)nMin = nH3;
if(nMin>nH4)nMin = nH4;
//网格的中心点坐标
CVector3 vDst(nXCenter,(nMax+nMin)/2,nZCenter);
SIZE size;
size.cx = nSize;size.cy=(nMax-nMin)/2;
//如果此网格不在视野内,返回
if(!m_pFrustum->CubeInFrustum(nXCenter,(nMax+nMin)/2,nZCenter,max(size.cx,size.cy)))
return ;
//视点到网格中心点的距离
int nDist = Dist(vPos,vDst);
if(nDist>3000)//距离大于3000时才将距离和误差(坡度)2个因素考虑
{
//与距离成正比,与误差(坡度)成反比,还与当前细分等级有关
if(70.0*Dist(vPos,vDst)/nSize/CalcError(nXCenter,nZCenter,nSize)>nLevel)
return;//此网格不需要细分
}
else//近距离(<3000)时只考虑误差(坡度)因素(因为动态LOD会造成地表呼吸现象,为减弱此现象不考虑距离因素)
{
if(CalcError(nXCenter,nZCenter,nSize)<30)
return;//此网格不需要细分
}
if(nSize!=MIN_GRID)
{ ////此网格需要细分
m_cFlag.Set(nXCenter,nZCenter,true);//把细分数组相应位设为true
//递归4个子网格
Update(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2,nLevel+1);
Update(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2,nLevel+1);
Update(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2,nLevel+1);
Update(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2,nLevel+1);
}
//此网格不需要细分
}
//修补网格顶部裂缝的函数
//参数:
// nXCenter: 网格中心点坐的X值
// nZCenter: 网格中心点坐的Y值
// nSize:网格大小
void CTerrain::RemedyTop(int nXCenter,int nZCenter,int nSize)
{
if(nZCenter-2*nSize>=0)//保证寻址不越界
{
if(!m_cFlag.IsTrue(nXCenter,nZCenter-2*nSize))//此网格相邻的顶部网格(同级的)没有细分
return;
}
else
return;
//此网格相邻的顶部网格(同级的)已经细分
//对顶部一分为二递归(跨级精度的情况)
RemedyTop(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2);
SetTextureCoord(nXCenter,nZCenter-nSize);
glVertex3f(nXCenter,Height(nXCenter,nZCenter-nSize),nZCenter-nSize);
RemedyTop(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2);
}
void CTerrain::RemedyRight(int nXCenter,int nZCenter,int nSize)
{
if(nXCenter+2*nSize<m_nMapSize)
{
if(!m_cFlag.IsTrue(nXCenter+2*nSize,nZCenter))
return;
}
else
return;
RemedyRight(nXCenter+nSize/2,nZCenter-nSize/2,nSize/2);
SetTextureCoord(nXCenter+nSize,nZCenter);
glVertex3f(nXCenter+nSize,Height(nXCenter+nSize,nZCenter),nZCenter);
RemedyRight(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2);
}
void CTerrain::RemedyBottom(int nXCenter,int nZCenter,int nSize)
{
if(nZCenter+2*nSize<m_nMapSize)
{
if(!m_cFlag.IsTrue(nXCenter,nZCenter+2*nSize))
return;
}
else
return;
RemedyBottom(nXCenter+nSize/2,nZCenter+nSize/2,nSize/2);
SetTextureCoord(nXCenter,nZCenter+nSize);
glVertex3f(nXCenter,Height(nXCenter,nZCenter+nSize),nZCenter+nSize);
RemedyBottom(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2);
}
void CTerrain::RemedyLeft(int nXCenter,int nZCenter,int nSize)
{
if(nXCenter-2*nSize>=0)
{
if(!m_cFlag.IsTrue(nXCenter-2*nSize,nZCenter))
return;
}
else
return;
RemedyLeft(nXCenter-nSize/2,nZCenter+nSize/2,nSize/2);
SetTextureCoord(nXCenter-nSize,nZCenter);
glVertex3f(nXCenter-nSize,Height(nXCenter-nSize,nZCenter),nZCenter);
RemedyLeft(nXCenter-nSize/2,nZCenter-nSize/2,nSize/2);
} |
|