游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3743|回复: 7

请教地形LOD的问题

[复制链接]

1

主题

19

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2004-3-6 21:08:00 | 显示全部楼层 |阅读模式
我正在读这个应用动态LOD四叉树算法的地形渲染程序,但是它每16个(4乘4)patch之间就会产生缝隙,就像下图:


线框模式的:

不知道这种现象是如何产生的?应该如何解决?
相应文章:http://3dbrothers.html.533.net/articles/quadtreelod.htm
源代码:
http://3dbrothers.html.533.net/articles/QuadTreeLOD.rar
[em7]

1

主题

19

帖子

19

积分

新手上路

Rank: 1

积分
19
 楼主| 发表于 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);
}

6

主题

33

帖子

33

积分

注册会员

Rank: 2

积分
33
发表于 2004-3-7 11:12:00 | 显示全部楼层

Re:请教地形LOD的问题

的确有此问题,我也发现了,请高手指教!

20

主题

473

帖子

502

积分

高级会员

Rank: 4

积分
502
发表于 2004-3-7 13:56:00 | 显示全部楼层

Re:请教地形LOD的问题

看任何一个terrain的教程都会说的,取消T型连接需要自己加一个顶点就OK

如果你看的代码连这个都没涉及,不看也罢

1

主题

19

帖子

19

积分

新手上路

Rank: 1

积分
19
 楼主| 发表于 2004-3-7 14:05:00 | 显示全部楼层

Re:请教地形LOD的问题

这个不是T型连接的问题,处理裂缝的问题这个程序已经解决了。
从上面的截图可以看到,我指的是大块地形之间存在一道整齐的间隙

18

主题

122

帖子

132

积分

注册会员

Rank: 2

积分
132
发表于 2004-3-8 10:33:00 | 显示全部楼层

Re:请教地形LOD的问题

用7级精算把    这里有那个文章  

89

主题

4036

帖子

4132

积分

论坛元老

Rank: 8Rank: 8

积分
4132
发表于 2004-3-9 17:36:00 | 显示全部楼层

Re:请教地形LOD的问题

Why?
我用的都是无级精度

0

主题

1

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2007-6-6 20:23:00 | 显示全部楼层

Re:请教地形LOD的问题

int精度问题,改float就行了
int CTerrain::RenderQuad(float nXCenter,float nZCenter,float nSize)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-26 05:40

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表