前言
搞定了天空盒,才算是真正完成了场景的搭建,以后再要进行什么样的图形学测试,都可以在这个场景下进行。比如后面的反射、折射就是这样的例子。
写完这篇,我决定暂时结束这个系列。主要是因为我太懒了,居然拖拖拉拉拖了两年。其实可以探索的内容还有很多,比如阴影啊、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