OpenGL教程:14. 立方体贴图和天空盒

Indexing/Sampling from a cubemap in OpenGL

立方体贴图是指一个立方体的贴图,一共有六个面。这种贴图一般是立方体的展开图:

Image of a skybox for a cubemap in OpenGL

但是OpenGL不会自动裁剪这个贴图,所以你需要手动裁剪,然后读取。

假如裁剪后得到的序列如下:

1
2
3
4
5
6
7
8
9
vector<std::string> faces;
{
"right.jpg",
"left.jpg",
"top.jpg",
"bottom.jpg",
"front.jpg",
"back.jpg"
};

我们写一个函数来读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
unsigned int loadSkyBox(std::vector<std::string> &faces) {
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

int width, height, nrComponents;
for (unsigned int i = 0; i < faces.size(); i++) {
unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrComponents, 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 << "Error: Fail to load skybox !\n";
stbi_image_free(data);
exit(0);
}
}
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);
return textureID;
}

顺序分别是:

1
2
3
4
5
6
7
8
// loads a cubemap texture from 6 individual texture faces
// order:
// +X (right)
// -X (left)
// +Y (top)
// -Y (bottom)
// +Z (front)
// -Z (back)

之后记得激活texture:

1
2
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyBoxTextureID);

利用VAO、VBO:

1
2
3
4
5
6
7
8
9
10
glGenVertexArrays(1, &skyBoxVAO);
glBindVertexArray(skyBoxVAO);
glGenBuffers(1, &skyBoxVBO);
glBindBuffer(GL_ARRAY_BUFFER, skyBoxVBO);
glBufferData(GL_ARRAY_BUFFER,
sizeof(skyBoxVertices),
&skyBoxVertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) (nullptr));

绘制:

1
2
3
4
glDepthFunc(GL_EQUAL);
glBindVertexArray(skyBoxVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glDepthFunc(GL_LESS);

效果:

image-20221116125558861

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
void initSkyBox() {
std::vector<std::string> faces{
"textures/skybox/right.jpg",
"textures/skybox/left.jpg",
"textures/skybox/top.jpg",
"textures/skybox/bottom.jpg",
"textures/skybox/front.jpg",
"textures/skybox/back.jpg"
};
float skyBoxVertices[] = {
// 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
};

glGenVertexArrays(1, &skyBoxVAO);
glBindVertexArray(skyBoxVAO);
glGenBuffers(1, &skyBoxVBO);
glBindBuffer(GL_ARRAY_BUFFER, skyBoxVBO);
glBufferData(GL_ARRAY_BUFFER,
sizeof(skyBoxVertices),
&skyBoxVertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) (nullptr));

skyBoxTextureID = loadSkyBox(faces);
skyBoxShader->use();
skyBoxShader->setInt("skyBox", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyBoxTextureID);
}
unsigned int loadSkyBox(std::vector<std::string> &faces) {
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

int width, height, nrComponents;
for (unsigned int i = 0; i < faces.size(); i++) {
unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrComponents, 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 << "Error: Fail to load skybox !\n";
stbi_image_free(data);
exit(0);
}
}
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);
return textureID;
}
void displaySkyBox() {
view = glm::mat4(glm::mat3(camera.getView()));// remove translation from the view matrix
skyBoxShader->use();
skyBoxShader->set4Matrix("view", view);
skyBoxShader->set4Matrix("projection", projection);

glDepthFunc(GL_EQUAL);
glBindVertexArray(skyBoxVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glDepthFunc(GL_LESS);
}

着色器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//vsh
#version 330 core
layout (location = 0) in vec3 aPos;

out vec3 TexCoords;
//out vec3 position;
uniform mat4 view;
uniform mat4 projection;

void main(){
TexCoords = aPos;
vec4 pos = projection * view * vec4(aPos, 1.0);
gl_Position = pos.xyww;
}

//fsh
#version 330 core
in vec3 TexCoords;
out vec4 FragColor;
uniform samplerCube skyBox;

void main(){
FragColor = texture(skyBox, TexCoords);
}
Author

InverseDa

Posted on

2022-11-13

Updated on

2023-03-30

Licensed under

Comments

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×