天空盒(Skybox)是一种高效渲染环境的核心技术,它通过在立方体表面贴合精心处理的环境纹理,创造出无限延展的世界幻觉。当你在其中转动视角时,这些环境纹理会随着视角变化而呈现正确的透视关系,让人错觉自己置身于一个无边无际的世界中。
相比传统的3D环境模型,天空盒进需要36个顶点即可创建完整的环境体验。对于天空盒的绘制,我们需要用到6张不同的图片作为立方体不同面的纹理,这6张图片构成我们完整的环境。
在OpenGL中,对于天空盒的绘制有两种方法,一种是直接绘制一个立方体,然后对立方体每个面贴不同的图片,另一种是通过 cubemap 实现天空盒的绘制。在这里,我们使用第一种方式进行天空盒的实现。
- 首先,我们需要了解所使用的纹理的分布:

上面的图就是我们所用到的天空盒的纹理,我们需要将这张图绘制到立方体上面去,并且还是有顺序的进行绘制,下面是对应的UV坐标。

float skyboxVertices[180] = {
// +Z (Front Face)
-1.0f, -1.0f, 1.0f, 0.25f, 1.0f/3.0f, // 左下
1.0f, -1.0f, 1.0f, 0.50f, 1.0f/3.0f, // 右下
1.0f, 1.0f, 1.0f, 0.50f, 2.0f/3.0f, // 右上
1.0f, 1.0f, 1.0f, 0.50f, 2.0f/3.0f, // 右上
-1.0f, 1.0f, 1.0f, 0.25f, 2.0f/3.0f, // 左上
-1.0f, -1.0f, 1.0f, 0.25f, 1.0f/3.0f, // 左下
// -X (Left Face)
-1.0f, 1.0f, 1.0f, 0.25f, 2.0f/3.0f, // 右上
-1.0f, 1.0f, -1.0f, 0.00f, 2.0f/3.0f, // 左上
-1.0f, -1.0f, -1.0f, 0.00f, 1.0f/3.0f, // 左下
-1.0f, -1.0f, -1.0f, 0.00f, 1.0f/3.0f, // 左下
-1.0f, -1.0f, 1.0f, 0.25f, 1.0f/3.0f, // 右下
-1.0f, 1.0f, 1.0f, 0.25f, 2.0f/3.0f, // 右上
// +X (Right Face)
1.0f, 1.0f, -1.0f, 0.75f, 2.0f/3.0f, // 右上
1.0f, 1.0f, 1.0f, 0.50f, 2.0f/3.0f, // 左上
1.0f, -1.0f, 1.0f, 0.50f, 1.0f/3.0f, // 左下
1.0f, -1.0f, 1.0f, 0.50f, 1.0f/3.0f, // 左下
1.0f, -1.0f, -1.0f, 0.75f, 1.0f/3.0f, // 右下
1.0f, 1.0f, -1.0f, 0.75f, 2.0f/3.0f, // 右上
// -Z (Back Face)
1.0f, -1.0f, -1.0f, 0.75f, 1.0f/3.0f, // 左下
-1.0f, -1.0f, -1.0f, 1.00f, 1.0f/3.0f, // 右下
-1.0f, 1.0f, -1.0f, 1.00f, 2.0f/3.0f, // 右上
-1.0f, 1.0f, -1.0f, 1.00f, 2.0f/3.0f, // 右上
1.0f, 1.0f, -1.0f, 0.75f, 2.0f/3.0f, // 左上
1.0f, -1.0f, -1.0f, 0.75f, 1.0f/3.0f, // 左下
// +Y (Top Face)
-1.0f, 1.0f, 1.0f, 0.25f, 2.0f/3.0f, // 左下
1.0f, 1.0f, 1.0f, 0.50f, 2.0f/3.0f, // 右下
1.0f, 1.0f, -1.0f, 0.50f, 1.00f, // 右上
1.0f, 1.0f, -1.0f, 0.50f, 1.00f, // 右上
-1.0f, 1.0f, -1.0f, 0.25f, 1.00f, // 左上
-1.0f, 1.0f, 1.0f, 0.25f, 2.0f/3.0f, // 左下
// -Y (Bottom Face)
-1.0f, -1.0f, -1.0f, 0.25f, 0.00f, // 左上
1.0f, -1.0f, -1.0f, 0.50f, 0.00f, // 右上
1.0f, -1.0f, 1.0f, 0.50f, 1.0f/3.0f, // 右下
1.0f, -1.0f, 1.0f, 0.50f, 1.0f/3.0f, // 右下
-1.0f, -1.0f, 1.0f, 0.25f, 1.0f/3.0f, // 左下
-1.0f, -1.0f, -1.0f, 0.25f, 0.00f // 左上
};
我们使用这些数据进行立方体的绘制,这里不做赘述。
- 在准备完数据之后,我们需要进行shader的准备。其实也是正常绘制,一个很简单的shader
// simple_skybox.vert
#version 440
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec2 aTexCoord;
out vec2 tc;
uniform mat4 projection;
uniform mat4 view;
void main()
{
tc = aTexCoord;
vec4 pos = projection * view * vec4(aPosition, 1.0);
gl_Position = pos.xyww; // 深度值设为最大值(w/w = 1.0)
}
// simple_skybox.frag
#version 440
out vec4 fragColor;
in vec2 tc;
layout (binding = 0) uniform sampler2D samp;
void main()
{
fragColor = texture(samp, tc);
}
- 前序工作都准备好之后,就是进行渲染了,渲染顺序都是:渲染其他物体 -> 禁止深度写入 -> 渲染天空盒 -> 重置深度状态。
// 1. 首先生效的物体
renderScene(window, mainShader, currentTime, true);
// 2. 天空盒配置(在最后渲染)
glDepthMask(GL_FALSE); // 禁用深度写入
glDepthFunc(GL_LEQUAL); // 启用小于等于深度测试
skybox.use();
skybox.setMat4("projection", pMat);
// 消位移,只保留旋转(防止近裁剪面穿帮)
glm::mat4 view = glm::mat4(glm::mat3(vMat));
skybox.setMat4("view", view);
// 3. 绘制天空盒
mySkybox.draw(ResourceManager::getTexture("skybox"));
// 4. 重置深度状态
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);

但是这种方法渲染容易出现一个问题,那就是接缝之间可能会出现伪影,这是因为我们在计算UV坐标的时候,存在了误差,所以会导致这种问题。比较好的解决方法就是使用cubemap进行天空盒的绘制。


被折叠的 条评论
为什么被折叠?



