原理我在这里不再过多叙述,主要从代码的运行方向来解读立方体贴图
添加天空盒顶点
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
};
我们可将天空盒顶点添加到单独存储数据的文件,来保证主代码的简洁性。
使用 extern float skyboxVertices[3*6*6];
来引入主函数。
注意,这里必须要指定引用数组的长度,否则在sizeof(skyboxVertices)
处会报错。
配置顶点属性
unsigned int skyboxVAO, skyboxVBO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
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*)0);
加载天空盒
vector<std::string> faces
{
("./Picture_source/CubePicture/skybox/right.jpg"),
("./Picture_source/CubePicture/skybox/left.jpg"),
("./Picture_source/CubePicture/skybox/top.jpg"),
("./Picture_source/CubePicture/skybox/bottom.jpg"),
("./Picture_source/CubePicture/skybox/front.jpg"),
("./Picture_source/CubePicture/skybox/back.jpg")
};
unsigned int cubemapTexture = loadCubemap(faces);
loadCubemap函数加载天空盒的纹理并返回textureID;
函数申明unsigned int loadCubemap(vector<std::string> faces);
函数定义
// loads a cubemap texture from 6 individual texture faces
// order:
// +X (right)
// -X (left)
// +Y (top)
// -Y (bottom)
// +Z (front)
// -Z (back)
// -------------------------------------------------------
unsigned int loadCubemap(vector<std::string> faces)
{
unsigned int textureID;
glGenTextures(1, &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(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);
//循环将六个纹理加载到GL_TEXTURE_CUBE_MAP_POSITIVE_X+i的位置
stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << 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);
return textureID;
}
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i , (i = 0~5) 分别是
GL_TEXTURE_CUBE_MAP_POSITIVE_X, 右
GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 左
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 上
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 下
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 前
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 后
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
具体内部怎么做的还不清楚,代补充!!!
希望评论区帮忙补充(love U@。@)
加载Shader(着色器)
Shader skyboxShader("6.1.skybox.vs", "6.1.skybox.fs");
6.1.skybox.vs
#version 450 core
layout (location = 0) in vec3 aPos;
out vec3 TexCoords;
uniform mat4 projection;
uniform mat4 view;
void main()
{
TexCoords = aPos;
vec4 pos = projection * view * vec4(aPos, 1.0);
gl_Position = pos.xyww;
}
由透视投影变换矩阵可知,顶点的第四个分量w = 1-z/d (z为坐标z轴距离,d为视距(眼睛到画布的距离)),
第四个分量会将棱台空间转变为长方体空间,深度值z也会相应的除以w变为伪深度值。
为了使最终的深度值z/w的值始终为1,我们将z值设为w,这样既不会影响x,y的透视变化,也能正确地使z轴始终为一。
因此有了gl_Position = pos.xyww;
的用法。
6.1.skybox.fs
#version 330 core
out vec4 FragColor;
in vec3 TexCoords;
uniform samplerCube skybox;
void main()
{
FragColor = texture(skybox, TexCoords);
}
samplerCube 传入一个立方体贴图
texture(skybox, TexCoords);第一个参数传入立方体贴图纹理,第二个参数传入一个方向。
我们知道传入GPU的数据是立方体各个方向正方形的顶点,至于OpenGL是如何计算每个像素点在天空盒中的方向向量的,暂时还不清楚。
我们暂时只需知道,OpenGL给你的TexCoords即为该像素点对应的方向向量。
绘制
//天空盒
glDepthFunc(GL_LEQUAL); // 改变深度通过方式,使得深度为1的物体也可以被渲染
skyboxShader.use();
view = glm::mat4(glm::mat3( maincamera.getViewMetrix() )); //移除视口移动变化
skyboxShader.setMat4("view", view);
skyboxShader.setMat4("projection", projection);
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // 将深度函数设为默认
view = maincamera.getViewMetrix();
最终效果
注意,绘制的顺序是
先将各种物体绘制在帧缓存上
再绘制天空盒
然后绘制透明物体
最后将帧缓存绘制在屏幕缓存上。
暂无评论内容