Linux 下的 OpenGL 之路(九):天空盒、反射和折射
前言
搞定了天空盒,才算是真正完成了场景的搭建,以后再要进行什么样的图形学测试,都可以在这个场景下进行。比如后面的反射、折射就是这样的例子。
写完这篇,我决定暂时结束这个系列。主要是因为我太懒了,居然拖拖拉拉拖了两年。其实可以探索的内容还有很多,比如阴影啊、HDR啊、辉光啊、基于物理的渲染啊什么的,内容还挺多,我想的是等以后我把我的代码搞得更完善一点之后,可能重写这一个系列。
也有可能不重写这个系列了,直接进军 Vulkan。
天空盒的技术其实挺简单
在没有做天空盒的时候,觉得天空盒很神秘。做了天空盒之后,发现天空盒的技术其实挺简单,就是利用了立方体贴图而已。
先说模型,就是绘制六个面,组成一个盒子而已,唯一不同的,可能就是这个盒子的面都是向内的。至于盒子的大小,其实不重要,后来我算是想明白了,反正应用纹理之后,我们看到的都是一个封闭的世界背景,那么把这个背景放在坐标为1的位置,还是放在远处,其实没什么区别。唯一需要注意的是,需要在 Shader 里面修改它的深度值,让它绘制在所有物体的后面,这样,其实是给了我们一个天空盒在无穷远处的假象。
再说立方体贴图,其实就是 6 张图片,使用相应的 API 载入而已。
最后说一说在场景中漫游的问题。通过前面的描述,可以看出盒子的位置一定在原点,盒子的大小不重要,所以 Model Matrix 就没有必要要了。但是我们还是需要在场景中转动,所以 View Matrix 还是要考虑,但是又要和前面的 View Matrix 不同,也就是我们只考虑摄像机的转动,而不用考虑摄像机的移动。所以这个要单独处理。
下面直接贴代码:
#ifndef __SKYBOX_H__
#define __SKYBOX_H__
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
#include <string>
#include <string.h>
#include <GL/glew.h>
#include <iostream>
#include "model.hpp"
class SkyBox
{
protected:
GLuint VAO, VBO, textureID;
public:
GLuint getTextureID(){
return textureID;
}
void loadSkyBox(std::string directory)
{
std::cout << "Directory name: " << directory << std::endl;
float vertex_data[] = {
// positions
-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};
GLuint vertex_num = 36;
glCreateVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glCreateBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glNamedBufferStorage(VBO, sizeof(vertex_data), vertex_data, 0);
std::cout << "sizeof vertex_data:" << sizeof(vertex_data) << std::endl;
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
std::vector<std::string> faces{
"right.jpg",
"left.jpg",
"top.jpg",
"bottom.jpg",
"front.jpg",
"back.jpg"
};
glGenTextures(1, &textureID);
glActiveTexture(GL_TEXTURE0 + 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((directory + "/" + 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: " << directory + "/" + 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);
}
void render()
{
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
};
#endif
主文件的代码我就不贴了。下面来看看顶点着色器的代码和片段着色器的代码。
#version 330 core
layout (location = 0) in vec3 aPos;
out vec3 TexCoords;
uniform mat4 projection_matrix;
uniform mat4 view_matrix;
void main()
{
TexCoords = aPos;
vec4 tempPosition = projection_matrix * view_matrix * vec4(aPos, 1.0);
gl_Position = tempPosition.xyww;
}
#version 330 core
out vec4 FragColor;
in vec3 TexCoords;
uniform samplerCube skybox;
void main()
{
FragColor = texture(skybox, TexCoords);
}
关于反射和折射
关于反射和折射的计算,大家都懂,主要是涉及到视线方向和法线方向。当我知道了 GLSL 里面本来就有 reflect 函数和 refract 函数之后,我就觉得,这 TM 也实在是太简单了。
多的话不说,直接上图吧。


嗯,还是有点意思的。
总结
这个系列暂时就到这里结束吧,虽然我还有一点意犹未尽。让图形学的东西通过自己的代码在自己的屏幕上显示出来,还是挺激动人心的。以后我可能会将这个系列重整,也可能会添加一些东西,但是那就不知道是什么时候的事情了,可能要等我把手上的事情忙完,也可能那时候我又换了新电脑,换了新环境。
如果下次再重整这个系列,我可能会先解决在 OpenGL 里面显示文字的功能,然后显示个帧率看看,这样,可以让自己对各种图形学的算法和实现的效率有一个体验,也是不错的。
哦,对了,还有 VS Code 不得不提,真的是挺好用的。
版权申明
该随笔由京山游侠在2023年07月29日发布于博客园,引用请注明出处,转载或出版请联系博主。QQ邮箱:1841079@qq.com
Linux 下的 OpenGL 之路(九):天空盒、反射和折射的更多相关文章
- linux下Oracle11g RAC搭建(九)
linux下Oracle11g RAC搭建(九) 八.创建ASM仓储 相同在图形化界面操作 [root@node1 ~]# su - grid [grid@node1 ~]$ asmca //创 ...
- linux 下使用opengl的glut库显示和旋转BMP图片
效果图: 这里显示的图和原图有明显的色差,目前猜测是opengl渲染时的颜色表顺序跟BMP文件里的颜色表顺序相反导致. BMP里应该是BGRBGRBRG... ,而opengl渲染时应该是按照RGBR ...
- linux下的学习之路下的小困难
centos下源码安装python3wget --no-check-certificate https://www.python.org/ftp/python/3.6.2/Python-3.6.2.t ...
- linux 下的OpenGL的安装配置
https://blog.csdn.net/qq_38228254/article/details/78521155 本人亲测有效
- 怎样在Windows和Linux下写相同的代码
目前,Linux在国内受到了越来越多的业内人士和用户的青睐.相信在不久的将来,在国内为Linux开发 的应用软件将会有很大的增加(这不,金山正在招兵买马移植WPS呢).由于未来将会是Windows和L ...
- 【Oracle 集群】Linux下Oracle RAC集群搭建之基本测试与使用(九)
Oracle 11G RAC数据库安装(九) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总 ...
- 【转】【Oracle 集群】Linux下Oracle RAC集群搭建之基本测试与使用(九)
原文地址:http://www.cnblogs.com/baiboy/p/orc9.html 阅读目录 目录 检查RAC状态 检查创建的数据库 全部参考文献 相关文章 Oracle 11G RAC ...
- 单片机成长之路(51基础篇) - 006 在Linux下搭建51单片机的开发烧写环境
在Linux下没有像keli那样好用的IDE来开发51单片机,开发环境只能自己搭建了. 第一步:安装交叉编译工具 a) 安装SDCC sudo apt-get install sdcc b)测试SDC ...
- linux下递归删除目录下所有exe文件---从删库到跑路篇
linux下递归删除目录下所有exe文件 find . -name '*.exe' -type f -print -exec rm -rf {} \; (1) "." 表示从当前目 ...
- 局域网内Linux下开启ftp服务的“曲折路”和命令复习
今天主要学习了Linux下网络配置以及vsftp(FTP)和samba的服务配置,学习起来,难度也就一般,并没有特别难,可是在可以做实验的时候,却并没有自己想像的那么顺利,可见,很多事情看起来不难,做 ...
随机推荐
- Python 创建数字列表
创建数字列表 用于存储数字集合,高效地处理数字列表 使用函数range() range(value1,value2),从指定的第一个值开始数,到达指定的第二个值后停止,输出不包含第二个值 使用rang ...
- python的format方法中文字符输出问题
format方法的介绍 前言 提示:本文仅介绍format方法的使用和中文的输出向左右和居中输出问题 一.format方法的使用 format方法一般可以解决中文居中输出问题,假如我们设定宽度,当中文 ...
- 第139篇:JS数组常用方法(map(),reduce(),foreach())
好家伙,本篇为MDN文档数组方法的学习笔记 Array.prototype.reduce() - JavaScript | MDN (mozilla.org) 数组方法这块的知识缺了,补一下 1. ...
- 【Docker】容器管理
一.容器生命周期及启动过程 1.容器生命周期 2.容器启动过程 二.容器管理命令 Usage: docker [OPTIONS] COMMAND A self-sufficient runtime f ...
- 【H5】Emmet 指令 HTML
Emmet操作指南 HTML篇 生成带有内容的标签 标签名{内容}可以生成带有内容的标签 div{abc} <div>abc</div> 生成带有属性的标签 生成带有class ...
- PaddlePaddle 飞桨复现 VGG16
import paddle.nn as nn class VGG16(nn.Layer): def __init__(self, num_classes=1000): super(VGG16, sel ...
- 2021-11-23:规定:L[1]对应a,L[2]对应b,L[3]对应c,...,L[25]对应y。 S1 = a, S(i) = S(i-1) + L[i] + reverse(invert(S(
2021-11-23:规定:L[1]对应a,L[2]对应b,L[3]对应c,-,L[25]对应y. S1 = a, S(i) = S(i-1) + L[i] + reverse(invert(S(i- ...
- vue全家桶进阶之路31:Vue3 数据和方法的双向绑定ref、reactive、toRefs
ref 在 Vue 3 中,你可以使用 setup 函数来定义组件的数据和方法.在 setup 函数中,你可以使用 ref.reactive 和 computed 等 Vue 3 的响应式 API 来 ...
- ArcGIS如何自动获得随机采样点?
本文介绍基于ArcMap软件,实现在指定区域自动生成随机点的方法. 在GIS应用中,我们时常需要在研究区域内进行地理数据的随机采样:而采样点的位置往往需要在结合实际情况的前提下,用计算机随机生 ...
- Kubernetes 架构原则和对象设计
Kubernet¶ Kubernetes 架构原则和对象设计¶ 什么是云计算¶ 云计算平台的分类¶ 以Openstack为典型的虚拟化平台 虚拟机构建和业务代码部署分离. 可变的基础架构使后续维护风险 ...