Read "Computer Graphics Programming (Using OpenGL and C++)" 7 - Rendering different objects in the same scene

A simple way to render different objects in the same scene is to use a separate buffer for each model. Each model requires its own model matrix, so we need to generate a new model-view matrix for each model we render. You also need to call glDrawArrays() individually for each model. Therefore, we need to modify the init() and display() functions.

Let's go ahead and add a simple pyramid so that our scene consists of a cube and a pyramid.

The vertex and fragment shader code has been omitted, they are the same as the code shown at the end of Section 4.

main.cpp

void setupVertices(void)
{    
    // 立方体
    float cubePositions[108] = {
        -1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,  1.0f, -1.0f,  1.0f,  1.0f,  1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,  1.0f,  1.0f,  1.0f,  1.0f,  1.0f, -1.0f,
         1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f, -1.0f,  1.0f,  1.0f,  1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f, -1.0f,  1.0f, -1.0f, -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f, -1.0f,
    };
    // 金字塔有18个顶点,由6个三角形组成(侧面4个,底面2个)
    float pyramidOsitions[54] = {
        -1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  1.0f,  0.0f,  1.0f,  0.0f, // 前面
         1.0f, -1.0f,  1.0f,  1.0f, -1.0f, -1.0f,  0.0f,  1.0f,  0.0f, // 右面
         1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  1.0f,  0.0f, // 后面
        -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  1.0f,  0.0f, // 左面
        -1.0f, -1.0f, -1.0f,  1.0f, -1.0f,  1.0f, -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, vao); // 创建一个vao,并返回它的整数型ID存进数组vao中
    glBindVertexArray(vao[0]); // 激活vao
    glGenBuffers(numVBOs, vbo);// 创建两个vbo,并返回它们的整数型ID存进数组vbo中

    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); // 激活vbo第0个缓冲区
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubePositions), cubePositions, GL_STATIC_DRAW); // 将包含顶点数据的数组复制进活跃缓冲区(这里是第0个VBO)

    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); // 激活vbo第0个缓冲区
    glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidOsitions), pyramidOsitions, GL_STATIC_DRAW); // 将包含顶点数据的数组复制进活跃缓冲区(这里是第1个VBO)
}

void init(GLFWwindow* window) {
    renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
    cameraX = 0.0f;  cameraY = 0.0f;    cameraZ = 8.0f;
    cubeLocX = 0.0f; cubeLocY = -2.0f; cubeLocZ = 0.0f; // 沿Y轴下移以展示透视
    pyrLocX = 2.0f; pyrLocY = 2.0f; pyrLocZ = 0.0f;
    setupVertices();
}


void display(GLFWwindow* window, double currentTime)
{
    glClear(GL_DEPTH_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(renderingProgram);

    // 获取MV矩阵和投影矩阵的统一变量的引用
    mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
    projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
    
    // 构建透视矩阵
    glfwGetFramebufferSize(window, &width, &height);
    aspect = (float)width / (float)height;
    pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees
    
    // 构建视图矩阵、模型矩阵
    vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));

    // 绘制立方体(使用0号缓冲区)
    mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cubeLocX, cubeLocY, cubeLocZ));
    mvMat = vMat * mMat;

    glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat)); // 着色器需要视图矩阵的统一变量
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));

    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);                    // 标记第0个缓冲区为“活跃”
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);    // 将第0个属性关联到缓冲区
    glEnableVertexAttribArray(0);                            // 启用第0个顶点属性

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glDrawArrays(GL_TRIANGLES, 0, 36);                        // 执行该语句,第0个VBO中的数据将被传输给拥有位置0的layout修饰符的顶点属性中。这会将立方体的顶点数据发送到着色器。

    // 绘制金字塔(使用1号缓冲区)
    mMat = glm::translate(glm::mat4(1.0f), glm::vec3(pyrLocX, pyrLocY, pyrLocZ));
    mvMat = vMat * mMat;

    glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat)); // 着色器需要视图矩阵的统一变量
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));

    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);                    // 标记第1个缓冲区为“活跃”
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);    // 将第0个属性关联到缓冲区
    glEnableVertexAttribArray(0);                            // 启用第0个顶点属性

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glDrawArrays(GL_TRIANGLES, 0, 18);                        // 执行该语句,第1个VBO中的数据将被传输给拥有位置0的layout修饰符的顶点属性中。这会将金字塔的顶点数据发送到着色器。
    
}

The result is as follows:

matrix stack

A matrix stack is a bunch of transformation matrices. It allows transformations to be built on top of (or removed from) other transformations.

You can use the stack class of the C++ Standard Template Library (STL).

The "*=" operator is overloaded in mat4 so it can be used to concatenate matrices.

Instead of building the transformation by creating an instance of mat4, we use the push() command to create a new matrix on top of the stack. The desired transformation is then applied as needed to the newly created matrix at the top of the stack.

Animation effects that simulate the movement of the solar system.

Put the view matrix into the stack, the matrix directly above the view matrix will be the MV matrix of the sun. The matrix above it will be the Earth's MV matrix, consisting of a copy of the Sun's MV matrix and the Earth model matrix transformation applied on top of it. That is, the Earth's MV matrix is ​​established by combining the transformations of the planets into the transformations of the Sun. Likewise, the Moon's MV matrix is ​​on top of the planet's MV matrix and is constructed by applying the Moon's model matrix transformation to the MV matrix of the planet immediately below it.

After rendering the moon, you can render the second "moon" by "popping" the first moon's matrix from the stack (reverting the top of the stack to the planet's model-view matrix) and then repeating the process for the second moon. ".

The basic method is as follows.

(1) We declare our stack and name it "mvStack".

(2) When creating a new object relative to the parent object , call "mvStack.push(mvStack.top())".

(3) Apply the required transformation to the new object, that is, multiply the required transformation by it.

(4) After completing the drawing of the object or sub-object, call "mvStack.pop()" to remove its model-view matrix from the top of the matrix stack.

main.cpp

void display(GLFWwindow* window, double currentTime)
{
    glClear(GL_DEPTH_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(renderingProgram);

    // 获取MV矩阵和投影矩阵的统一变量的引用
    mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
    projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
    
    // 构建透视矩阵
    glfwGetFramebufferSize(window, &width, &height);
    aspect = (float)width / (float)height;
    pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));

    // 将视图矩阵推入堆栈
    vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
    mvStack.push(vMat);

    // 金字塔 == 太阳
    mvStack.push(mvStack.top());
    mvStack.top() *= glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); // 太阳位置
    mvStack.push(mvStack.top());
    mvStack.top() *= glm::rotate(glm::mat4(1.0f), (float)currentTime, glm::vec3(1.0f, 0.0f, 0.0f));

    // 太阳旋转
    glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvStack.top())); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LEQUAL);
    glDrawArrays(GL_TRIANGLES, 0, 18);    // 绘制太阳
    mvStack.pop();  // 从堆栈中移除太阳的轴旋转

    // 立方体 == 行星
    mvStack.push(mvStack.top());
    mvStack.top() *= glm::translate(glm::mat4(1.0f), glm::vec3(sin((float)currentTime)*4.0, 0.0f, cos((float)currentTime)*4.0));
    mvStack.push(mvStack.top());
    mvStack.top() *= glm::rotate(glm::mat4(1.0f), (float)currentTime, glm::vec3(0.0, 1.0, 0.0));

    // 行星旋转
    glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvStack.top()));
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
    glDrawArrays(GL_TRIANGLES, 0, 36); // 绘制行星
    mvStack.pop();  // 从堆栈中移除行星的轴旋转

    // 小立方体 == 月球
    mvStack.push(mvStack.top());
    mvStack.top() *= glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, sin((float)currentTime)*2.0, cos((float)currentTime)*2.0));
    mvStack.top() *= glm::rotate(glm::mat4(1.0f), (float)currentTime, glm::vec3(0.0f, 0.0f, 1.0f));

    // 月球旋转
    mvStack.top() *= glm::scale(glm::mat4(1.0f), glm::vec3(0.25f, 0.25, 0.25)); // 让月球小一些
    glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvStack.top()));
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
    glDrawArrays(GL_TRIANGLES, 0, 36); // 绘制行星

    // 从堆栈中移除月球缩放、旋转、位置矩阵,行星位置矩阵,太阳位置矩阵,和视图矩阵
    mvStack.pop();
    mvStack.pop();
    mvStack.pop();
    mvStack.pop();    
}

Shader code unchanged

The effect is as follows:

Guess you like

Origin blog.csdn.net/ttod/article/details/135346231