高度图地形读取与漫游

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

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

地形系统在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)


相关推荐

  • SpringApplication_一个阶段结束

    SpringApplication_一个阶段结束1、SpringApplication正常结束SpringBoot2.0为SpringApplication正常结束新引入了SpringApplicationRunListener的生命周期,即running(ConfigurableApplicationContext),该方法在Spring应用上下文中已准备,并且CommandLineRunner和ApplicationRunnerBean均已执行完毕。EventPublishingRunListener作为SpringApplicationRu

  • 积分上限函数_定积分的基本计算方法

    积分上限函数_定积分的基本计算方法设函数$f(x)$在区间$[a,b]$上可积,对任意的$x\in[a,b]$,做变上限积分$$\Phi(x)=\int_{a}^{x}f(t)dt$$这个积分称为函数$f(x

  • MySQL的索引类型及创建索引

    MySQL的索引类型及创建索引一、介绍MySQL目前主要有以下几种索引类型:1.普通索引2.唯一索引3.主键索引4.组合索引5.全文索引二、语法CREATETABLEtable_name[col_namedatatype][unique|fulltext][index|key][index_name](col_name[length])[asc|desc]1.unique|fulltext…

  • URL转发基础!_URL怎么用

    URL转发基础!_URL怎么用 1、什么是URL转发?所谓URL转发是当你访问一个域名时,将会自动跳转到您所指定的另一个网络地址(URL)。假设abc.com是您要访问的域名,则通过URL转发服务可以实现当访问http://www.abc.com时,自动转向访问另外一个URL,如:http://www.otherdomain.com/somedir/other.htm。URL转发服务尤其对于拥有一个主网站并同时拥有多个域

    2022年10月10日
  • 深度相机 结构光_结构光三维成像原理

    深度相机 结构光_结构光三维成像原理版权声明:本文为博主原创文章,未经博主允许不得转载。违者必究。 https://blog.csdn.net/electech6/article/details/78707839 &lt;/div&gt; &lt;linkrel="styles…

    2022年10月26日

发表回复

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

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