高度图地形读取与漫游

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

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

地形系统在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方向的缩放大小,最终的结果即使地形上的高度值.

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

高度图地形读取与漫游

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

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

(0)
上一篇 2022年5月25日 下午5:40
下一篇 2022年5月25日 下午5:40


相关推荐

  • 现代密码学概述_密码学概论

    现代密码学概述_密码学概论1、简述密码学与信息安全的关系密码学是信息安全的重要组成部分。伴随着网络的普及,计算机网络安全成为影响网络效能的重要问题,这就对网络的安全提出了更高的要求。一个安全的网络信息系统应当确保所传输信息的完整性、保密性、不可否认性等。目前保障通信和网络安全技术的种类很多,其中数据加密技术是保障信息安全的最核心的技术措施,信息加密也是现代密码学的主要组成部分。2、简述密码学发展的三个阶段及其主要特点a.古典密码阶段大约是指19世纪末以前的漫长时期,其基本特点是手工加密和解密。因此,该阶段也称为手工密码时代;

    2025年6月28日
    4
  • 安装 arm nginx aarch64[通俗易懂]

    安装 arm nginx aarch64[通俗易懂]网上搜了一大堆通过编译方式安装nginx的方法其实nginx提供了aarch64版本的nginxhttp://nginx.org/en/linux_packages.html2中选择,使用yum安装软件的选择centos的方法,apt安装软件的选择debain或者ubuntu的方法添加软件源地址,然后就可以快乐的玩耍了不管是yum还是apt安装后nginx都不会自己启动需要systemctlstartnginx手动启动nginx服务

    2022年10月16日
    3
  • OpenClaw 安装部署指南

    OpenClaw 安装部署指南

    2026年3月13日
    2
  • S3D算法详解

    S3D算法详解S3D 论文详解论文地址 RethinkingSp Speed AccuracyTrad offsinVideoC 代码地址 pytorch 非官方 https github com kylemin S3D 文章是谷歌发表于 ECCV2018 为了平衡视频理解算法精度和速度 提出了 S3D 这个视频理解网络 文章首先提出了下列问题 是否需要 3D 卷积 如果是 哪些层需要是 3D 卷积哪些可以是 2D 卷积 这些操作会

    2026年3月26日
    2
  • Kubernetes基础一[通俗易懂]

    Kubernetes基础一[通俗易懂]一、K8S的架构介绍1.1Docker原理:依赖于镜像,容器之间隔离使用cgroup+namespace实现隔离的;1.2K8S的架构介绍了解K8S之前需要掌握DockerKubernetes设计之初就是为了管理,调度容器技术;是google开发的一套开源的容器化编排技术;业界还有其他公司的容器编排技术例如Docker-compose,Docker-swarm,Mesos,目前k8s使用最广泛。Kubernetes则采用了Pod和Label这样的概念把容器组合成一个个的互相存在依赖关系

    2022年6月25日
    47
  • 登录织梦后台提示用户名不存在的解决方法介绍

    登录织梦后台提示用户名不存在的解决方法介绍

    2021年10月7日
    40

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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