基于VC++的3D地形绘制与纹理贴图

news2024/11/24 14:12:59

前言

随着地理信息系统产业的发展,三维产品也在生活中处处吸引着我们的眼球。作为数字城市的核心内容,城市模型的构建成为了目前研究的热点。OpenGL是独立于操作系统和硬件环境的三维图形库,其为实现逼真的三维绘制效果和建立交互的三维场景提供了高效率的函数库,在交互式三维图形建模能力和编程方面具有无可比拟的优越性。在VC++中配置OpenGL开发环境,利用OpenGL,VC++构建了地形的三维可视化模型,通过对三维地形进行纹理贴图,光照设置使得绘制结果更具真实感。结果表明,利用利用 VC++和OpenGL进行三维真实感地形生成是切实可行的,其实现的功能全面而且效率高。

三维地形可视化可以直观、真实地表达地形的三维信息及综合特征,在国民经济各个领域中有着广泛的应用价值和广阔的应用前景。基于上述理论及成果,以VC++为平台,利用OpenGL库函数编写三维地形可视化过程。首先读取DEM数据,构建三角面片,计算其法向量;以BMP格式图片作为纹理,通过纹理坐标与三角面片顶点坐标的匹配实现纹理贴图;最后为场景添加光照、投影等,使场景更具真实感。最终实现了一个可以旋转、缩放的三维地形展示。

一、技术路线

本文主要分为两个部分,三维地形生成与纹理贴图和环境设置。前者是研究的主要内容。先生成并读取DEM数据,运用栅格的位置坐标和高程值生成三维点,基于三维点构建三角面片;接着利用法向量生成函数计算得到各三角面片的法向量,为添加光照服务;最后读取RGB图像作为纹理,利用glTexCoor2f()与glVertex3f()函数为三角面片顶点指定纹理坐标,将纹理贴到三角面片上。同时运用OpenGL添加光照,设置视点等,使地形的三维效果更具真实感。技术路线如图1。

图1 技术路线

二、程序实现

1.建立光照模型

(1)打开光源

(2)设置光源属性

光源属性主要包括Position、Ambient、Diffuse、Specular,本文只考虑了前三者。

GL_AMBIENT表示该光源所发出的光,经过非常多次的反射后,最终遗留在整个光照环境中的强度(颜色)。GL_DIFFUSE 表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)。每个属性由四个值表示,分别代表了颜色的R,G,B,A值。GL_POSITION属性。表示光源所在的位置。由四个值(X,Y,Z,W)表示。如果第四个值 W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,通常,太阳可以近似的被认为是方向性光源。本文设置的即为接近太阳的光源。

glClear函数来自OpenGL,其中它是通过glClear使用红,绿,蓝以及AFA值来清除颜色缓冲区的,并且都被归一化在(0,1)之间的值,其实就是清空当前的所有颜色。

(3)设置材质参数

其中,GL_FRONT_AND_BACK代表材质的面,GL_DIFFUSE代表漫反射反射系数,包括四个参数,前三个参数RGB对应于该色光的反射百分比(反射系数)。如R=1.0,G=0.5,B=0.0,则反射全部红光,一半绿光,不反射蓝光。

2.读取DEM数据

(1)在Arcmap中将DEM数据转换成ASCII码存储形式,即将每个栅格的高程用文本的形式记录下来,或者可以自动生成一个指定大小n*n的二维数组,存放指定的高程信息。

(2)通过文件操作将栅格大小和每个栅格的高程读入,使用数组记录下高程,即为Z轴坐标,通过栅格大小计算出每个栅格距离中心点的X轴方向距离和Y轴方向距离,即为X轴坐标和Y轴坐标。将X、Y、Z存入动态数组DTMX、DTMY和DTMZ。具体的程序实现为:

3.构建三角面片并计算法向量

在上一步骤中,实现了将DEM栅格数据的位置坐标和高程值(DTMX、DTMY、DTMZ)存入数组,接下来根据这些点的坐标建立不规则三角网,并计算所有三角面片的法向量。

(1)绘制三角形格网

对于三角形,OpenGL一般有三种绘制一系列三角形的方式,分别是GL_TRIANGLES、GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN,本文采用第二种方式创建三角形格网。其规律是构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始):如果当前顶点是奇数:组成三角形的顶点排列顺序:T = [n-1 n-2 n]。如果当前顶点是偶数:组成三角形的顶点排列顺序:T = [n-2 n-1 n]。依据以上原理,通过编程实现三角网的绘制以及纹理坐标的计算。

(2)设置法向量

glNormal*()用来设置当前的法向量,这个法向量将被应用到紧接下来的glVertex*()所定义的顶点上。但是通常各个顶点的法向是各不相同的,所以我们通常在定义每个顶点之前都为它确定一次法向量。

在一个平面内,有两条相交的线段,假设其中一条为矢量W,另一条为矢量V,平面法向为N,则平面法向就等于两个矢量的叉积(遵循右手定则),即N=W×V。例如:一个三角形平面三个顶点分别为P0、P1、P2,相应两个向量为W、V,本文定义ddGetNormal()函数来计算三角面片的法向量,计算方式如下列代码所示:

4.指定纹理坐标与贴图

(1)读取RGB图像

OpenGL在以前的很多版本中,限制纹理的大小必须是2的整数次方,即纹理的宽度和高度只能是16,32,64,128,256等值,直到最近的新版本才取消了这个限制。而且,一些OpenGL实现并没有支持到如此高的OpenGL版本。因此,在使用纹理时要特别注意其大小。尽量使用大小为2的整次方的纹理。当这个要求无法满足时,使用gluScaleImage函数把图像缩放至所指定的大小。本实验用的是512*512大小的bmp格式的图片,把像素值赋值给pixels指针。

(2)启用纹理与载入纹理

具体载入程序如下:

(3)指定纹理坐标

纹理一般有两种定义法,它们分别是连续法以及离散法。其中,连续法是指纹理可以用一个合适的二元函数来表示,纹理空间就是这个二元函数的定义域。相反的,离散法则是用一个特定的二维数组来定义纹理函数,每个二维数组的元素就表示对应的纹理空间中行、列间隔都固定的网格点的纹理值。另外,两个网格点之间的点的纹理值就通过两个格点间的值进行插值运算的得出。最后再根据纹理与物体两个不同空间的坐标变换来把纹理正确贴到对应物体的表面。在这里,也就是利用纹理贴图技术将纹理影像经过坐标变换贴到由数字地面高程模型 DEM 所构建成的三维模型上,这个过程是产生真实三维感的重要环节。

当我们绘制一个三角形时,只需要指定三个顶点的颜色。三角形中其它各点的颜色不需要我们指定,这些点的颜色是OpenGL自己通过计算得到的。 在我们学习OpneGL 光照时,法线向量、材质的指定,都是只需要在顶点处指定一下就可以了,其它地方的法线向量和材质都是OpenGL 自己通过计算去获得。

纹理的使用方法也与此类似。只要指定每一个顶点在纹理图象中所对应的像素位置,OpenGL 就会自动计算顶点以外的其它点在纹理图象中所对应的像素位置。

以二维纹理为例,规定纹理最左下角的坐标为(0, 0),最右上角的坐标为(1, 1),于是纹理中的每一个像素的位置都可以用两个浮点数来表示(三维纹理会用三个浮点数表示,一维纹理则只用一个即可)。使用glTexCoord*系列函数来指定纹理坐标。这些函数的用法与使用glVertex*系列函数来指定顶点坐标十分相似。例如:glTexCoord2f(0.0f,0.0f);指定使用(0, 0)纹理坐标。

每个顶点使用不同的纹理,下面这样形式的代码是比较常见的。

当我们用一个坐标表示顶点在三维空间的位置时,可以使用glRotate*等函数来对坐标进行转换。只要使用glMatrixMode(GL_TEXTURE),就可以把三维物体转变为二维图象,将三维空间切换到二维纹理矩阵。为了指定当前操作的是何种矩阵,我们使用了函数glMatrixMode。依据以上原理,编程实现三角网的绘制和纹理坐标的计算如下:

5.设置视角

投影变换就是定义一个可视空间,可视空间以外的物体不会被绘制到屏幕上。如果需要操作投影矩阵,需要以GL_PROJECTION 为参数调用glMatrixMode函数。gluPerspective的实现是通过将当前矩阵与通过这个函数指定的参数而建立的矩阵相乘来实现的,而在OpenGL中,矩阵的相乘都是连乘的,也就是说,调用gluPerspective这个函数会与其他的变化矩阵的函数效果相叠加从而影响原矩阵,因此,在调用这个函数之前,通常需要先调用glLoadidentity来把当前矩阵单位化,从而使各种变换效果不会叠加,比如旋转就只旋转,透视就只透视,通过调用glLoadidentity就不会既旋转有透视了。

透视投影所产生的结果类似于照片,有近大远小的效果,比如在火车头内向前照一个铁轨的照片,两条铁轨似乎在远处相交了。 使用gluPerspective函数可以将当前的可视空间设置为透视投影空间。其参数的意义如下。

三、实验结果

最终实现基于VC++的3D地形绘制与纹理贴图,如下图:

图2 Google earth 北京地区卫星影像模拟三维地形

总结

VisualC++是是一个功能非常强大的编程开发环境,在VC 环境下进行OpenGL的编程,调用的是DLL动态链接库,因此速度也不会受到影响。本文利用建模技术建了三维地形模型,在场景中使用光照、纹理映射技术使之具有了较强的真实感,最终实现了三维地形的可视化。

后续本文会把代码cpp与工程文件列出,供大家学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/144227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ES6 课程概述④

文章目录6-1. 对象解构什么是解构在解构中使用默认值非同名属性解构7-2. 共享符号7-3. 知名(公共、具名)符号[回顾]事件循环8-1. 事件和回调函数的缺陷8-2. 异步处理的通用模型8-3. Promise 的基本使用8-4. Promise 的串联8-5. Promise 的其他 api原型成…

通关手册 | 祝我通关成功!!!

Framework of MLLoss on training data1. large1.1 Model Bias1.2 Optimization2. smallLoss on testing data2.1 large2.1.1 overfitting2.1.2 mismatch2.2 small通关手册:祝我通关成功!!! Loss on training data 1. large 1.1…

Vue--》Vue3生命周期以及其它组合API的讲解

目录 生命周期 自定义hook函数 toRef shallowReactive与shallowRef readonly与shallReadonly toRaw与markRaw 生命周期 Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但是有两个被更名:Vue2中的beforeDestroy改名为:beforeUnmount&…

【数字孪生百科】可视化图表知识科普——Pareto图(Pareto Chart)

简介Pareto图(Pareto Chart)又称帕累托图、排列图,是一种特殊类型的条形图。图中标绘的值是按照事件发生的频率排序而成,显示由于各种原因引起的缺陷数量或不一致的排列顺序。Pareto图是根据 Vilfredo Pareto 命名的,他…

Go语言设计与实现 --Goroutine

Goroutine是GMP模型中的G,是属于用户态的线程,由Go runtime管理,而不是操作系统管理。 数据结构 type g struct {goid int64 // 唯一的goroutine的IDsched gobuf // goroutine切换时,用于保存g的上下文stack stack // 栈gopc…

Android Studio调用so库中方法

一、JNI规范so库调用 在 Android Studio生成自己的so库 中已经创建了自己的so库,这是一个JNI规范的so库,可以直接将so库放到libs中,并按照上面文章中MainActivity中的调用方法使用。 1、build.gradle(app)配置 andro…

SHELL脚本学习 --- 第六次作业(正则和sed)

SHELL脚本学习 — 第六次作业 思路: 作业1: 1,正则匹配h或H即可 2,sh$匹配以sh结尾 3,使用[[:space:]]匹配空格,[^[:space:]]匹配非空格 4,^to开头,中间.匹配任意字符0次或多次&…

Java一学就会系列:介绍与第一个java程序

系列文章目录 java环境-jdk环境安装与配置(jdk1.8) 文章目录系列文章目录前言一、JAVA是什么?二、环境安装三、开发工具1. Eclipse(推荐)2. IntelliJ IDEA (收费)四、第一个Java程序总结前言 …

搭建Django项目——实现简单的API访问

1、创建Django项目 打开pycharm,新建Django项目,可以选择一个虚拟环境 建完之后目录如下: 2、创建应用,我这里命名为demo 在命令行执行 python manage.py startapp demo执行之后,会发现项目目录下多了demo文件夹…

Linux小黑板(6):软硬链接

"飞吧,去寻觅红色的流星!"一、软硬链接简介软链接:软链接又叫符号链接,这个文件包含了另一个文件的路径名。可以是任意文件或目录,可以链接不同文件系统的文件。软链接硬链接:硬链接(hard link,也称链接&…

美创科技深度参编,中国信通院《数据安全治理实践指南(2.0)》发布

1月5日,由中国信息通信研究院、中国通信标准化协会指导,中国通信标准化协会大数据技术标准推进委员会主办,数据安全推进计划承办的第二届数据安全治理峰会成功召开,多项数据安全研究重要成果发布。会上,美创科技参与编…

MyBatis中数组套数组的格式

数组套数组的形式写法 1.dao层 List<Regulation> queryAllRegulations(); 配置 <resultMap id"RegulationResultMap" type"com.elfsack.cs.dto.allot.Regulation"><result column"shop_code" property"shopCode" /…

ARM32平台系统crash(系统崩溃) 问题定位的一种解决方法

说明 分享一种crash问题定位的一种解决方法&#xff0c;仅供参考。 ARM32平台上通过错误使用内存&#xff0c;触发系统异常&#xff0c;系统崩溃。系统异常被挂起后&#xff0c;能在串口中看到异常调用栈打印信息和关键寄存器信息。 如下所示&#xff0c;excType表示异常类…

上传文件前后端处理【vue3 + springboot】

前端 1.处理modal框 <template><n-modalv-model:show"modalVisible"preset"card":title"title"class"w-700px"><n-space class"w-full pt-16px" :size"24" justify"end"><n-but…

python数据分析:采集分析岗位数据,看看薪资的高低都受什么因素影响呢

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 在我们学习的时候,通常会产生疑问:这个行业前景好不好呢? 今天我们就用python的数据分析这个就业方向来举例 看一下都有哪些因素影响了薪资的高低呢&#xff1f; 数据采集 模块使用: reques…

这些js原型及原型链面试题你能做对几道

一、前言 在面试过程中&#xff0c;频频被原型相关知识问住&#xff0c;每次回答都支支吾吾。后来有家非常心仪的公司&#xff0c;在二面时&#xff0c;果不其然&#xff0c;又问原型了&#xff01; 我痛下决心用了两天时间钻研了下原型&#xff0c;弄明白后发现世界都明亮了…

Spark 在 KaiwuDB 中的应用与实践

线上沙龙-技术流第 24 期营业啦01月12日&#xff08;周四&#xff09;19:30开务数据库 - B站直播间当数据库面对大量数据复杂 OLAP 查询时&#xff0c;性能出现局限性&#xff0c;无法满足用户 AP 方面的高性能要求。为此&#xff0c;KaiwuDB 推出了此项解决方案&#xff1a;借…

【NI Multisim 14.0原理图环境设置——电路图属性设置】

目录 序言 一、电路图属性设置 &#x1f34a;1.设置对象可见性 &#x1f34a;2.设置图纸颜色 &#x1f34a;3.设置图纸尺寸 &#x1f34a;4.设置图纸方向 &#x1f34a;5.设置图纸单位 &#x1f34a;6.设置图纸网格点 &#x1f34a;7.设置图纸边框 &#x1f34a;8. 设…

ELK安装使用

太久没用了&#xff0c;熟悉一下。 JDK1.8以上环境 下载地址 elasticsearch&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch kibana: https://www.elastic.co/cn/downloads/kibana logstash &#xff1a; https://www.elastic.co/cn/downloads/logstash…

社招前端二面必会手写面试题总结

字符串查找 请使用最基本的遍历来实现判断字符串 a 是否被包含在字符串 b 中&#xff0c;并返回第一次出现的位置&#xff08;找不到返回 -1&#xff09;。 a34;b1234567; // 返回 2 a35;b1234567; // 返回 -1 a355;b12354355; // 返回 5 isContain(a,b);function isContain(a,…