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

OpenGL shader开发实战学习笔记:第十一章 立方体贴图和天空盒

1. 立方体贴图和天空盒

1.1. 什么是立方体贴图

立方体贴图(Cube Map)是一种纹理,它由六个纹理图像组成,每个纹理图像对应一个方向。这些方向通常是立方体的六个面,分别是“前面”,“后面”,“右面”,“左面”,“上面”,“下面”。

1.1.1. 前面

1.1.2. 后面

1.1.3. 右面

1.1.4. 左面

1.1.5. 上面

1.1.6. 下面

1.2. 如何对立方体贴图进行采样?

1.2.1. 立方体贴图的存储结构

立方体贴图(Cube Map)本质上是由六个 2D 纹理组成,它们分别对应一个立方体的六个面,即前面(Positive Z)、后面(Negative Z)、右面(Positive X)、左面(Negative X)、上面(Positive Y)和下面(Negative Y)。这六个纹理按照特定的方向排列,形成一个包围空间的立方体。在 OpenGL 中,立方体贴图使用 GL_TEXTURE_CUBE_MAP 类型来表示,每个面都有对应的纹理目标,如下所示:

  • 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 :后面

1.2.2. 方向向量的作用

对立方体贴图采样时,需要使用一个三维的方向向量。这个向量从立方体的中心出发,指向空间中的某个方向。OpenGL 根据这个方向向量来确定应该从立方体贴图的哪个面进行采样。具体来说,方向向量的最大分量决定了采样的面,例如:

  • 如果 x 分量的绝对值最大且为正,那么从右面采样。
  • 如果 x 分量的绝对值最大且为负,那么从左面采样。
  • 以此类推,根据 y 和 z 分量的正负和大小确定其他面。
  • 在顶点着色器中,每个顶点的位置也可以看为从网络模型空间原点开始的一个向量,由于立方体网格中的顶点都以原点为中心,因此网格的顶点位置也可以看为从立方体中心出发的一个向量。

1.2.3. 顶点着色器中的计算

#version 410
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);
}

1.2.4. 片段着色器中的计算

#version 410
out vec4 FragColor;in vec3 TexCoords;uniform samplerCube skybox;void main()
{    // 对立方体贴图进行采样FragColor = texture(skybox, TexCoords);
}

1.3. 在openFrameworks 中加载立方体贴图

由于openFrameworks 中没有提供直接加载立方体贴图的函数,我们需要自己实现。具体见附书代码。
此处仅列出如何使用

ofxEasyCubemap cubemap; // 创建立方体贴图对象
cubemap.load(	"cube_front.jpg", "cube_back.jpg", "cube_right.jpg", "cube_left.jpg", "cube_top.jpg", "cube_bottom.jpg"); // 加载立方体贴图// 需要显式地调用getTexture()函数,才能获取到纹理对象  
shd.setUniformTexture("envMap", cubemap.getTexture(), 0);

1.4. 立方体贴图效果

1.4.1. ofApp.cp

void ofApp::setup()
{ofDisableArbTex();ofEnableDepthTest();//cubemapMesh.load("cube.ply");cubemapMesh.load("cube_28_vertex.ply");cubemapShader.load("cubemap.vert", "cubemap.frag");cubemap.load(	"cube_front.jpg", "cube_back.jpg", "cube_right.jpg", "cube_left.jpg", "cube_top.jpg", "cube_bottom.jpg"); // 加载立方体贴图directionalLightData.direction = glm::vec3(0.5f, -1.0f, -1.0f); directionalLightData.color = glm::vec3(1.0f, 1.0f, 1.0f); // 白色directionalLightData.intensity = 1.0f; // 强度		}void ofApp::drawCubemap()
{using namespace glm;cameraData.position=vec3(0, 0.0f, 1.0f);cameraData.fov=radians(90.f);float aspect=1024.0f/768.0f;//mat4 model=glm::rotate(1.0f,vec3(1,1,1))*scale(vec3(0.5,0.5,0.5));mat4 model = glm::rotate(glm::radians(45.0f), vec3(1,1, 0)) * scale(vec3(0.5, 0.5, 0.5));//mat4 model = glm::mat4(1.0f)*scale(vec3(0.5, 0.5, 0.5));//mat4 view=glm::inverse(glm::translate(cameraData.position))*rotate(glm::radians(-10.0f*(float)(ofGetElapsedTimef())), vec3(1,0, 0));mat4 view = glm::inverse(glm::translate(cameraData.position)) * rotate(glm::radians(-10.0f ), vec3(1, 0, 0));mat4 proj=glm::perspective(cameraData.fov,aspect,0.01f,100.0f);mat4 mvp=proj*view*model;vec3 lightDirection = getLightDirection(directionalLightData); // 获取光源方向mat3 normalMatrix = glm::transpose(glm::inverse(mat3(model)));cubemapShader.begin();cubemapShader.setUniformMatrix4f("mvp", mvp);cubemapShader.setUniform3f("lightDir", lightDirection);cubemapShader.setUniform3f("lightCol", directionalLightData.color*directionalLightData.intensity);cubemapShader.setUniformMatrix4f("setUniformMatrix4f", model); // 模型矩阵cubemapShader.setUniformTexture("cubemap", cubemap.getTexture(), 0); // 立方体贴图cubemapMesh.draw();cubemapShader.end();
}

1.4.2. cubemap.vert

#version 410layout (location = 0) in vec3 pos;uniform mat4 mvp;out vec3 texCoord;
void main() {gl_Position = mvp * vec4(pos, 1.0);texCoord = pos;
}

1.4.3. cubemap.frag

#version 410in vec3 texCoord;
uniform samplerCube cubemap;out vec4 fragColor;void main() {fragColor = texture(cubemap, texCoord);//fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

1.5. 天空盒

天空盒(Skybox)是一种用于模拟天空的技术,它通常由一个立方体组成,每个面都有一个纹理图像。天空盒的纹理图像通常是天空的全景图像,它可以包含天空的所有信息,例如天空的颜色,天空的亮度,天空的纹理等。

1.5.1. cube.ply

以下是天空盒使用的立方体网格数据(部份),从中可以看到,其面数并不为6面,以便于展示时类似于天空,而不是明显的立方体

3 0 1 2
3 3 4 5
3 6 7 8
3 9 10 11
3 12 13 14
3 5 15 16
3 0 2 17
3 3 5 18
3 6 8 19
3 9 11 20
3 12 14 21
3 5 16 22

通过这种方式创建的水面,看起来与背景图像不协调,因此,我们还需要对水面进行一些处理,使其看起来更加自然。

1.6. 创建立方体贴图反射

立方体贴图反射是计算机图形学中用于模拟物体表面反射环境效果的一种技术,借助立方体贴图来呈现物体周围环境在其表面的反射影像,让渲染出的物体更具真实感。下面从原理、实现步骤和应用场景来详细介绍。

1.6.1. 原理

  • 立方体贴图 :由六个 2D 纹理构成,分别对应立方体的六个面(前、后、左、右、上、下),这些纹理能存储环境的全景信息。例如,在一个室外场景里,立方体贴图可以存储天空、地面、周围建筑等环境信息。
  • 反射向量计算 :对于物体表面的每个点,依据该点的法线向量和视线方向向量计算出反射向量。这个反射向量就像现实中光线照射到物体表面后反射出去的方向。
  • 采样立方体贴图 :把反射向量作为方向向量,对立方体贴图进行采样,获取反射的颜色值,将该颜色值应用到物体表面的着色计算中。

下图展示了立方体贴图反射的运行结果

1.6.1.1. water.frag

核心代码:

vec3 viewDir=normalize(cameraPos-fragWorldPos);	// 视线方向
vec3 reflectDir=reflect(-viewDir,normal);	// 反射方向
vec3 diffuseCol=texture(envTex,reflectDir).xyz*lightCol*diffuse;

1.6.1.1. water.frag

核心代码:

vec3 viewDir=normalize(cameraPos-fragWorldPos);	// 视线方向
vec3 reflectDir=reflect(-viewDir,normal);	// 反射方向
vec3 diffuseCol=texture(envTex,reflectDir).xyz*lightCol*diffuse;

相关文章:

  • 计算机网络基础概论
  • Linux中docker容器拉取镜像失败解决方案
  • HCIP(OSPF )(2)
  • 第一篇:linux之虚拟环境与centos安装
  • 在ubuntu20.04上安装ros2
  • 【软考-系统架构设计师】OSI体系解析
  • 远程医疗系统安全升级:构建抗CC攻击的全方位防护网
  • SpringBoot_第十章(启动过程)
  • 代码学习总结(三)
  • 离散化--
  • 金融数据库转型实战读后感
  • 目标检测概述
  • AI在市场营销分析中的核心应用及价值,分场景详细说明
  • 【C++】深入浅出之继承
  • 召回率和精准率-找书的例子
  • 机器视觉在贴标机的应用
  • Flowable进阶
  • C++编译与链接:从源码到可执行文件的魔法之旅(Visual Studio实践)
  • 【嵌入式系统设计师(软考中级)】第一章:计算机系统基础知识(中)
  • 对于“人工智能+教育”的一些思考
  • 工人日报评规范隐藏式车门把手:科技美学须将安全置顶
  • 寒武纪陈天石:公司的产品力获得了行业客户广泛认可,市场有望迎来新增量需求
  • 城市轨道交通安全、内河港区布局规划、扎实做好防汛工作……今天的上海市政府常务会议研究了这些重要事项
  • 影子调查丨三名“淘金客”殒命雪峰山:千余废弃金矿洞的监管难题
  • 外交部就习近平主席将出席中拉论坛第四届部长级会议开幕式介绍情况
  • 竞彩湃|热刺、曼联一周双赛不易,勒沃库森能否欢送阿隆索