搞定了天空盒,才算是真正完成了場景的搭建,以後再要進行什麼樣的圖學測試,都可以在這個場景下進行。比如後面的反射、折射就是這樣的例子。
寫完這篇,我決定暫時結束這個系列。主要是因為我太懶了,居然拖拖拉拉拖了兩年。其實可以探索的內容還有很多,比如陰影啊、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郵箱:[email protected]