第十八课,立方体贴图(加载天空盒)

原理我在这里不再过多叙述,主要从代码的运行方向来解读立方体贴图

添加天空盒顶点

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();

最终效果

注意,绘制的顺序是
先将各种物体绘制在帧缓存上
再绘制天空盒
然后绘制透明物体
最后将帧缓存绘制在屏幕缓存上。
在这里插入图片描述

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片