高度图地形读取与漫游

高度图地形读取与漫游高度图的读取渲染以及漫游

大家好,又见面了,我是你们的朋友全栈君。

地形系统在3d程序中是一个重要的部分,这里介绍一下我正在使用的一个简单的地形类.地形数据可以保存在一张灰度图里面,所谓的灰度图就是一张只有黑色和白色的图片,使用颜色深度代表数据大小.我们可以读取出图片上每个像素的颜色值作为地图中某个位置的高度,下面是地形网格投影在平面上的样子


高度图地形读取与漫游


嗯,也可以用三角形网格组织,我的地形类用的就是三角面.


看一下加载数据的方法:

Terrain::Terrain(const char* strName,BYTE* pHeightMap) {
        int nSize=MAP_SIZE * MAP_SIZE;//地图大小平方
        FILE *pFile = NULL;
        pFile = fopen( strName, "rb" );

        if ( pFile == NULL )
                return;

        fread( pHeightMap, 1, nSize, pFile );
        int result = ferror( pFile );
        if (result)
                return;

        fclose(pFile);

        vertice=0;
        normal=1;
        texcood=2;
        vbos=new GLuint[3];

        int nums=6*(MAP_SIZE-STEP_SIZE)*(MAP_SIZE-STEP_SIZE)/(STEP_SIZE*STEP_SIZE);
        vertics=new GLfloat[nums*3];
        normals=new GLfloat[nums*3];
        texCoods=new GLfloat[nums*2];

        planarNum=2*(MAP_SIZE-STEP_SIZE)*(MAP_SIZE-STEP_SIZE)/(STEP_SIZE*STEP_SIZE);
        terrainPlanar=new Planar*[planarNum];//保存网格平面方程
}

其中的terrainPlanar用于储存平面信息,这些信息将会用于之后的地形漫游方法中.


接着用读取的高度数据构造地形网格:
float Terrain::getHeight(BYTE *pHeightMap, int px, int pz) {
        int x = px % MAP_SIZE;                                                                // Error Check Our x Value
        int z = pz % MAP_SIZE;                                                                // Error Check Our y Value

        if(!pHeightMap)
                return 0;                                                        // Make Sure Our Data Is Valid

        float y=0;
        if(px>=0&&pz>=0)
                y=pHeightMap[x + (z * MAP_SIZE)];
        return y;                                // Index Into Our Height Array And Return The Height
}

void Terrain::getNormal(GLfloat p1[3],GLfloat p2[3],GLfloat p3[3],GLfloat* result) {
        const GLfloat l1[] = {p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]};
        const GLfloat l2[] = {p3[0]-p1[0], p3[1]-p1[1], p3[2]-p1[2]};
        GLfloat n[] = {
                l1[1]*l2[2] - l1[2]*l2[1],
                l1[2]*l2[0] - l1[0]*l2[2],
                l1[0]*l2[1] - l1[1]*l2[0]
            };
        GLfloat abs = sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
        n[0] /= abs;
        n[1] /= abs;
        n[2] /= abs;
        result[0]=n[0]/2;
        result[1]=n[1]/2;
        result[2]=n[2]/2;
}

getHeight方法根据网格的xz坐标获取对应高度,getNormal方法根据平面三点求取法线


以下putTexCoord和putVertic方法则是把纹理坐标数据与定点数据分别放入对应的数组,之后就能把数组储存在顶点缓冲区

void Terrain::putTexCoord(int& curTexCoord,GLfloat u,GLfloat v) {
        texCoods[curTexCoord]=u;
        curTexCoord++;
        texCoods[curTexCoord]=v;
        curTexCoord++;
}

void Terrain::putVertic(int& curVert,GLfloat x,GLfloat y,GLfloat z) {
        vertics[curVert]=x;
        curVert++;
        vertics[curVert]=y;
        curVert++;
        vertics[curVert]=z;
        curVert++;
}

接着构造地形网格

        GLfloat x, y, z;
        GLfloat u,v;
        int curVert=0;
        int curNorm=0;
        int curTexCood=0;

        if(!pHeightMap)
                return;

        int curPlanar=0;

        for (int i = 0; i < (MAP_SIZE-STEP_SIZE); i += STEP_SIZE ) {
                for (int j = 0; j < (MAP_SIZE-STEP_SIZE); j += STEP_SIZE ) {

                        x = (GLfloat)i;
                        z = (GLfloat)j;
                        y = (GLfloat)getHeight(pHeightMap, x, z );
                        u=0.0f;
                        v=0.0f;
                        putNormal(curNorm,pHeightMap,x,y,z);
                        putTexCoord(curTexCood,u,v);
                        putVertic(curVert, x, y, z);

                        VECTOR3D p1(x,y,z);

                        x = (GLfloat)i;
                        z = (GLfloat)(j + STEP_SIZE) ;
                        y = (GLfloat)getHeight(pHeightMap, x, z );
                        u=0.0f;
                        v=1.0f;
                        putNormal(curNorm,pHeightMap,x,y,z);
                        putTexCoord(curTexCood,u,v);
                        putVertic(curVert, x, y, z);

                        VECTOR3D p2(x,y,z);

                        x = (GLfloat)(i + STEP_SIZE);
                        z = (GLfloat)(j + STEP_SIZE) ;
                        y = (GLfloat)getHeight(pHeightMap, x, z );
                        u=1.0f;
                        v=1.0f;
                        putNormal(curNorm,pHeightMap,x,y,z);
                        putTexCoord(curTexCood,u,v);
                        putVertic(curVert, x, y, z);

                        VECTOR3D p3(x,y,z);

                        x = (GLfloat)i;
                        z = (GLfloat)j;
                        y = (GLfloat)getHeight(pHeightMap, x, z );
                        u=0.0f;
                        v=0.0f;
                        putNormal(curNorm,pHeightMap,x,y,z);
                        putTexCoord(curTexCood,u,v);
                        putVertic(curVert, x, y, z);

                        VECTOR3D p4(x,y,z);

                        x = (GLfloat)(i + STEP_SIZE);
                        z = (GLfloat)(j + STEP_SIZE) ;
                        y = (GLfloat)getHeight(pHeightMap, x, z );
                        u=1.0f;
                        v=1.0f;
                        putNormal(curNorm,pHeightMap,x,y,z);
                        putTexCoord(curTexCood,u,v);
                        putVertic(curVert, x, y, z);

                        VECTOR3D p5(x,y,z);

                        x = (GLfloat)(i + STEP_SIZE);
                        z = (GLfloat)j;
                        y = (GLfloat)getHeight(pHeightMap, x, z );
                        u=1.0f;
                        v=0.0f;
                        putNormal(curNorm,pHeightMap,x,y,z);
                        putTexCoord(curTexCood,u,v);
                        putVertic(curVert, x, y, z);

                        VECTOR3D p6(x,y,z);

                        Planar* planar=new Planar(p1,p2,p3);
                        terrainPlanar[curPlanar]=planar;
                        curPlanar++;

                        Planar* planarx=new Planar(p4,p5,p6);
                        terrainPlanar[curPlanar]=planarx;
                        curPlanar++;
                }
        }

此方法用于构造地形网格数据以及网格平面数据,其中的MAP_SIZE是地图的宽度与高度,STEP_SIZE是每个网格在xz平面上的纵向与横向间隔大小.


此方法计算网格平面的法线,注意一个顶点并不是只属于一个平面,而是属于临近的6个平面,因此一个顶点将会计算出6条法线

void Terrain::putNormal(int& curNorm,BYTE* pHeightMap,GLfloat x,GLfloat y,GLfloat z) {
        GLfloat v1[]={0,0,0};
        GLfloat v2[]={0,0,0};
        GLfloat v3[]={0,0,0};
        GLfloat v4[]={0,0,0};
        GLfloat v5[]={0,0,0};
        GLfloat v6[]={0,0,0};
        GLfloat nf[]={0,0,0};

        GLfloat c1[]={x,y,z};
        GLfloat u1[]={x,getHeight(pHeightMap,x,z+STEP_SIZE),z+STEP_SIZE};
        GLfloat r1[]={x+STEP_SIZE,getHeight(pHeightMap,x+STEP_SIZE,z),z};
        GLfloat d1[]={x,getHeight(pHeightMap,x,z-STEP_SIZE),z-STEP_SIZE};
        GLfloat l1[]={x-STEP_SIZE,getHeight(pHeightMap,x-STEP_SIZE,z),z};
        GLfloat dl1[]={x-STEP_SIZE,
                        getHeight(pHeightMap,x-STEP_SIZE,z-STEP_SIZE),z-STEP_SIZE};
        GLfloat ur1[]={x+STEP_SIZE,
                        getHeight(pHeightMap,x+STEP_SIZE,z+STEP_SIZE),z+STEP_SIZE};

        getNormal(c1,l1,u1,v1);
        getNormal(c1,ur1,r1,v2);
        getNormal(c1,r1,d1,v3);
        getNormal(c1,d1,dl1,v4);
        getNormal(c1,dl1,l1,v5);
        getNormal(c1,u1,ur1,v6);
        normalize(v1,v2,v3,v4,v5,v6,nf);

        normals[curNorm]=nf[0];
        curNorm++;
        normals[curNorm]=nf[1];
        curNorm++;
        normals[curNorm]=nf[2];
        curNorm++;
}


接着求法线,要
求出6条法线的平均值

void Terrain::normalize(GLfloat* v1,GLfloat* v2,GLfloat* v3,GLfloat* v4,
                GLfloat* v5,GLfloat* v6,GLfloat* result) {
        GLfloat x=v1[0]+v2[0]+v3[0]+v4[0]+v5[0]+v6[0];
        GLfloat y=v1[1]+v2[1]+v3[1]+v4[1]+v5[1]+v6[1];
        GLfloat z=v1[2]+v2[2]+v3[2]+v4[2]+v5[2]+v6[2];
        GLfloat l=sqrt(x*x+y*y+z*z);
        result[0]=x/l;
        result[1]=y/l;
        result[2]=z/l;
}


渲染所需要的数据计算完了,接着计算地形漫游.


所谓的地形漫游就是能够取得地形上任意点的位置,也就是输入任意的xz坐标即可求得y坐标.
float Terrain::calHeight(float cx,float cz,float scale,float scaleY) {
        float px=-MAP_SIZE/2+STEP_SIZE/2;
        float pz=-MAP_SIZE/2+STEP_SIZE/2;
        float cy=0;

        float posx=cx-px*scale;
        float posz=cz-pz*scale;
        int x=posx/(STEP_SIZE*scale);
        int z=posz/(STEP_SIZE*scale);
        int colNum=(MAP_SIZE-STEP_SIZE)/STEP_SIZE;
        int num=x*colNum+z;
        num*=2;

        float ux=cx/scale-px;
        float uz=cz/scale-pz;

        if(x>=0&&x<colNum&&z>=0&&z<colNum) {
                Planar* target=terrainPlanar[num];
                VECTOR3D p(ux,target->getY(ux,uz),uz);
                bool isIn=target->pointInPlanar(p);
                if(!isIn)
                        target=terrainPlanar[num+1];

                float uy=target->getY(ux,uz);

                float py=-MAP_SIZE/10;
                float ny=(uy+py)*scaleY;
                cy=-ny;
        }
        return cy;
}

其中cx与cz是输入的xz坐标值,scale是xz方向的缩放大小,scaleY是y方向的缩放大小,最终的结果即使地形上的高度值.

编译运行,结果就是这个样

高度图地形读取与漫游

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141544.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号