Lesson 18, Cube Mapping (Loading Sky Box)

Posted by uglyrat on Tue, 19 Oct 2021 18:13:02 +0200

I'm not going to go into that much here, but I'm going to look at the cube map mainly from the direction in which the code is going to run.

Add Sky Box Vertices

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
};

We can add sky box vertices to separate files that store data to keep the main code simple.
Use extern float skyboxVertices[3*6*6]; To introduce the main function.
Note that you must specify the length of the reference array here, or you will get an error at sizeof(skyboxVertices).

Configure vertex properties

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

Loading Sky Box

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

The loadCubemap function loads the texture of the sky box and returns the textureID.
The function declares unsigned int loadCubemap (vector < std:: string > faces);
Function Definition

// 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);//Set this texture as a cube map

    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);
            //Loop six textures into GL_ TEXTURE_ CUBE_ MAP_ POSITIVE_ Location of 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);//Set the zoom-out method
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//Set magnification change method
    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) are
GL_TEXTURE_CUBE_MAP_POSITIVE_X, Right
GL_TEXTURE_CUBE_MAP_NEGATIVE_X, left
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, Up
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, bottom
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, front
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, after

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); The specific internal how to do is not clear, to add!!!
I would like the comment area to help me add (love U@.)

Load 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;
}  

From the perspective projection transformation matrix, the fourth component of the vertex w = 1-z/d (z is the coordinate z-axis distance, D is the distance from the eye to the canvas).
The fourth component transforms the prism space into a cube space, and the depth value z divides by w into a pseudo depth value.
In order for the final depth value z/w to always be 1, we set the Z value to w, which will not affect the perspective changes of x and y, but will also correctly keep the Z axis one.
So there's gl_Position = pos.xyww; Usage.

6.1.skybox.fs

#version 330 core
out vec4 FragColor;

in vec3 TexCoords;

uniform samplerCube skybox;

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

samplerCube passes in a cube map
texture(skybox, TexCoords); The first parameter passes in the cube map texture, and the second parameter passes in a direction.
We know that the incoming GPU data is the vertex of the square in all directions of the cube, and how OpenGL calculates the direction vector of each pixel point in the sky box is not yet known.
For the moment, all we need to know is that the TexCoords given to you by OpenGL are the direction vectors corresponding to that pixel point.

Draw

	//Sky Box
        glDepthFunc(GL_LEQUAL);  // Change the depth pass so that objects with a depth of 1 can also be rendered
        
        skyboxShader.use();
        view = glm::mat4(glm::mat3( maincamera.getViewMetrix() )); //Remove Viewport Move Change
        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); // Set depth function as default
        view = maincamera.getViewMetrix();

Final results

Note that the order of drawing is
Draw objects on the frame cache first
Redraw Sky Box
Then draw transparent objects
Finally, the frame cache is drawn on the screen cache.

Topics: C++ OpenGL