计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 09.天空和背景

天空和背景

对于 3D 场景,通常可以通过在远处的地平线附近创造一些逼真的效果,来增强其真实感。

我们可以采用天空盒、天空柱(Skydome)或天空穹(Skydome)等技术来模拟天空。

天空盒

天空盒(Skybox)是一种在3D图形渲染中用于模拟远处背景的技术。它通过将场景包裹在一个巨大的立方体(或球体)中,并在其内表面贴上纹理来实现。天空盒通常用于表示天空、云、山脉或其他远景背景。

工作原理:

  1. 立方体模型:天空盒通常是一个立方体,摄像机位于其中心。
  2. 纹理贴图:立方体的六个面分别贴上对应的纹理(前、后、左、右、上、下),这些纹理拼接在一起形成完整的背景。
  3. 固定位置:天空盒始终跟随摄像机移动,但不会旋转或缩放,从而给人一种背景无限远的错觉。
  4. 渲染顺序:天空盒通常在渲染场景之前绘制,并禁用深度测试,以确保它始终位于场景的最远处。

优点:

  • 高效:天空盒的实现简单,性能开销低。
  • 真实感:可以通过高质量纹理提供逼真的背景效果。
  • 灵活性:适用于各种场景,如白天、夜晚、宇宙等。

缺点:

  • 分辨率限制:纹理分辨率过低可能导致模糊或失真。
  • 接缝问题:如果纹理拼接不当,可能会在立方体的边缘出现接缝。

天空盒广泛应用于游戏和虚拟现实中,用于增强场景的沉浸感和视觉效果。

对于天空盒,可以有两下两种实现方式:

  1. 采用6张图片,对应立方体的六个面,分别贴上图片,然后渲染。
  2. 采用一张图片,将图片贴在立方体的六个面,然后渲染。

我们先采用第二种方式,实现天空盒。
下面是将6张图片放到一张图片上形成的纹理

其与立方体六个面的关系如下:

实现思路

  1. 创建一个立方体模型,设置其纹理坐标,使其与天空盒纹理对应。
  2. 创建一个纹理对象,将天空盒纹理加载到该对象中。
  3. 在渲染循环中,将纹理对象绑定到着色器,并绘制立方体模型。
  4. 立方体的中心位置始终与摄像机的位置相同。在摄像机移动时,更新立方体的位置,使其始终跟随摄像机。
  5. 渲染时,不要启用深度测试,以确保天空盒始终位于场景的最远处。
  6. 由于摄像机是在内部,而我们定义立方体时,是从外部定义,外部立方体三角形是逆时针,当我们从内部看时,需要将三角形定义为顺时针

4

立方体的坐标
float cubeVertexPositions[108] ={	-1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f, 1.0f,  1.0f, -1.0f, -1.0f,  1.0f, -1.0f,1.0f, -1.0f, -1.0f, 1.0f, -1.0f,  1.0f, 1.0f,  1.0f, -1.0f,1.0f, -1.0f,  1.0f, 1.0f,  1.0f,  1.0f, 1.0f,  1.0f, -1.0f,1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f, 1.0f,  1.0f,  1.0f,-1.0f, -1.0f,  1.0f, -1.0f,  1.0f,  1.0f, 1.0f,  1.0f,  1.0f,-1.0f, -1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,  1.0f,-1.0f, -1.0f, -1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f,-1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,-1.0f,  1.0f, -1.0f, 1.0f,  1.0f, -1.0f, 1.0f,  1.0f,  1.0f,1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f, -1.0f};float cubeTextureCoord[72] ={	1.00f, 0.6666666f, 1.00f, 0.3333333f, 0.75f, 0.3333333f,	// back face lower right0.75f, 0.3333333f, 0.75f, 0.6666666f, 1.00f, 0.6666666f,	// back face upper left0.75f, 0.3333333f, 0.50f, 0.3333333f, 0.75f, 0.6666666f,	// right face lower right0.50f, 0.3333333f, 0.50f, 0.6666666f, 0.75f, 0.6666666f,	// right face upper left0.50f, 0.3333333f, 0.25f, 0.3333333f, 0.50f, 0.6666666f,	// front face lower right0.25f, 0.3333333f, 0.25f, 0.6666666f, 0.50f, 0.6666666f,	// front face upper left0.25f, 0.3333333f, 0.00f, 0.3333333f, 0.25f, 0.6666666f,	// left face lower right0.00f, 0.3333333f, 0.00f, 0.6666666f, 0.25f, 0.6666666f,	// left face upper left0.25f, 0.3333333f, 0.50f, 0.3333333f, 0.50f, 0.0000000f,	// bottom face upper right0.50f, 0.0000000f, 0.25f, 0.0000000f, 0.25f, 0.3333333f,	// bottom face lower left0.25f, 1.0000000f, 0.50f, 1.0000000f, 0.50f, 0.6666666f,	// top face upper right0.50f, 0.6666666f, 0.25f, 0.6666666f, 0.25f, 1.0000000f		// top face lower left};
渲染代码(部分) 绘制立方体
void display()
{//...//立方体的位置始终同摄像机位置相同mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cameraX, cameraY, cameraZ))glDisable(GL_DEPTH_TEST); // 关闭深度测试glEnable(GL_CULL_FACE); // 开启面剔除glFrontFace(GL_CCW); // 设置正面为顺时针glDrawArrays(GL_TRIANGLES, 0, 36); // 绘制三角形}

顶点着色器代码

顶点着色器相对简单,只是将顶点位置和纹理坐标传递给片段着色器。

#version 430
// 指定 GLSL 的版本为 4.30layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord; // 输入变量,表示顶点的颜色,绑定到 location = 1uniform mat4 mv_matrix;
// uniform 变量,表示模型-视图矩阵,用于将顶点从模型空间变换到视图空间uniform mat4 proj_matrix;
// uniform 变量,表示投影矩阵,用于将顶点从视图空间变换到裁剪空间out vec2 tc;
// 输出变量,表示顶点的颜色,绑定到 location = 0
void main(void)
// 主函数,计算顶点的最终位置
{gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);// 将顶点位置从模型空间依次变换到视图空间和裁剪空间// 最终结果存储在内置变量 gl_Position 中,用于后续的光栅化阶段tc = texCoord;}

片段着色器代码

#version 430
// 指定 GLSL 的版本为 4.30in vec2 tc;out vec4 color;
// 输出变量,表示片段的最终颜色uniform mat4 mv_matrix;
// uniform 变量,模型-视图矩阵(未使用)uniform mat4 proj_matrix;
// uniform 变量,投影矩阵(未使用)
layout (binding=0) uniform sampler2D tex0;
//uniform sampler2D tex0;void main(void)
// 主函数,计算片段的最终颜色
{color = texture(tex0, tc);
}

下图上方能看到立方体的接缝

使用 OpenGL 立方体贴图

用 OpenGL 立方体贴图有自己的优点,例如可以减少接缝以及支持环境贴图
OpenGL 纹理立方体贴图类似于稍后将要研究的3D 纹理,它们都使用带有3 个变量的纹理坐标访问——通常标记为**(s, t, r)**,而不是我们目前为止用到的带有两个变量的纹理坐标。OpenGL立方体贴图的另一个特性是,其中的图像以纹理图像的左上角而不是通常的左下角)作为纹理坐标(0, 0, 0)

实现思路

  1. 创建一个立方体模型,无需额外立方体纹理坐标,立方体顶点坐标就是纹理坐标
  2. 创建一个纹理对象(片段着色器中 samplerCube),将6张天空盒图片加载到该对象中。
  3. 在渲染循环中,将纹理对象绑定到着色器,并绘制立方体模型。
  4. 立方体的中心位置始终与摄像机的位置相同。在摄像机移动时,更新立方体的位置,使其始终跟随摄像机。
  5. 渲染时,不要启用深度测试,以确保天空盒始终位于场景的最远处。
  6. 由于摄像机是在内部,而我们定义立方体时,是从外部定义,外部立方体三角形是逆时针,当我们从内部看时,需要将三角形定义为顺时针

采样器类型

采样器类型维度主要用途特点
sampler2D2D普通2D纹理采样• 用于常规2D纹理映射
• 返回(r,g,b,a)四个分量
• 最常用的纹理采样器类型
samplerCube3D立方体贴图采样• 用于环境映射、天空盒等
• 使用3D向量作为采样坐标
• 六个面的纹理组合成立方体
sampler2DShadow2D阴影贴图采样• 专门用于阴影映射
• 返回单个深度值(0.0到1.0)
• 自动进行深度值比较
• 通常与深度纹理配合使用

代码实现

以下是运行效果

加载6张天空盒图片
GLuint Utils::loadCubeMap(const char* mapDir) {GLuint textureRef;string xp = mapDir; xp = xp + "/xp.jpg";string xn = mapDir; xn = xn + "/xn.jpg";string yp = mapDir; yp = yp + "/yp.jpg";string yn = mapDir; yn = yn + "/yn.jpg";string zp = mapDir; zp = zp + "/zp.jpg";string zn = mapDir; zn = zn + "/zn.jpg";textureRef = SOIL_load_OGL_cubemap(xp.c_str(), xn.c_str(), yp.c_str(), yn.c_str(), zp.c_str(), zn.c_str(),SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);if (textureRef == 0) cout << "didnt find cube map image file" << endl;//	glBindTexture(GL_TEXTURE_CUBE_MAP, textureRef);// reduce seams//	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 textureRef;
}
渲染立方体

此部份与之前代码基本相同,只是绑定 GL_TEXTURE_CUBE_MAP

    glActiveTexture(GL_TEXTURE0);                    // 激活纹理单元 glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture); // 绑定纹理对象glDisable(GL_DEPTH_TEST);          // 关闭深度测试glEnable(GL_CULL_FACE);            // 开启面剔除glFrontFace(GL_CCW);               // 设置正面为顺时针
顶点着色器

此处采用 mat4(mat3(mv_matrix)) 来将模型-视图矩阵转换为模型-视图矩阵,去除平移部分,这样确保天空盒与摄像机始终处于同一位置


#version 430
// 指定 GLSL 的版本为 4.30
layout (location=0) in vec3 position; // 输入变量,表示顶点的三维位置,绑定到 location = 0out vec3 texCoord; // 输出变量uniform mat4 mv_matrix;
// uniform 变量,表示模型-视图矩阵,用于将顶点从模型空间变换到视图空间
uniform mat4 proj_matrix;
// uniform 变量,表示投影矩阵,用于将顶点从视图空间变换到裁剪空间
void main(void)
// 主函数,计算顶点的最终位置
{mat4 vrot_matrix=mat4(mat3(mv_matrix)); //remove the translation partgl_Position = proj_matrix * vrot_matrix * vec4(position,1.0);// 将顶点位置从模型空间依次变换到视图空间和裁剪空间// 最终结果存储在内置变量 gl_Position 中,用于后续的光栅化阶段texCoord = position;
}
片段着色器

片段着色器中 只是进行纹理采样

#version 430
// 指定 GLSL 的版本为 4.30in vec3 texCoord; // 输入变量,表示顶点对应的纹理坐标
out vec4 fragColor; // 输出变量,表示片元最终的颜色
uniform samplerCube texCube; // 纹理采样器,表示立方体贴图
void main(void)
{fragColor = texture(texCube, texCoord); // 采样立方体贴图,得到片元的颜色
}

环境贴图

环境贴图概述

环境贴图是一种模拟物体表面反射周围环境的渲染技术,主要用于实现镜面反射、金属材质等效果。

工作原理

反射原理

  • 通过采集物体周围环境的图像信息
  • 根据视角和表面法线计算反射向量
  • 使用反射向量从立方体贴图中采样颜色

主要应用场景

  1. 镜面物体

    • 镜子
    • 金属表面
    • 光滑水面
  2. 金属材质

    • 车身漆面
    • 金属器皿
    • 珠宝首饰

优缺点

优点
  • 渲染效率高
  • 可以实现逼真的反射效果
  • 适合实时渲染
缺点
  • 无法实现真实的反射折射
  • 环境贴图分辨率限制细节表现
  • 难以实现动态场景的实时反射

常见变体

  1. 球形环境贴图

    • 使用单张球形投影的图像
    • 实现简单但有畸变
  2. 立方体环境贴图

    • 使用六张图构成立方体
    • 质量更好,无畸变问题
  3. 动态环境贴图

    • 实时渲染场景到环境贴图
    • 可实现动态反射效果

相应实现原理

顶点着色器

#version 430
// 指定 GLSL 的版本为 4.30layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord; // 输入变量,表示顶点的颜色,绑定到 location = 1
layout (location=2) in vec3 normal; // 输入变量,表示顶点的法线,绑定到 location = 2
// 输入变量,表示顶点的三维位置,绑定到 location = 0uniform mat4 mv_matrix;
// uniform 变量,表示模型-视图矩阵,用于将顶点从模型空间变换到视图空间uniform mat4 proj_matrix;
// uniform 变量,表示投影矩阵,用于将顶点从视图空间变换到裁剪空间uniform mat4 normal_matrix;
out vec2 tc;out vec3 fragNormal;out vec3 vertPos; 
void main(void)
// 主函数,计算顶点的最终位置
{vertPos=(mv_matrix*vec4(position,1.0)).xyz;gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);// 将顶点位置从模型空间依次变换到视图空间和裁剪空间// 最终结果存储在内置变量 gl_Position 中,用于后续的光栅化阶段tc = texCoord;fragNormal = mat3(normal_matrix) * normal;// 将法线从模型空间变换到视图空间}

片段着色器

核心代码为 vec3 R = -reflect(V, N); 其中 reflect 函数的第一个参数为入射向量,第二个参数为法线向量,返回值为反射向量。

#version 430
// 指定 GLSL 的版本为 4.30in vec2 tc;in vec3 fragNormal;
in vec3 vertPos;
out vec4 color;
// 输出变量,表示片段的最终颜色uniform mat4 mv_matrix;
// uniform 变量,模型-视图矩阵(未使用)uniform mat4 proj_matrix;
// uniform 变量,投影矩阵(未使用)
layout (binding=0) uniform samplerCube tex0;void main(void)
// 主函数,计算片段的最终颜色
{vec3 N = normalize(fragNormal);vec3 V = normalize(-vertPos); // 视线方向vec3 R = -reflect(V, N); // 反射方向color = texture(tex0, R);// 采样环境贴图,获取反射颜色//color=vec4(R,1.0); // 仅用于调试,显示反射方向
}

参考

  1. 学习笔记完整代码下载
  2. OpenGL shader开发实战学习笔记:第十一章 立方体贴图和天空盒_opengl 天空盒-CSDN博客

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

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

相关文章

【Leetcode 每日一题】1550. 存在连续三个奇数的数组

问题背景 给你一个整数数组 a r r arr arr&#xff0c;请你判断数组中是否存在连续三个元素都是奇数的情况&#xff1a;如果存在&#xff0c;请返回 t r u e true true&#xff1b;否则&#xff0c;返回 f a l s e false false。 数据约束 1 ≤ a r r . l e n g t h ≤ 10…

面试题解析 | C++空类的默认成员函数(附生成条件与底层原理)

在C面试中&#xff0c;“空类默认生成哪些成员函数”是考察对象模型和编译器行为的高频题目。许多资料仅提及前4个函数&#xff0c;但完整的答案应包含6个核心函数&#xff0c;并结合C标准深入解析其生成规则与使用场景。 一、空类默认生成的6大成员函数 1. ​缺省构造函数​ …

视频编解码学习7之视频编码简介

视频编码技术发展历程与主流编码标准详解 视频编码技术是现代数字媒体领域的核心技术之一&#xff0c;它通过高效的压缩算法大幅减少了视频数据的体积&#xff0c;使得视频的存储、传输和播放变得更加高效和经济。从早期的H.261标准到最新的AV1和H.266/VVC&#xff0c;视频编码…

使用Stable Diffusion(SD)中,步数(Steps)指的是什么?该如何使用?

Ⅰ定义&#xff1a; 在Stable Diffusion&#xff08;SD&#xff09;中&#xff0c;步数&#xff08;Steps&#xff09; 指的是采样过程中的迭代次数&#xff0c;也就是模型从纯噪声一步步“清晰化”图像的次数。你可以理解为模型在画这张图时“润色”的轮数。 Ⅱ步数的具体作…

消息队列如何保证消息可靠性(kafka以及RabbitMQ)

目录 RabbitMQ保证消息可靠性 生产者丢失消息 MQ丢失消息 消费端丢失了数据 Kakfa的消息可靠性 生产者的消息可靠性 Kakfa的消息可靠性 消费者的消息可靠性 RabbitMQ保证消息可靠性 生产者丢失消息 1.事务消息保证 生产者在发送消息之前&#xff0c;开启事务消息随后生…

如何查看项目是否支持最新 Android 16K Page Size 一文汇总

前几天刚聊过 《Google 开始正式强制 Android 适配 16 K Page Size》 之后&#xff0c;被问到最多的问题是「怎么查看项目是否支持 16K Page Size」 &#xff1f;其实有很多直接的方式&#xff0c;但是最难的是当你的项目有很多依赖时&#xff0c;怎么知道这个「不支持的动态库…

HttpServletResponse的理解

HttpServletResponse 是 Java Servlet API 提供的一个接口 常用方法 方法用途setContentType(String type)设置响应内容类型&#xff08;如 "application/json"、"text/html"&#xff09;setStatus(int sc)设置响应状态码&#xff08;如 200、404&#x…

可灵 AI:开启 AI 视频创作新时代

在当今数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的速度渗透到各个领域&#xff0c;尤其是在内容创作领域&#xff0c;AI 的应用正引发一场革命性的变革。可灵 AI 作为快手团队精心打造的一款前沿 AI 视频生成工具&#xff0c;宛如一颗璀璨的…

用 AltSnap 解锁 Windows 窗口管理的“魔法”

你有没有遇到过这样的场景&#xff1a;电脑屏幕上堆满了窗口&#xff0c;想快速调整它们的大小和位置&#xff0c;却只能拖来拖去&#xff0c;费时又费力&#xff1f;或者你是个多任务狂魔&#xff0c;喜欢一边写代码、一边看文档、一边刷视频&#xff0c;却发现 Windows 自带的…

深度策略梯度算法PPO

一、策略梯度核心思想和原理 从时序差分算法Q学习到深度Q网络&#xff0c;这些算法都侧重于学习和优化价值函数&#xff0c;属于基于价值的强化学习算法&#xff08;Value-based&#xff09;。 1. 基于策略方法的主要思想&#xff08;Policy-based&#xff09; 基于价值类方…

【LaTeX】Word插入LaTeX行间公式如何编号和对齐

在 Word 文档中插入公式&#xff0c;需要用到 LaTeX \LaTeX LATE​X 。但遗憾的是&#xff0c;Word 只支持部分 LaTeX \LaTeX LATE​X 语法&#xff0c;这就导致很多在 Markdown 能正常渲染的公式在 Word 中无法正常显示。 “内嵌”和“显示” 首先介绍一下 Word 的“内嵌”…

互联网大厂Java面试实战:Spring Boot到微服务的技术问答解析

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…

spring boot3.0自定义校验注解:文章状态校验示例

文章目录 Spring Boot 自定义校验注解&#xff1a;状态校验示例一、创建 State 注解步骤&#xff1a;1. 创建自定义注解&#xff1a;2. 实现校验逻辑&#xff1a; 二、 实现自定义校验步骤:1. 在实体类中使用自定义校验注解 State&#xff1a;2. 添加 State 注解&#xff1a; 总…

无侵入式弹窗体验_探索 Chrome 的 Close Watcher API

1. 引言 在网页开发中,弹窗(Popup)是一种常见的交互方式,用于提示用户进行操作、确认信息或展示关键内容。然而,传统的 JavaScript 弹窗方法如 alert()、confirm() 和 prompt() 存在诸多问题,包括阻塞主线程、样式不可定制等。 为了解决这些问题,Chrome 浏览器引入了 …

调出事件查看器界面的4种方法

方法1. 方法2. 方法3. 方法4.

Ubuntu 安装远程桌面连接RDP方式

1. 安装 XFCE4 桌面环境 如果你的 Ubuntu 系统默认使用 GNOME 或其它桌面环境&#xff0c;可以安装轻量级的 XFCE4&#xff1a; sudo apt update sudo apt install xfce4 xfce4-goodies 说明&#xff1a;xfce4-goodies 包含额外的插件和工具&#xff08;如面板插件、终端等&a…

LWIP传输层协议笔记

传输协议简介 文件/图片/视频 都是一堆二进制数据 经过传输层来传输 这两种协议有什么区别呢&#xff1f; 传输层的TCP/UDP三个步骤 TCP使用传输流程 1、三次握手 作用&#xff1a;三次握手就是建立连接的过程 2、传输数据 作用&#xff1a;建立连接完成之后&#xff…

数据分析与逻辑思维:六步解决业务难题;参考书籍《数据分析原理:6步解决业务分析难题 (周文全, 黄怡媛, 马炯雄)》

文章目录 一、懂业务&#xff1a;业务背景与逻辑前提1.1 明确业务目标与问题定义1.2 培养批判性思维与高于业务视角 二、定指标&#xff1a;构建科学的指标体系2.1 指标拆解与维度分析2.2 典型指标体系案例&#xff1a;用户与业务视角 三、选方法&#xff1a;匹配业务需求的分析…

开启WSL的镜像网络模式

开启WSL的镜像网络模式 前提 Windows主机系统版本高于Windows 11 22H2。WLS版本>2.0。 可输入wsl --version查看当前系统wsl版本。 修改设置 图形界面修改 在开始菜单中搜索&#xff1a;wsl settings&#xff0c;结果如下图所示&#xff1a; 点击“打开”&#xff0…

Python爬虫第20节-使用 Selenium 爬取小米商城空调商品

目录 前言 一、 本文目标 二、环境准备 2.1 安装依赖 2.2 配置 ChromeDriver 三、小米商城页面结构分析 3.1 商品列表结构 3.2 分页结构 四、Selenium 自动化爬虫实现 4.1 脚本整体结构 4.2 代码实现 五、关键技术详解 5.1 Selenium 启动与配置 5.2 页面等待与异…