【问题标题】:How do i display 2 or more objects in openGL (model - view - projection matrices and shaders)如何在 openGL 中显示 2 个或更多对象(模型 - 视图 - 投影矩阵和着色器)
【发布时间】:2026-01-27 10:05:01
【问题描述】:

当我想绘制一个对象时,一切都可以,例如一个立方体。我为立方体创建顶点,创建缓冲区,创建 MVP 矩阵并将其发送到着色器,效果很好。

但是,当我想绘制 2 个或更多对象时该怎么办,例如立方体和三角形?我相信三角形和立方体的视图和投影矩阵应该相同,我只需要不同的模型矩阵,对吗? 所以这意味着我将有两个 MVP?

//Example (using GLM):

glm::mat4 MVPC = Projection * View * ModelCube; 
glm::mat4 MVPT = Projection * View * ModelTriangle; 

那我现在怎么处理这两个?这是适用于立方体的顶点着色器

//vertex shader
#version 330 core

layout(location = 0) in vec3 verticesCube;

uniform mat4 MVPC;

void main(){

     gl_Position =  MVPC * vec4(verticesCube,1);

}

我应该如何在着色器中处理 MVPT(三角形),我尝试处理不同的东西,但我无法让它工作,我无法同时显示立方体和三角形。

【问题讨论】:

    标签: opengl glsl shader


    【解决方案1】:

    混淆来自于认为着色器一次控制多个顶点数组,而它应该被认为是一个通用实体。一个顶点数组被传递给着色器,然后对象被绘制。并且重复这个过程。

    例如,假设我们将变量 matrixID 分配给统一的 MVP

        // get handle for our "MVP" uniform
        GLuint matrixID = glGetUniformLocation(programID, "MVP");
    

    当我们准备好绘制一个对象时,我们将 matrixID 设置为对象的 MVP:

        glUniformMatrix4fv(matrixID, 1, GL_FALSE, &cubeMVP[0][0]);
    

    然后绑定顶点缓冲区,设置属性指针,绘制:

        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, cubeVerteciesBuffer);
    
        glVertexAttribPointer(
                0,      // shader layout location
                3,
                GL_FLOAT,
                GL_FALSE,
                0,
                (void *)0
        );
        glDrawArrays(GL_TRIANGLES, 0, 12*3);    // draw cube
    

    现在我们移动到三角形并重复这个过程 - 将 matrixID 设置为对象的 MVP,绑定顶点缓冲区,设置属性指针,然后绘制它:

        glUniformMatrix4fv(matrixID, 1, GL_FALSE, &triMVP[0][0]);
    
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, triangleVerteciesBuffer);
    
        glVertexAttribPointer(
                0,      // shader layout location
                3,
                GL_FLOAT,
                GL_FALSE,
                0,
                (void *)0
        );
        glDrawArrays(GL_TRIANGLES, 0, 3);   // draw triangle
    

    对应的顶点着色器代码:

    #version 330 core
    // Input vertex data, different for all executions of this shader.
    layout(location = 0) in vec3 vertecies_modelspace;
    
    uniform mat4 MVP;
    
    void main(){
        gl_Position = MVP * vec4(vertecies_modelspace, 1);
    }
    

    【讨论】:

      【解决方案2】:

      OpenGL 不是场景图。它根据当前状态绘制事物然后忘记它。

      因此,如果您想绘制不同的几何图形,使用不同的变换只需设置相应的变换矩阵(统一),绘制对象并为您要绘制的每个对象重复此操作。几何图形绘制完成后,以下操作对其没有进一步的影响,只是可能会被过度绘制。

      【讨论】:

      • @IanDess:伪代码for(o in objects){glUniformMatrix(object->transformation_matrix); object->draw();}
      • 嗯我不知道该怎么做
      【解决方案3】:

      另一种可行的方法是在顶点着色器中进行“ModelViewProjection”矩阵计算。您可以通过在顶点着色器中创建统一的 modelviewprojection 矩阵变量来做到这一点。然后,您可以全局计算 viewprojection 矩阵,并将它们发送到着色器。然后,您可以单独计算立方体和三角形(或您需要渲染的任何对象)的模型矩阵,并将这些模型矩阵也发送到着色器。


      视图和投影矩阵计算;这可能在一个单独的“相机”类中:

      glm::mat4 viewMatrix = glm::lookAt(
        glm::vec3(0, -5, 0), // camera location in world
        glm::vec3(0, 0, 0), // point camera is looking at
        glm::vec3(0, 1, 0) // orientation of camera, change 1 to -1 to flip camera upside down
      );
      
      glm::mat4 projectionMatrix = glm::perspective(35.0f, displayWidth / displayHeight, 0.1f, 100.0f);
      
      // send view and projection matrices to the shader
      glUseProgram(shaderProgram);
      GLint viewMatrixId = glGetUniformLocation(shaderProgram, "view");
      GLint projectionMatrixId = glGetUniformLocation(shaderProgram, "projection");
      
      glUniformMatrix4fv(viewMatrixId, 1, GL_FALSE, &viewMatrix[0][0]);
                  glUniformMatrix4fv(projectionMatrixId, 1, GL_FALSE, &projectionMatrix[0][0]);
      glUseProgram(0);
      

      模型矩阵计算;这段代码可以放在一个单独的类中,您可以为每个要渲染的对象实例化它:

      // this can go after where you initialize your cube or triangle vertex information
      glUseProgram(shaderProgram);
      modelMatrixId = glGetUniformLocation(shaderProgram, "model"); //modelMatrixId can be a global GLint
      glUniformMatrix4fv(modelMatrixId, 1, GL_FALSE, &modelMatrix[0][0]); //modelMatrix can be a global glm::mat4
      glUseProgram(0);
      
      //use this for every render frame
      glUseProgram(shaderProgram);
      glUniformMatrix4fv(modelMatrixId, 1, GL_FALSE, &modelMatrix[0][0]);
      
      // code to bind vertices and draw you objects goes here
      
      glUseProgram(0);
      

      新的顶点着色器:

      //vertex shader
      #version 330 core
      
      layout(location = 0) in vec3 vertices;
      
      uniform mat4 model, view, projection;
      
      void main(){
      
           gl_Position =  projection * view * model * vec4(vertices, 1.0);
      
      }
      

      【讨论】:

        【解决方案4】:

        有两个顶点数组。让我们说立方体的array1,圆形的array2。 创建 2 个 vaos 和 2 个 vbos。用于立方体的 vao1 和 vbo1。圆的 vao2 和 vbo2。

        绑定 vao1,绑定 vbo1,用 array1 填充 vbo1 缓冲区。 glUserProgram(program) 是一个着色器程序,设置 vertexattripointer。

        调用 glDrawArray()

        对其他 vao 和 vbo 做同样的事情。

        【讨论】: