当前位置: 首页 > news >正文

Opengl之立方体贴图

简单来说,立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。你可能会奇怪,这样一个立方体有什么用途呢?为什么要把6张纹理合并到一张纹理中,而不是直接使用6个单独的纹理呢?立方体贴图有一个非常有用的特性,它可以通过一个方向向量来进行索引/采样。假设我们有一个1x1x1的单位立方体,方向向量的原点位于它的中心。使用一个橘黄色的方向向量来从立方体贴图上采样一个纹理值会像是这样:

创建立方体贴图 

立方体贴图是和其它纹理一样的,所以如果想创建一个立方体贴图的话,我们需要生成一个纹理,并将其绑定到纹理目标上,之后再做其它的纹理操作。这次要绑定到GL_TEXTURE_CUBE_MAP:

unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

因为立方体贴图包含有6个纹理,每个面一个,我们需要调用glTexImage2D函数6次,参数和之前教程中很类似。但这一次我们将纹理目标(target)参数设置为立方体贴图的一个特定的面,告诉OpenGL我们在对立方体贴图的哪一个面创建纹理。这就意味着我们需要对立方体贴图的每一个面都调用一次glTexImage2D。

由于我们有6个面,OpenGL给我们提供了6个特殊的纹理目标,专门对应立方体贴图的一个面。

纹理目标方位
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

和OpenGL的很多枚举(Enum)一样,它们背后的int值是线性递增的,所以如果我们有一个纹理位置的数组或者vector,我们就可以从GL_TEXTURE_CUBE_MAP_POSITIVE_X开始遍历它们,在每个迭代中对枚举值加1,遍历了整个纹理目标:

int width, height, nrChannels;
unsigned char *data;  
for(unsigned int i = 0; i < textures_faces.size(); i++)
{data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0);glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}

这里我们有一个叫做textures_faces的vector,它包含了立方体贴图所需的所有纹理路径,并以表中的顺序排列。这将为当前绑定的立方体贴图中的每个面生成一个纹理。

因为立方体贴图和其它纹理没什么不同,我们也需要设定它的环绕和过滤方式:

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

不要被GL_TEXTURE_WRAP_R吓到,它仅仅是为纹理的R坐标设置了环绕方式,它对应的是纹理的第三个维度(和位置的z一样)。我们将环绕方式设置为GL_CLAMP_TO_EDGE,这是因为正好处于两个面之间的纹理坐标可能不能击中一个面(由于一些硬件限制),所以通过使用GL_CLAMP_TO_EDGE,OpenGL将在我们对两个面之间采样的时候,永远返回它们的边界值。

在绘制使用立方体贴图的物体之前,我们要先激活对应的纹理单元,并绑定立方体贴图,这和普通的2D纹理没什么区别。

在片段着色器中,我们使用了一个不同类型的采样器,samplerCube,我们将使用texture函数使用它进行采样,但这次我们将使用一个vec3的方向向量而不是vec2。使用立方体贴图的片段着色器会像是这样的:

in vec3 textureDir; // 代表3D纹理坐标的方向向量
uniform samplerCube cubemap; // 立方体贴图的纹理采样器void main()
{             FragColor = texture(cubemap, textureDir);
}

看起来很棒,但为什么要用它呢?恰巧有一些很有意思的技术,使用立方体贴图来实现的话会简单多了。其中一个技术就是创建一个天空盒(Skybox)。

天空盒

天空盒是一个包含了整个场景的(大)立方体,它包含周围环境的6个图像,让玩家以为他处在一个比实际大得多的环境当中。游戏中使用天空盒的例子有群山、白云或星空。下面这张截图中展示的是星空的天空盒,它来自于『上古卷轴3』:

 立方体贴图能完美满足天空盒的需求:我们有一个6面的立方体,每个面都需要一个纹理。在上面的图片中,他们使用了夜空的几张图片,让玩家产生其位于广袤宇宙中的错觉,但实际上他只是在一个小小的盒子当中。

你可以在网上找到很多像这样的天空盒资源。比如说这个网站就提供了很多天空盒。天空盒图像通常有以下的形式:

加载天空盒

因为天空盒本身就是一个立方体贴图,加载天空盒和之前加载立方体贴图时并没有什么不同。为了加载天空盒,我们将使用下面的函数,它接受一个包含6个纹理路径的vector:

unsigned int loadCubemap(vector<std::string> faces)
{unsigned int textureID;glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);int width, height, nrChannels;for (unsigned int i = 0; i < faces.size(); i++){unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);if (data){glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);stbi_image_free(data);}else{std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;stbi_image_free(data);}}glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);return textureID;
}

函数本身应该很熟悉了。它基本就是上一部分中立方体贴图的代码,只不过合并到了一个便于管理的函数中。

之后,在调用这个函数之前,我们需要将合适的纹理路径按照立方体贴图枚举指定的顺序加载到一个vector中。

vector<std::string> faces
{"right.jpg","left.jpg","top.jpg","bottom.jpg","front.jpg","back.jpg"
};
unsigned int cubemapTexture = loadCubemap(faces);

现在我们就将这个天空盒加载为一个立方体贴图了,它的id是cubemapTexture。我们可以将它绑定到一个立方体中,替换掉用了很长时间的难看的纯色背景。

显示天空盒

由于天空盒是绘制在一个立方体上的,

相关文章:

Opengl之立方体贴图

简单来说,立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。你可能会奇怪,这样一个立方体有什么用途呢?为什么要把6张纹理合并到一张纹理中,而不是直接使用6个单独的纹理呢?立方体贴图有一个非常有用的特性,它可以通过一…...

EF Core报错:Error Number:-2146893019

appsettings.json中的连接字符串要添加上&#xff1a;TrustServerCertificatetrue; 所以这里的连接字符串为&#xff1a;Data SourceLAPTOP-61GDB2Q7\\SQLEXPRESS;Initial CatalogMvcMovie.Data;Persist Security InfoTrue;TrustServerCertificatetrue;User IDsa;Passwordroot…...

QT之可自由折叠和展开的布局

介绍和功能分析 主要是实现控件的折叠和展开&#xff0c;类似抽屉控件&#xff0c;目前Qt自带的控件QToolBox具有这个功能&#xff0c;但是一次只能展开一个&#xff0c;所以针对自己的需求可以自己写一个类似的功能&#xff0c;这里实现的方法比较多&#xff0c;其实原理也比较…...

javascript二维数组(7)数组指定元素求和

项目需求 对指定数据中的score求和 const data [ { name: Alice, age: 23, score: 85 }, { name: Bob, age: 30, score: 90 }, { name: Charlie, age: 35, score: 80 } ];1.封装函数 这个函数接受两个参数&#xff1a;一个对象数组和一个键名&#xff08;也就是你想要…...

网络安全——黑客自学(笔记)

想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01;&#xff01;&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队…...

Docker 安装 Elasticsearch7.16.x

docker hub地址&#xff1a;https://hub.docker.com 拉取镜像 docker pull elasticsearch:7.16.3创建容器 docker run -di --nameelasticsearch -p 9200:9200 -p 9300:9300 -p 5601:5601 -e "discovery.typesingle-node" -e "cluster.nameelasticsearch" -…...

springmvc-controller视图层配置SpringMVC处理请求的流程

目录 1. 什么是springmvc 2.项目中加入springmvc支持 2.1 导入依赖 2.2 springMVC配置文件 2.3 web.xml配置 2.4 中文编码处理 3. 编写一个简单的controller 4. 视图层配置 4.1 视图解析器配 4.2 静态资源配置 4.2 编写页面 4.3 页面跳转方式 5. SpringMVC处理请求…...

三模块七电平级联H桥整流器电压平衡控制策略Simulink仿真

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

【window10】Dart+Android Studio+Flutter安装及运行

安装Dart SDK安装Android Studio安装Flutter在Android Studio中创建并运行Flutter项目 安装前&#xff0c;请配置好你的jdk环境&#xff0c;准备好你的梯子~ 安装Dart SDK 浅浅了解一下Dart&#xff1a; Dart 诞生于2011年&#xff0c;是由谷歌开发的一种强类型、跨平台的客户…...

【C++】运算符重载 ⑩ ( 下标 [] 运算符重载 | 函数原型 int operator[](int i) | 完整代码示例 )

文章目录 一、下标 [] 运算符重载1、数组类回顾2、下标 [] 运算符重载 二、完整代码示例1、Array.h 数组头文件2、Array.cpp 数组实现类3、Test.cpp 测试类4、执行结果 一、下标 [] 运算符重载 1、数组类回顾 在之前的博客 【C】面向对象示例 - 数组类 ( 示例需求 | 创建封装类…...

ROS机械臂开发-开发环境搭建【一】

目录 前言环境配置docker搭建Ubuntu环境安装ROS 基础ROS文件系统 bugs 前言 想系统学习ROS&#xff0c;做一些机器人开发。因为有些基础了&#xff0c;这里随便写写记录一下。 环境配置 docker搭建Ubuntu环境 Dockerfile # 基础镜像 FROM ubuntu:18.04 # 设置变量 ENV ETC…...

深度思考rpc框架面经之五:rpc限流:rpc事务:tps测试

11 注册中心监控和rpc限流&#xff08;用友云产品部二面&#xff09; 11.1 你这个注册中心有实现相关的监控吗 11.1.1 如何实现注册中心有实现相关的监控 是的&#xff0c;我可以为你提供关于RPC注册中心及其监控的相关信息。RPC注册中心是用于管理微服务之间调用关系的中心…...

[AIGC] Java序列化利器 gson

前言&#xff1a; 本篇文章主要介绍了Gson库&#xff0c;从是什么、为什么、怎么用三个方面进行了详细的介绍。在是什么部分&#xff0c;介绍了Gson库的作用和特点&#xff1b;在为什么部分&#xff0c;介绍了为什么要使用Gson库&#xff1b;在怎么用部分&#xff0c;介绍了如何…...

VMware 虚拟机删除+重建

由于本人暴力地关闭虚拟机&#xff0c;导致虚拟机出现了一些问题&#xff0c;并且还没有给虚拟机拍快照&#xff0c;虽然还能用&#xff0c;但本人不想将就&#xff0c;于是乎打算重新新建一个虚拟机 一、删除 1.打开workstation,选择虚拟机&#xff0c;右键选择移除。 虽然虚…...

环面上 FHE 的快速自举:LUT/Automata Blind Rotate

参考文献&#xff1a; [AP14] Alperin-Sheriff J, Peikert C. Faster bootstrapping with polynomial error[C]//Advances in Cryptology–CRYPTO 2014: 34th Annual Cryptology Conference, Santa Barbara, CA, USA, August 17-21, 2014, Proceedings, Part I 34. Springer B…...

VScode配置文件launch.json 和 tasks.json配置项详细说明

tasks.json tasks.json为编译配置文件 {"version": "2.0.0", // tasks.json 文件的版本号"tasks": [ // 任务数组&#xff0c;包含一个编译任务配置对象{"type": "cppbuild", // 任务类型&#xff0c;这里是 cppbuild …...

DNSlog 注入简单笔记

无回显的盲注可以想办法回显到 dns 日志上&#xff1a; 1、打开 http://www.dnslog.cn 获取域名 2、注入&#xff1a; ?id1 and (select load_file(concat(//,(select database()),.3.mw0gxd.dnslog.cn/a)))-- 3、点击刷新得到回显&#xff1a;...

HDLbits: Dualedge

FPGA没有双边缘触发触发器&#xff0c;&#xff08;posedge clk或negedge clk&#xff09;会报错 “FPGA&#xff08;以及其他任何地方&#xff09;上的触发器是一个具有一个时钟且仅对该时钟的一个边缘敏感的器件。”参考verilog为什么不能双边沿触发 实现双边沿的两种方法 …...

网络安全_黑客(自学)

想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01;&#xff01;&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队…...

AI 大框架分析基于python之TensorFlow(归一化处理,多类别分类的概率)

AI 大框架分析基于python之TensorFlow(归一化处理,多类别分类的概率) AI&#xff08;人工智能&#xff09;的大框架有很多种&#xff0c;以下是一些常见的AI框架&#xff1a; TensorFlow&#xff1a;由谷歌开发的开源机器学习框架&#xff0c;支持各种任务&#xff0c;包括图像…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

ArcGIS Pro+ArcGIS给你的地图加上北回归线!

今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等&#xff0c;设置经线、纬线都以10间隔显示。 2、需要插入背会归线&#xf…...

Web APIS Day01

1.声明变量const优先 那为什么一开始前面就不能用const呢&#xff0c;接下来看几个例子&#xff1a; 下面这张为什么可以用const呢&#xff1f;因为复杂数据的引用地址没变&#xff0c;数组还是数组&#xff0c;只是添加了个元素&#xff0c;本质没变&#xff0c;所以可以用con…...

可下载旧版app屏蔽更新的app市场

软件介绍 手机用久了&#xff0c;app越来越臃肿&#xff0c;老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法&#xff0c;还能帮我们卸载不需要的app。 手机现状 如今的app不断更新&#xff0c;看似在优化&#xff0c;实则内存占用越来越大&#xff0c;对手机性…...