因为项目需要,要用Qt+OpenGL显示三维地形,业务代码涉及保密,但是这种纯技术上的东西还是可以分享的。

话不多说,先看效果

这里我介绍一个简单的使用QT中的OpenGL实现三维地形显示的demo,可以实现第一人称的前后左右以及左右旋转上升下降等动作。

opengl的程序大多是三步式操作。主要实现三个函数就可以了。(当然还有着色器这些其他的)

这三个函数分别是

void initializeGL();
void resizeGL(int w, int h);
void paintGL();

首先介绍

void initializeGL();
void OpenGLWidget::initializeGL()
{initializeOpenGLFunctions();// vertex shaderQOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);vshader->compileSourceFile("://shader/vert.vert");// fragment shaderQOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);fshader->compileSourceFile("://shader/frag.frag");loadterrian();// shader programprogram = new QOpenGLShaderProgram;program->addShader(vshader);program->addShader(fshader);program->link();program->bind();view.setToIdentity();view.lookAt(QVector3D( 6, 0.5, 0), QVector3D( 6, 0.5, -6), QVector3D( 0, 1, 0));//1是眼睛的中心位置,2是眼睛看向的位置,3是眼睛的朝向位置// set color used to clear backgroundglClearColor(0.5f, 0.0f, 1.0f, 1.0f);glEnable(GL_DEPTH_TEST);xtrans=0;ytrans=0;ztrans=0;//初始化坐标轴转动值glGenBuffers(3, handle);//在handle数组中返回当前n个未使用的名称,表示缓冲区对象glBindBuffer(GL_ARRAY_BUFFER, handle[0]);//激活缓冲区对象,指定当前活动缓冲区的对象glBufferData(GL_ARRAY_BUFFER, sizeof(terrian_pos), terrian_pos, GL_STATIC_DRAW);//用数据分配和初始化缓冲区对象//使设置在着色器中生效GLuint vPosition = program->attributeLocation("VertexPosition");glEnableVertexAttribArray(vPosition);glBindBuffer(GL_ARRAY_BUFFER, handle[0]);glVertexAttribPointer( (GLuint)0, 3, GL_INT, GL_FALSE, 3*sizeof(GLint), 0 );//指定位置和偏移//glVertexAttribPointer( (GLuint)1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat),  (const void *)(3*sizeof(GLfloat)) );//备用model.setToIdentity();glClearDepthf(1.0);glEnable(GL_TEXTURE_2D);//glEnable(GL_CULL_FACE);//是否使能正反面glDepthFunc(GL_LEQUAL);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);
}

这段程序的注释比较完整,就不介绍了。主要其中有一个 loadterrian()函数,这个函数是从图片文件中读取地形数据的。举一个地形数据的图像如下:

每个像素点的灰度值代表该点的高度。

loadterrian()的具体实现如下:
void OpenGLWidget::loadterrian()
{QImage heightmap;GLint terrian_pos1[img_width*img_height][3];GLint terrian_index1[img_width*img_height*3];GLfloat terrian_texture1[img_width*img_height*2][2];heightmap.load("heightmap.png");//索引int index1=0;for(int i=0;i<img_width*img_height*2;i++){terrian_index1[index1++]=i;terrian_index1[index1++]=i+img_width;}for(int i=0;i<index1;i++){terrian_index[i]=terrian_index1[i];}//顶点for(int j=img_width-1;j>=0;j--){for(int i=0;i<=img_width-1;i++){terrian_pos1[img_height*(img_width-1-j)+i][0]=i;QColor color=heightmap.pixel(i,j);terrian_pos1[img_height*(img_width-1-j)+i][1]=color.red()/25;terrian_pos1[img_height*(img_width-1-j)+i][2]=-(img_width-1-j);//z是负数}}for(int i=0;i<=img_width-1;i++){for(int j=0;j<=img_width-1;j++){terrian_pos[(img_height-1)*i+j][0]=terrian_pos1[(img_height-1)*i+j][0];terrian_pos[(img_height-1)*i+j][1]=terrian_pos1[(img_height-1)*i+j][1];terrian_pos[(img_height-1)*i+j][2]=terrian_pos1[(img_height-1)*i+j][2];}}//纹理for(int i=0;i<img_width*img_height*2;i++){terrian_texture1[i][0]=(float)terrian_pos1[i][0];terrian_texture1[i][1]=(float)terrian_pos1[i][2];}for(int i=0;i<img_width*img_height*2;i++){terrian_texture[i][0]=terrian_texture1[i][0];terrian_texture[i][1]=terrian_texture1[i][1];}
}

这个函数读取了每个点的高度数据,建立了纹理坐标,并且还建立了索引数组。

下面实现

void resizeGL(int w, int h);
void OpenGLWidget::resizeGL(int w, int h)
{glViewport(0,0,w/2,h/2);projection.setToIdentity();projection.perspective(60.0f, (GLfloat)w/(GLfloat)h, 0.001f, 100.0f);
}

这个就简单了

下面直接看

void paintGL();
void OpenGLWidget::paintGL()
{if(flag==0)//平移变化{//model.rotate(0, 0.0, 1.0, 0.0);model.translate(xtrans, ytrans, ztrans);//模型平移add_xtrans+=xtrans;add_ytrans+=ytrans;add_ztrans+=ztrans;model.rotate(yrot, 0.0, 1.0, 0.0);}else//旋转变化{model.setToIdentity();model.translate(6, 0.5, 0);//模型平移add_yrot+=yrot;model.rotate(add_yrot, 0.0, 1.0, 0.0);model.translate(add_xtrans-errorx, add_ytrans-errory, add_ztrans);//模型平移}flag=0;xtrans=0;ytrans=0;ztrans=0;yrot=0;program->setUniformValue("view", view);program->setUniformValue("projection", projection);program->setUniformValue("model", model);program->bind();//设置纹理QImage image(":/shader/texture_img.png");image = image.convertToFormat(QImage::Format_RGB888);image = image.mirrored();GLuint texture;glGenTextures(1, &texture);//glGenTextures的第一个参数是要创建的纹理数量,后面的参数就是保存这么多数量的整型数数组。glBindTexture(GL_TEXTURE_2D, texture);//绑定到OpenGL的环境里glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(),0, GL_RGB, GL_UNSIGNED_BYTE, image.bits());//把加载的图片数据放到纹理中glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    //横坐标glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    //纵坐标glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//缩小时的过滤方式glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//放大时的过滤方式glGenerateMipmap(GL_TEXTURE_2D);//创建mipmaps,更流畅?glBindBuffer(GL_ARRAY_BUFFER, handle[1]);//激活缓冲区对象,指定当前活动缓冲区的对象glBufferData(GL_ARRAY_BUFFER, sizeof(terrian_texture), terrian_texture, GL_STATIC_DRAW);//用数据分配和初始化缓冲区对象GLuint m_texCoordAttr = program->attributeLocation("tempTextCoord");glEnableVertexAttribArray(m_texCoordAttr);//glBindBuffer(GL_ARRAY_BUFFER, handle[0]);glVertexAttribPointer( (GLuint)2, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), 0 );//指定位置和偏移program->bind();//绘制,数目应该是一个面的顶点数x面数for(int s=0; s<img_width-2; s++)//z小于数据宽度w{glDrawElements(GL_TRIANGLE_STRIP, img_width*2, GL_UNSIGNED_INT, &terrian_index[img_width*s*2]);//索引应为数据宽度w,绘图数量应该是一层图像中面数x顶点数}qDebug()<<"paintGL";
}

这个函数中主要是纹理的加载和初始化,坐标的变化,以及glDrawElements绘图

这样子opengl的三维地形图就创建完成了。但是现在的三维地图还无法移动,三维地图按照第一人称视角移动是有一点点麻烦的,因为opengl中的移动和旋转都是参照原点坐标系,所以我们需要先做坐标变化。

地图移动的代码如下所示:

void MainWindow::on_forward_clicked()
{openGLWidget->xtrans+=0.25*sin(-openGLWidget->add_yrot/180.0*3.1415926);openGLWidget->ztrans+=0.25*cos(-openGLWidget->add_yrot/180.0*3.1415926);qDebug()<<openGLWidget->add_yrot;//openGLWidget->ztrans=0.25f;openGLWidget->update();
}void MainWindow::on_back_clicked()
{openGLWidget->ztrans=-0.25f;openGLWidget->update();
}void MainWindow::on_left_shift_clicked()
{openGLWidget->xtrans=0.25f;openGLWidget->update();
}void MainWindow::on_right_shift_clicked()
{openGLWidget->xtrans=-0.25f;openGLWidget->update();
}void MainWindow::on_left_handed_clicked()
{openGLWidget->yrot=-2.0f;openGLWidget->flag=1;openGLWidget->update();
}void MainWindow::on_right_handed_clicked()
{openGLWidget->yrot=2.0f;openGLWidget->flag=1;openGLWidget->update();
}void MainWindow::on_rise_clicked()
{openGLWidget->ytrans=-0.25f;openGLWidget->update();
}void MainWindow::on_fall_clicked()
{openGLWidget->ytrans=0.25f;openGLWidget->update();
}

源码我已经上传了,链接如下:

https://download.csdn.net/download/weixin_42521239/11225126

如果没有积分的小伙伴可以在评论中留下你的邮箱,我稍后发给你。

Qt+OpenGL实现三维地形显示相关推荐

  1. OpenGL(十四)——Qt OpenGL纹理

    OpenGL(十四)--Qt OpenGL纹理 一.纹理 终于写到纹理的部分了: 纹理(Texture)的本质是一个2D图片(1D和3D),或者叫图形数据.只是在OpenGL中专业术语中称其为纹理. ...

  2. OpenGL(十二)——Qt OpenGL绕着坐标轴旋转多边形

    OpenGL(十二)--Qt OpenGL绕着坐标轴旋转多边形 一.旋转多边形 前两篇介绍了如何绘制多边形,并且给多边形进行上色.本篇介绍如何旋转多边形. 多边形的旋转,在类中增加两个变量来控制这两个 ...

  3. 【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

    通过Qt opengl不是为了3D绘制,而是为了将视频绘制起来 使用opengl 可以极大降低yuv转rgb的转换开销 使用Opengl需要考虑三大问题: 1.QOpenGLWidget(与界面如何交 ...

  4. WEB端显示三维地形模型

    注:正常在WEB上显示三维地形首选Cesium,本文内容仅作为研究,展示文章用DEM制作通用三维地形模型中制作的局部三维地形模型 Cesium是可以很容易的实现在WEB端三维地形的,下面的图是分别是使 ...

  5. QT + opengl 三维视图变换

    QT + Opengl 三维视图变换 原本是导师的项目,要求像paraview一样实现三维网格的可视化以及三键操作,从qt 和opengl零基础开始动手鼓捣了大半个月的才完成 QT +Opengl环境 ...

  6. (转)利用ArcScene进行三维地形模拟

    本文摘自:http://www.sunzx.net/archive/1109.html 在ArcGIS Desktop中,可用于三维场景展示的程序为ArcGlobe和ArcScene,由于两者的差别, ...

  7. OpenGL之三维GIS

    相对于二维图形开发,大家一般认为三维开发要难很多.其实也不一定.相比二维开发,三维开发一般都是在三维图形引擎的基础之上进行的,比如常用的OpenGL或DirectX,而在工程和行业应用领域中,Open ...

  8. 游戏设计之基于高程图的三维地形绘制

    2015年完成的游戏设计课程大型实验论文(基础应用) -- 引用请联系博主 一.概述 地形绘制是室外三维游戏中必须面对的问题.三维真实感地形是虚拟场景中的基础部分,利用Visual C++,以通用的标 ...

  9. QT+OpenGL导入STL文件(二进制/ascll码格式),鼠标交互实现缩放旋转

    碎碎念 由于课设和大创涉及到了模型的旋转,因此专门去学习了模型的导入,也是废了不少心思,现在总结一下两种格式的简单导入,以及对stl模型两种格式的简单介绍.网上有很多大佬都有详细的解答,结尾附上链接: ...

最新文章

  1. debian查询端口进程_Linux查看端口、进程情况及kill进程
  2. 国家和货币(符号/图片)数组(countries and currency symbol array)
  3. PAT乙级(1016 部分A+B)
  4. 缓存问题引发的一系列优化
  5. python gui下载进度条_对python GUI实现完美进度条的示例详解
  6. 获取应用程序根目录物理路径(Web and Windows)
  7. ORB_SLAM3_一张图说明ORB-SLAM中的ORB特征提取和图像匹配算法流程
  8. 利用 Commons-Fileupload 实现文件上传分析(转)
  9. java并发包是谁编写的_0.Java并发包系列开篇
  10. linux 下的绘图软件Visio——流程图,矢量图
  11. android 打开默认浏览器,Android调用系统自带浏览器打开网页的实现方法
  12. c语言编程if语句的用法,if句子用法(C语言中,if语句的用法)
  13. 如何解决苹果账号续费时支付失败的问题
  14. 《费马大定理》-站在巨人的肩膀上
  15. odi连接oracle数据库,通过ODI接口把数据从Oracle数据库抽到HIVE数据库怎么解决
  16. zencart 模板文件说明
  17. Java获取汉字的拼音
  18. 【高项备考】多种类型计算题学习
  19. Android 插件化原理 完胜360插件框架 技术实战
  20. K8S集群搭建,并部署nginx实现跨网络访问

热门文章

  1. 【 简化的插入排序 】 本题要求编写程序,将一个给定的整数插到原本有序的整数序列中,使结果序列仍然有序
  2. 网游人才市场两难困境:招聘难,应聘也难
  3. 共享自助洗车小程序源码
  4. 剖析Mozilla代码之七武器
  5. 2021哈工大计算机考研复试名单,哈尔滨工业大学各学院2021年硕士研究生复试名单...
  6. 夜神模拟器设置与PC同一网段,单IP
  7. 百分百面试题:遇到过线上问题没有?
  8. 从给定的大图中切出指定矩形区域的小图
  9. 农夫山泉:HANA应用在企业中层层展现
  10. AtCoder Regular Contest 084