OpenGL实现3D游戏编程【连载5】——纹理坐标、纹理贴图

news2024/11/15 20:03:46

OpenGL实现3D游戏编程【连载5】——纹理坐标、纹理贴图

在这里插入图片描述

欢迎来到zhooyu的专栏。
个人主页:【zhooyu】
文章专栏:【OpenGL实现3D游戏编程】

本专栏内容:

我们从游戏的角度出发,用C++去了解一下游戏中的功能都是怎么实现的。这一切还是要从自己玩游戏开始说起,此前就玩过一下3D游戏,当时就被游戏里的一些画面和设置深深的吸引了,同时游戏里还有很多很有趣的设定,比如,玩家的视角是怎么移动的?崎岖不平的地图是怎样制作的?人物和物体、地面的碰撞是怎样检测的?鼠标是怎样选中眼前的物体的?魔法技能是怎样释放的?不用加载进度条的无缝世界地图是怎么实现的?带着这些疑问,我们走进了一个OpenGL世界的3D世界。

在这里插入图片描述

1、本节实现的内容

在OpenGL中,纹理是一种常用的技术,用于将图像或图案映射到3D模型的表面上,以增加图形的细节和真实感。那么我们上一节已经做好了纹理的准备工作,将需要的纹理图片加载并绑定到了纹理编号中,我们这一节就探讨一下怎样将对应的纹理图片显示到我们的程序中去。

2、纹理坐标

纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到三维图形上的。

在这里插入图片描述

3、最简单的纹理贴图

那么先准备一个最简单的贴图。我们需要自定义一个整型变量texExampleBox用来存储我们的纹理编号,随后在消息函数的WM_CREATE中加载我们的纹理图片,加载函数直接用我们上节课的Texture_LoadFromFile_2或Texture_LoadFromFile_3即可,参数为我们图片在工程文件夹中的相对路径。

//自定义纹理编号

GLuint	texExampleBox;

	case WM_CREATE:

		......

		//加载纹理

		texExampleBox=Texture_LoadFromFile_3("Image/Box1693.bmp");
		
		......
		

接下来,我们就可以在WM_PAINT消息中添加我们显示纹理的操作。我们先创建一个垂直用地面的矩形,然后在每个glVertex3f函数前添加glTexCoord2f函数,将图片的纹理信息与正方形图形的坐标顶点对应,就可以将纹理显示在正方形图形中了。


				//启用二维纹理,调用特定编号的纹理	
				
				glEnable(GL_TEXTURE_2D);
				
				//绑定箱子的纹理
				
				glBindTexture(GL_TEXTURE_2D,texExampleBox);
				
				//显示长方形
				
				glBegin(GL_QUADS);
				
				//显示长方形的四个顶点
				
				glTexCoord2f(0,0);glVertex3f(-2.0f,-2.0f,0.0f);
				
				glTexCoord2f(1,0);glVertex3f(2.0f,-2.0f,0.0f);
				
				glTexCoord2f(1,1);glVertex3f(2.0f,2.0f,0.0f);
				
				glTexCoord2f(0,1);glVertex3f(-2.0f,2.0f,0.0f);
									
				glEnd();

				//关闭纹理贴图
		
				glDisable(GL_TEXTURE_2D);

添加纹理前我们的正方形是白色的,没有任何纹理:
在这里插入图片描述

添加纹理坐标后,我们的正方形显示如下:
在这里插入图片描述

4、三角形的纹理贴图

要想把纹理映射到三角形上,必须指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联一个纹理坐标(Texture Coordinate),表明顶点对应纹理图像的哪个位置的片段采样,之后图形的其它位置片段系统会自动进行片段插值(Fragment interpolation)。

在这里插入图片描述


				//启用二维纹理,调用特定编号的纹理	
				
				glEnable(GL_TEXTURE_2D);
				
				//绑定箱子的纹理
				
				glBindTexture(GL_TEXTURE_2D,texExampleBox);
				
				//显示三角形
				
				glBegin(GL_TRIANGLES);
				
				//显示长方形的四个顶点
				
				glTexCoord2f(0,0);glVertex3f(-2.0f,-2.0f,0.0f);
				
				glTexCoord2f(1,0);glVertex3f(2.0f,-2.0f,0.0f);
				
				glTexCoord2f(0.5,1);glVertex3f(0.0f,2.0f,0.0f);
									
				glEnd();

				//关闭纹理贴图
		
				glDisable(GL_TEXTURE_2D);

在这里插入图片描述

5、平面圆形纹理贴图

处理了简单的纹理贴图,我们来看下复杂的圆形贴图,深刻理解一下纹理坐标和顶点坐标的关系。我们准备了一个圆形的图片,但该图片在PNG文件中仍然是以矩形的图片保存的。如果我们想将这个图片纹理显示到一个圆形的图形中,由于圆形不存在正方形那样的四个角,我们无法向正方形那样将纹理坐标进行关联,那怎么处理这种纹理贴图呢?

在这里插入图片描述
首先我们需要明确的是,我们这里的圆形是以正多边形的方式显示出来的,当正多边形的边数较多时,下面正多边形的边数CircleFrame我们设置为36,就能较为圆滑的显示出圆形的轮廓。那我们就需要在圆弧上画出36个点,并连接中心点形成36个三角形。那我们只需要将中心点和36个圆弧上的点找到对应的纹理坐标即可。


				//启用二维纹理,调用特定编号的纹理	
				
				glEnable(GL_TEXTURE_2D);
				
				//绑定箱子的纹理
				
				glBindTexture(GL_TEXTURE_2D,texExampleCircle);
				
				//显示长方形
				
				glBegin(GL_TRIANGLE_STRIP);

				//设定自定义圆的精度

				int CircleFrame=36;

				//用于计算圆时的常量

				float PI=3.1415926f;
				
				//显示长方形的四个顶点
				
				for(int i=0;i<=CircleFrame;i++)
				{

					//圆心的纹理坐标

					glTexCoord2f(0.5f,0.5f);

					//圆心的三维坐标
					
					glVertex3f(0.0f,0.0f,0.0f);

					//圆弧上点的纹理坐标
					
					glTexCoord2f(0.5f+0.5f*cos(2*PI/CircleFrame*i),0.5f+0.5f*sin(2*PI/CircleFrame*i));

					//圆弧的三维坐标
					
					glVertex3f(2.0f*cos(2*PI/CircleFrame*i),2.0f*sin(2*PI/CircleFrame*i),0.0f);

				}
									
				glEnd();

				//关闭纹理贴图
		
				glDisable(GL_TEXTURE_2D);

经过纹理编号关联后,我们可以看到纹理已将完美的显示在了圆形上,效果如下:

在这里插入图片描述

6、3D球体纹理贴图

我们现在需要显示一个更为复杂的纹理贴图,将下边这张世界地图纹理显示到一个3D圆球上,让圆球真正变成一个小型的地球仪。
在这里插入图片描述

首先,我们需要在三维空间中创建一个圆形的模型,如下图。我们这里为了方便演示,采用双for循环和sin、cos函数相配合时时获取球体各个顶点的方法,方便大家直观的理解。比如我们下边(x0,y0,z0)和(x1,y1,z1)就是临时计算出来的顶点。

在这里插入图片描述
同样,我们也可以时时的计算出以上顶点对应的纹理坐标,并将纹理图片显示到圆球体上。这里我们要说个题外话,初中地理课时,老不明白地图展开图为什么越靠近两极面积越大,比如俄罗斯地图为什么那么大,现在了解了纹理贴图后,终于好像明白了一点,原因在于在纹理贴图的时候越靠近两极的部分纹理图片会被逐步压缩,在两极点上整个图片宽度会被压缩到极点上,这也就说明了展开图特性。


				//设置线的宽度

				glLineWidth(1.0f);

				//启用二维纹理,调用特定编号的纹理	
				
				glEnable(GL_TEXTURE_2D);
				
				//绑定地球的纹理
				
				glBindTexture(GL_TEXTURE_2D,texExampleEarth);

				//设定自定义球体的精度

				int EarthFrame=18;

				//用于计算圆时的常量

				float PI=3.1415926f;

				float EarthSize=2.0f;
				
				//显示地球框架顶点:纬度点
				
				if(1)for(int i=0;i<EarthFrame-1;i++)
				{

					//按照划分的各个区域逐个显示纹理
					
					glBegin(GL_QUAD_STRIP);
					
					//显示地球框架顶点:经度点
					
					for(int j=0;j<EarthFrame;j++)
					{
						
						//当前维度的点
						
						float x0=EarthSize*cos(2*PI/(float)(EarthFrame-1)*j)*cos(PI/(float)(EarthFrame-1)*i-PI/2);
						
						float z0=EarthSize*sin(2*PI/(float)(EarthFrame-1)*j)*cos(PI/(float)(EarthFrame-1)*i-PI/2);
						
						float y0=EarthSize*sin(PI/(float)(EarthFrame-1)*i-PI/2);
																		
						//下一个维度的点
						
						float x1=EarthSize*cos(2*PI/(float)(EarthFrame-1)*j)*cos(PI/(float)(EarthFrame-1)*(i+1)-PI/2);
						
						float z1=EarthSize*sin(2*PI/(float)(EarthFrame-1)*j)*cos(PI/(float)(EarthFrame-1)*(i+1)-PI/2);
						
						float y1=EarthSize*sin(PI/(float)(EarthFrame-1)*(i+1)-PI/2);
												
						//计算纹理坐标并显示纹理
						
						glTexCoord2f(1/(float)(EarthFrame-1)*(j),1/(float)(EarthFrame-1)*((i+0)));
						
						glVertex3d(x0,y0,z0);
						
						glTexCoord2f(1/(float)(EarthFrame-1)*(j),1/(float)(EarthFrame-1)*((i+1)));
						
						glVertex3d(x1,y1,z1);
						
					}
					
					glEnd();
					
				}

				//关闭纹理贴图
		
				glDisable(GL_TEXTURE_2D);

完成纹理贴图后圆球体变成如下样式:
在这里插入图片描述

动态图效果如下:

在这里插入图片描述

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

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

相关文章

六、前后端分离通用权限系统(6)

&#x1f33b;&#x1f33b; 目录 一、用户管理1.1、代码生成器1.2、用户管理后端 CRUD1.2.1、controller1.2.2、service 接口1.2.3、service 接口实现1.2.4、mapper1.2.5、xml1.2.6、knife4j 测试 1.3、用户管理前端 CRUD1.3.1、添加路由1.3.2、定义基础 api1.3.3、实现页面功…

架构设计(5)服务网格(Service Mesh)

服务网格&#xff08;Service Mesh&#xff09;是一个专门设计的基础设施层&#xff0c;用于管理和处理微服务架构中服务间的通信。服务网格通过在服务间插入代理&#xff0c;提供了一种透明的方式来控制、监控和管理服务之间的流量。以下是关于服务网格的详细介绍&#xff0c;…

( 基于SystemView软件)AM调制与解调仿真实验

一、实验目的&#xff1a; 熟悉使用SystemView软件&#xff0c;了解各部分功能模块的操作和使用方法。 通过实验进一步观察、了解模拟信号AM调制、解调原理。 掌握AM调制信号的主要性能指标。 比较、理解AM调制的相干解调原理。 二、实验器材&#xff1a; 装有SystemView…

【有道云-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

WLAN网络优化,还得看这三剑客!

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 晚上好&#xff0c;我的网工朋友。 WLAN网络成为了企业网络接入的主要手段之一&#xff0c;用户对于WLAN网络的体验性也有了更高的要求。 对于W…

雷达水位监测站低功耗及免维护设计

QY-15雷达水位监测站采用非接触雷达&#xff0c;无磨损无污染产品概述 雷达水位监测站是一款高精度且具有水面波动滤波处理的地表水水位测量产品。它采用喇叭天线的设计&#xff0c;降低功耗&#xff0c;宽范围的输入电压&#xff0c;专门设计于适合野外无人值守的野外自动站应…

图像噪声与被污染图像的恢复

系列文章目录 文章目录 系列文章目录前言一、图像退化模型二、图像噪声2.2 图像噪声的分类2.2.1 加性噪声2.2.2 乘性噪声 三、图像噪声的概率密度函数3.1 高斯噪声3.2 瑞利噪声3.3 均匀分布噪声3.4 脉冲噪声3.5 图像信噪比 四、图像噪声的概率密度函数 前言 数字图像常会因受一…

Elasticsearch之DSL查询语法

​ 前言 在上一篇文章中&#xff0c;我们通过RestClient的Bulk操作导入了大量的hotel数据到elasticsearch&#xff0c;实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。本篇文章会通过使用DSL来介绍elasticsearch的搜索功能。 1. DSL查询分…

C++ 类和对象 3

构造函数扩展 构造函数体内的赋值&#xff1a;构造函数一般是用于类对象的初始化的&#xff0c;但严谨来说并不是成员变量的初始化&#xff0c;内置类型的初始化是在生成的同时赋值而且仅有一次&#xff0c;但是在构造函数体内是能对成员变量进行多次赋值的。所以在函数体内的…

GeoStudio2024:地质工程的瑰宝下载安装介绍

引言 青山隐隐&#xff0c;流水潺潺&#xff0c;吾心所向&#xff0c;乃地质之奥秘。GeoStudio2024&#xff0c;如同一卷古籍&#xff0c;蕴藏无尽智慧&#xff0c;助吾等探寻地质之真谛。今以李白之笔&#xff0c;述其妙用&#xff0c;愿与君共赏。 初识GeoStudio2024 初见…

优化系统性能:深入探讨Web层缓存与Redis应用的挑战与对策

Web层缓存对于提高应用性能至关重要&#xff0c;它通过减少重复的数据处理和数据库查询来加快响应时间。例如&#xff0c;如果一个用户请求的数据已经缓存&#xff0c;服务器可以直接从缓存中返回结果&#xff0c;避免了每次请求都进行复杂的计算或数据库查询。这不仅提高了应用…

【iOS】iOS中简单的网络请求

目录 前言认识API和RULAPIURL两者的联系 简单的网络请求1. 创建URL对象2. 创建URLRequest对象3. 设置请求头&#xff08;如果需要&#xff09;4. 发送请求异步请求同步请求 5. 处理请求结果6.启动数据任务完整代码示例及运行结果&#xff1a; 关于同步请求和异步请求同步请求异…

vue3 cascader省市区三级联动如何指定字段,如何根据id查到对应的名字

如果我们接口数据字段名不是value和code。要加个props :props"{ value:code,label:regionName}"根据id查name需要一个ref和一个change事件<el-cascader :options"areaData" ref"addressCodeRef" change"handleChange" :props"…

MySQL(五)——表设计(约束、范式、表关系)

文章目录 表设计约束非空约束&#xff08;NOT NULL&#xff09;唯一约束&#xff08;UNIQUE&#xff09;主键约束&#xff08;PRIMARY KEY&#xff09;外键约束&#xff08;FOREIGN KEY&#xff09;默认值约束&#xff08;DEFAULT&#xff09;检查约束&#xff08;CHECK&#x…

考研数学最迟什么时候要结束强化?10月才做真题是不是晚了?

已经快9月了&#xff0c;很多同学的考研数学的强化也接近尾声&#xff0c;这个时候要注意两个事情&#xff1a; 1、如果你还有很多没学完&#xff0c;不要强行收尾&#xff0c;稳扎稳打的把强化给结束了。 2、强化结束之前&#xff0c;要清理完所有在强化阶段产生的错题&…

Kafka日志及常见问题

目录 1.Topic下的消息是如何存储的 1.1log文件追加记录所有消息 1.2index和timeindex加速读取日志信息 2.文件清理机制 2.1如何判断哪些日志文件过期了 2.2日志清理策略 3.Kafka的文件高效读写机制 3.1Kafka的文件结构 3.2顺序写磁盘 3.3零拷贝 3.3.1传统IO 3.3.2m…

应用层与传输层

1.应用层 很多时候这一层的协议是程序员自定义的应用层协议&#xff08;相当于一种约定&#xff0c;约定数据如何进行传输&#xff09;。 eg&#xff1a; 实现登录的场景&#xff1a; 此时前端就需要与后端约定请求&#xff08;假设约定使用ajax请求&#xff09;中的一些参…

接口自动化测试面试题目详解

1、get和post区别是什么&#xff1f; 答&#xff1a;POST和GET都是向服务器提交数据&#xff0c;并且都会从服务器获取数据。 区别&#xff1a; &#xff08;1&#xff09;传送方式&#xff1a;get通过地址栏传输&#xff0c;post通过报文传输 &#xff08;2&#xff09;传…

c语言 自定义类型--枚举 、联合 #枚举类型的定义 #枚举的优点 #枚举的使用 #联合类型的定义 #联合的特点 #联合大小的计算

文章目录 前言 一、枚举 (一)、枚举类型的定义 (二)、枚举的优点 (三)、枚举的使用 二、联合 (一)、联合类型的定义 (二)、联合的特点 (三)、联合大小的计算 总结 前言 路漫漫其修远兮&#xff0c;吾将上下而求索。 枚举、联合跟结构体很像&#xff0c;想要细致地了…

基于SpringBoot+Vue+uniapp的“村游网”系统的微信小程序开发的详细设计和实现(源码+lw+部署文档+讲解等)

文章目录 前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus 系统测试系统测试目的系统功能测试系统测试结论 为什么选择我代码参考数据库参考源码获取源码获取 前言 &#x1f31e;博主介绍 &#xff1a;✌全网粉丝15W,CSDN特邀作者、21…