零基础学习OpenGL(八)--立方体贴图、天空盒、环境映射

2023-05-16

                                                                                    立方体贴图

       将多个纹理组合起来映射到一张纹理上的一种纹理类型:立方体贴图(Cube Map)。
       立方体贴图:一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。之所以使用6个纹理合并在一张纹理而不使用6个单独的纹理,是因为可以通过一个方向向量来进行索引或采样。方向向量的原点在立方体的中心。方向向量的大小并不重要,只要提供了方向,OpenGL就会获取方向向量(最终)所击中的纹素,并返回对应的采样纹理值。

        创建立方体贴图:

         unsigned int textureID;

         glGenTextures(1, &textureID);

         glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

        立方体贴图包含有6个纹理,每个面一个,将纹理目标(target)参数设置为立方体贴图的一个特定的面,告诉OpenGL我们在对立方体贴图的哪一个面创建纹理。和OpenGL的很多枚举(Enum)一样,它们背后的int值是线性递增的。如下图:

          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_CLAMP_TO_EDGE,这是因为正好处于两个面之间的纹理坐标可能不能击中一个面(由于一些硬件限制),所以通过使用GL_CLAMP_TO_EDGE,OpenGL将在我们对两个面之间采样的时候,永远返回它们的边界值。

         使用立方体贴图的片段着色器:

         in vec3 textureDir; // 代表3D纹理坐标的方向向量

         uniform samplerCube cubemap; // 立方体贴图的纹理采样器

         void main()

         {

               FragColor = texture(cubemap, textureDir);

         }

                                                                                                     天空盒

         天空盒是一个包含了整个场景的(大)立方体,它包含周围环境的6个图像,让玩家以为他处在一个比实际大得多的环境当中。将这六个面折成一个立方体,你就会得到一个完全贴图的立方体,模拟一个巨大的场景。一些资源可能会提供了这样格式的天空盒,你必须手动提取六个面的图像,但在大部分情况下它们都是6张单独的纹理图像。

         加载天空盒:

         vector<std::string> faces;

          { "right.jpg", "left.jpg", "top.jpg", "bottom.jpg", "front.jpg", "back.jpg" };

         unsigned int cubemapTexture = loadCubemap(faces);

         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;

           }

显示天空盒:

          顶点着色器:

           #version 330 core

           layout (location = 0) in vec3 aPos;

           out vec3 TexCoords;

           uniform mat4 projection;  

           uniform mat4 view;

           void main()

            {

                   TexCoords = aPos;

                   gl_Position = projection * view * vec4(aPos, 1.0);

            }

         将输入的位置向量作为输出给片段着色器的纹理坐标。片段着色器会将它作为输入来采样Cube。

         #version 330 core

         out vec4 FragColor;

          in vec3 TexCoords;

          uniform samplerCube skybox;

           void main()

           {

                  FragColor = texture(skybox, TexCoords);

            }

       绘制天空盒时,我们需要将它变为场景中的第一个渲染的物体,并且禁用深度写入。这样子天空盒就会永远被绘制在其它物体的背后了。

         glDepthMask(GL_FALSE);

         skyboxShader.use();

          // ... 设置观察和投影矩阵

          glBindVertexArray(skyboxVAO);

          glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);

          glDrawArrays(GL_TRIANGLES, 0, 36);

           glDepthMask(GL_TRUE);

           // ... 绘制剩下的场景

          希望移除观察矩阵中的位移部分,让移动不会影响天空盒的位置向量。通过取4x4矩阵左上角的3x3矩阵来移除变换矩阵的位移部分。我们可以将观察矩阵转换为3x3矩阵(移除位移),再将其转换回4x4矩阵,来达到类似的效果。

          glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));

优化:

         我们现在是首先渲染天空盒,之后再渲染场景中的其它物体。如果我们先渲染天空盒,我们就会对屏幕上的每一个像素运行一遍片段着色器,即便只有一小部分的天空盒最终是可见的。可以使用提前深度测试(Early Depth Testing)轻松丢弃掉的片段能够节省我们很多宝贵的带宽。

        天空盒只是一个1x1x1的立方体,它很可能会不通过大部分的深度测试,导致渲染失败。我们需要欺骗深度缓冲,让它认为天空盒有着最大的深度值1.0,只要它前面有一个物体,深度测试就会失败。z分量等于顶点的深度值。

        TexCoords = aPos;

         vec4 pos = projection * view * vec4(aPos, 1.0);

         gl_Position = pos.xyww;

      这样天空盒就只会在没有物体的地方渲染了。

                                                                                             环境映射

      通过使用环境的立方体贴图,我们可以给物体反射和折射的属性。

      根据观察方向向量I和物体的法向量N,来计算反射向量R。我们可以使用GLSL内建的reflect函数来计算这个反射向量。最终的R向量将会作为索引/采样立方体贴图的方向向量,返回环境的颜色值。最终的结果是物体看起来反射了天空盒。

       物体的顶点着色器:   

        layout (location = 0) in vec3 aPos;

         layout (location = 1) in vec3 aNormal;

         out vec3 Normal;

          out vec3 Position;

          uniform mat4 model;

          uniform mat4 view;

          uniform mat4 projection;

          void main()

          {

                   Normal = mat3(transpose(inverse(model))) * aNormal;

                   Position = vec3(model * vec4(aPos, 1.0));

                  gl_Position = projection * view * model * vec4(aPos, 1.0);

          }

       改变物体的片段着色器就可以让物体有反射性:

        #version 330 core

        out vec4 FragColor;

         in vec3 Normal;

         in vec3 Position;

          uniform vec3 cameraPos;

          uniform samplerCube skybox;

           void main()

           {

                 vec3 I = normalize(Position - cameraPos);

                 vec3 R = reflect(I, normalize(Normal));

                  FragColor = vec4(texture(skybox, R).rgb, 1.0);

           }

         还可以使用反射贴图,它也是可以采样的纹理图像,它决定这片段的反射性。

         使用折射可以创建出类玻璃的效果,reflect换为refract就好。

动态环境贴图:

        通过使用帧缓冲,我们能够为物体的6个不同角度创建出场景的纹理,并在每个渲染迭代中将它们储存到一个立方体贴图中。之后我们就可以使用这个(动态生成的)立方体贴图来创建出更真实的,包含其它物体的,反射和折射表面了。这就叫做动态环境映射(Dynamic Environment Mapping),因为我们动态创建了物体周围的立方体贴图,并将其用作环境贴图。

 

参考自https://learnopengl-cn.github.io

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

零基础学习OpenGL(八)--立方体贴图、天空盒、环境映射 的相关文章

  • Maven打包所有依赖到一个可执行jar中,将外部依赖加入到classPath中

    首先说一下比较常用的两种打包方式 xff1a 前提 xff1a maven构建可执行jar包时 xff0c 如果项目依赖了pom中定义的dependency之外的外部jar包 xff0c maven jar plugin默认是不会把这 些额
  • postgresql数据库|数据库实操----表复制详解

    前言 xff1a 通常情况下 xff0c 我们对数据库的增删改查的时候 xff0c 为了确保数据的安全 xff0c 需要备份表 xff0c 那么 xff0c 一种方法是通过pg dump 这个工具做SQL转储操作 xff0c 此方法比较复杂
  • Centos7 配置防火墙 firewall

    一 firewall 1 从CentOS7开始 xff0c 默认使用firewall来配置防火墙 xff0c 没有安装iptables xff08 旧版默认安装 xff09 2 firewall的配置文件是以xml的格式 xff0c 存储在
  • Windows多媒体开发框架介绍

    Windows 多媒体开发框架介绍 欢迎来到 Windows 的多媒体开发世界2D 绘图 API1 GDI2 GDI 43 3 Direct2D 音频 API1 MME2 DirectSound3 Windows Core AudioCor
  • 【Ubuntu】在QT运行程序后无结果显示,只有终端运行的解决办法

    转自 http stackoverflow com questions 3255035 qt creator run in terminal https bugs launchpad net ubuntu 43 source qtcreat
  • 【蓝桥杯嵌入式】关于CT117E下载程序出问题解决方案(含keil mdk4和keil mdk5移植)

    废话 万事开头难 xff0c 然后中间难 xff0c 最后难 寒假刚开始 xff0c 我看到了蓝桥杯嵌入式 很快啊 xff01 报名 买板一气呵成 没想到这块CT117E板子它不讲武德 xff0c 来骗 xff0c 来偷袭我这个二十岁的小伙
  • c语言冒泡排序详解(分析每一步,附代码)

    冒泡排序 xff08 Bubble Sort xff09 xff0c 是一种计算机科学领域的较简单的排序算法 它重复地走访过要排序的元素列 xff0c 依次比较两个相邻的元素 xff0c 如果顺序 xff08 如从大到小 首字母从Z到A x
  • 解决maven update project 后项目jdk变成1.5

    一 问题描述 在Eclipse中新建了一个Maven工程 然后更改JDK版本为1 7 结果每次使用Maven gt Update project的时候JDK版本都恢复成1 5 二 原因分析 Maven官方文档有如下描述 xff1a 编译器插
  • C语言——整型和浮点型混合运算

    1 int和double混合运算 C语言int和double混合运算时 xff0c 会自动将int类型的数据转换为double类型的数据 xff0c 最后得到的结果也是double类型 如下例 xff1a double a 61 4 0 9
  • C语言——函数指针

    目录 1 函数指针概念 1 1 函数指针的声明 1 2 函数指针的定义 1 3 使用typedef定义函数指针的别名 1 4 将常数转换为函数指针 1 5 函数指针的调用 1 6 将函数指针作为函数的传入参数 2 简单的例子 1 函数指针概
  • C语言——多线程基础(pthread)

    目录 1 线程的定义以及线程的创建 1 1 线程和进程的概念 1 2 使用pthread create 函数创建进程 2 使用pthread join 等待线程结束 2 1 使用pthread join 等待线程结束 2 1 使用pthre
  • C++——双端队列(deque)

    1 双端队列 xff08 deque xff09 双端队列 xff08 deque xff09 是队列的一种变形 xff0c 一般队列只能在队尾添加元素 xff08 push xff09 xff0c 在队首删除元素 xff08 pop xf
  • Linux|集群初始化脚本--osiniit.sh简介

    前言 xff1a 不管是什么部署 xff0c 前期的准备工作通常都是比较繁琐的 xff0c 但同时这些工作又具有程式化的特征 xff0c 也就是说都是有一定的流程的 xff0c 固定的步骤的 OK xff0c shell脚本处理这样的程式问
  • C++——优先级队列(priority_queue)

    目录 1 优先级队列 xff08 priority queue xff09 1 1 基本概念 1 2 优先级队列的定义 1 3 通过重写仿函数来支持自定义数据类型 1 4 通过运算符重载来支持自定义比较函数 1 5 优先级队列的基本操作 2
  • 操作系统——进程状态

    进程从创建到执行 xff0c 再到执行完毕销毁的过程中 xff0c 经历了不同的进程状态 xff0c 进程状态部分取决于进程当前的活动 xff0c 可以将进程状态分为 xff08 1 xff09 三状态模型 xff1b xff08 2 xf
  • 操作系统——进程调度

    目录 1 基本概念 1 1 CPU I O执行周期 1 2 CPU调度程序 xff08 CPU scheduler xff09 1 3 进程状态模型 1 4 抢占调度 1 5 调度程序 xff08 dispatcher xff09 1 6
  • C++实现优先级队列模板类

    1 优先级队列 1 1 基本原理 仿照C 43 43 STL 中的优先级队列priority queue xff0c 主要实现以下功能 xff1a 向队列中添加元素后 xff0c 队列自动调整 xff0c 保证队列中优先级最高的元素在队列头
  • C++通过WMI获取计算机电池电压的程序以及我出现的问题解决

    C 43 43 通过WMI获取计算机电池电压的程序以及我出现的问题解决 ConsoleApplication1 cpp 定义控制台应用程序的入口点 include stdafx h include DXGI h include vector
  • 深度学习——批量归一化(笔记)

    主流的卷积网络基本都设计批量归一化这个层 1 为什么要批量归一化 xff1f 网络层很深 xff0c 数据在底层 xff0c 损失函数在最顶层 反向传播后 xff0c 顶层的梯度大 xff0c 所以顶层训练的较快 数据在底层 xff0c 底
  • Mininet连接ONOS的一些问题

    Mininet连接ONOS的一些问题 一 启动ONOS二 简单小问题1 Mininet创建最简拓扑后连接不上远程控制器2 Mininet创建最简拓扑后主机之间ping不通3 不启动fwd应用自己通过REST API下发流表发现两台主机间pi

随机推荐

  • 关于codeblocks找不到路径从而无法编译的解决方法

    下载完codeblocks后 xff0c 可能会出现像下面的情况 解决方法是 xff1a 点击codeblocks里的settings的Compiler xff08 1 xff09 可能是中间箭头蓝色的那里出错 xff0c 有些人可能会在安
  • 如何获取维普免费账号--在校外下载维普万方等全文

    如何获取维普免费账号 在校外下载维普万方等全文 如何获取维普免费账号 在校外下载维普万方等全文 1 马上就要工作了 xff0c 觉得学校里面图书馆资料很多 xff0c 并且很有用 xff0c 都可以在CNKI 万方维普等中文数据库 xff0
  • 京东网络开放之路——自研交换机探索与实践

    相比传统商业交换机 xff0c 白盒交换机具有更好的开放性和灵活性 xff0c 解决了运维管理方面的诸多痛点 xff0c 随着SONiC的不断完善和白盒交换机市场的成熟 xff0c 京东基于SONiC研发了自己的白盒交换机 xff0c 更加
  • Linux|sed命令花式批量修改文件

    前言 xff1a sed命令 xff0c 流式修改文本文件的强大工具 xff0c 比如 xff0c 最常用的关闭selinux时通常采用sed命令快速修改 OK xff0c 其实sed命令虽然强大 xff0c 但它修改文件的方式是非常多的
  • Python列表详解_List()

    Python列表 一 了解列表二 列表的创建三 访问列表中的值四 对列表进行更新五 删除列表元素 一 了解列表 什么是列表 xff1f 列表能干什么 xff1f 今天我们几分钟统统解决他 xff01 1 首先我们先来了解一下列表 列表是Py
  • Linux(Deepin、Ubuntu)安装Citrix receiver教程

    Deepin20 6安装Citrix receiver教程 安装的deepin20 6想要安装Citrix receiver xff0c 发现要么装不上 xff0c 要么装上了不能用 xff0c 找了好多资料 xff0c 最终找到可以使用的
  • 服务器(一):VNC view 无法打开终端问题

    1 VNC view 无法打开终端问题 参考链接 xff1a Ubuntu 的终端打不开 xff0c gnome terminal没反应 ubuntu 打开不了gnome terminal 显示错误提示exited status 8 Ubu
  • Hadoop 从零开始学习系列-wordCount

    学习基于hadoop2 5 2版本 WordCount做为hadoop的hello world程序 xff0c 今天花了半天时间 xff0c 终于跑出了结果 xff0c 以下记录过程 1 建立maven工程 xff0c 加载hadoop相关
  • 图像处理 - GLCM灰度共生矩阵如何计算

    因为最近要在OpenCV上试试各种图像分割算法的效果 xff0c 其中灰度共生算法没有办法直接调用库函数实现 xff0c 看了很多文章 xff0c 其中有一篇博文讲的很清楚 博主有提供原理和源码 xff0c 大家可以直接看原博 原文地址 x
  • SUSE12 Remote side unexpectedly closed network connection ,Connection reset by peer原因之一

    用工具新开ssh远程报错如下 xff1a 已连接的ssh中 xff0c telnet报错如下 同网段机器ssh命令报错如下 xff1a 在对比了同操作系统同文件的配置后 xff0c 排除了 etc hosts deny跟 etc hosts
  • xterm连接虚拟机里的ubuntu

    mobaxterm连接在虚拟机的abuntu 1 在Ubuntu上打开SSH服务 安装 openssh client 和 openssh server sudo apt get install openssh client sudo apt
  • 图解Linux命令之--fdisk命令

    fdisk命令 gt 磁盘分区管理工具 添加分区的流程 fdisk dev sda 选择要分区的硬盘 p 列出当前分区表 n 添加新分区 回车 选择开始的块地址 直接回车默认就可以了 43 2G 输入要添加分区的大小 43 200M xff
  • 安装docker-compose报ERROR: Cannot uninstall ‘PyYAML‘. It is a distutils installed project and thus we c

    在CentOS7中 xff0c 如果python版本为3 x xff0c 在安装docker compose时会报错 xff1a ERROR Cannot uninstall PyYAML It is a distutils install
  • PostgreSql | 数据库 |自定义函数的设计和实现

    前言 xff1a 数据库中的函数概念 和开发语言 xff0c Java xff0c PHP xff0c Python等等类似 xff0c 关系型数据库也是有函数的 xff0c 函数指的是动态的封装一部分特定功能的集合 例如 xff0c 查询
  • 元学习系列(一):Siamese Network(孪生网络)

    目前有一种说法认为 xff0c 深度学习模型在数据量较大的情况下才能取得较好的效果 xff0c 当数据量较少 xff0c 更偏向于使用传统的机器学习模型 想办法从深度学习的方向构建模型 xff0c 使得模型在数据量较小的情况下也能取得较好效
  • SpringBoot整合MybatisPlus时“注入失败”的问题记录

    问题情景 xff1a 最近将几个小的Demo整合在一起 xff0c 其中项目A使用Mybatis项目B使用Mybatis plus 在正常的修改完application yml xff0c pom文件后尝试启动项目 xff0c 启动失败 报
  • VasSonic之流式拦截

    VasSonic之流式拦截 VasSonic框架用到了流式拦截和增量更新技术 xff0c 下面只简单介绍流式拦截 xff0c 详细参考 xff1a https github com Tencent VasSonic wiki 一 xff09
  • 全文搜索引擎 Elasticsearch 入门

    全文搜索属于最常见的需求 xff0c 开源的 Elasticsearch xff08 以下简称 Elastic xff09 是目前全文搜索引擎的首选 它可以快速地储存 搜索和分析海量数据 维基百科 Stack Overflow Github
  • Ubuntu16.04 安装,更新与卸载Docker CE

    Ubuntu16 04 安装 xff0c 更新与卸载Docker CE 污污老师 关注 2017 11 14 23 40 字数 1019 阅读 3079评论 0喜欢 1 Docker CE 17 09 操作系统要求 xff1a 一个64位的
  • 零基础学习OpenGL(八)--立方体贴图、天空盒、环境映射

    立方体贴图 将多个纹理组合起来映射到一张纹理上的一种纹理类型 xff1a 立方体贴图 Cube Map 立方体贴图 xff1a 一个包含了6个2D纹理的纹理 xff0c 每个2D纹理都组成了立方体的一个面 xff1a 一个有纹理的立方体 之