9
votes

It's all ok when i want to draw one object, for example a cube. I create vertices for cube, i create the buffer, i create the MVP matrix and send it to shader and it works nice.

But, what to do when i want to draw 2 or more objects, for example both cube and a triangle? I believe that View and Projection matrices should be same both for triangle and cube, i only need different Model matrix, right? So that means that i will have two MVPs?

//Example (using GLM):

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

So what do i do with those two now? This is the vertex shader that works good for cube

//vertex shader
#version 330 core

layout(location = 0) in vec3 verticesCube;

uniform mat4 MVPC;

void main(){

     gl_Position =  MVPC * vec4(verticesCube,1);

}

And what should i do with MVPT (triangle) in shader, i tried messing around with different things, but i can't get it to work, i can't display both the cube and the triangle at the same time.

4

4 Answers

11
votes

The confusion comes from thinking that the shader controls multiple vertex arrays at once, when it should be thought of as a universal entity. A vertex array is passed to the shader, then the object is drawn. And the process is repeated.

For example, let's say we assign the variable matrixID to the uniform MVP:

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

When we're ready to draw an object, we set matrixID to the object's MVP:

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

Then bind the vertex buffer, set the attribute pointer, and draw it:

    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

Now we move on to the triangle and repeat the process - set matrixID to the object's MVP, bind the vertex buffer, set the attribute pointer, and draw it:

    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

The corresponding vertex shader code:

#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);
}
4
votes

OpenGL is not a scene graph. It draws things according to the current state and then forgets about it.

So if you want to draw different geometries, with different transformations just set the corresponding transformation matrix (uniform), draw the object and repeat this for each object you want to draw. After geometry has been drawn, the following operations will have no further effect on it, other than it might be overdrawn.

2
votes

An alternative that may also work, would be to do the 'ModelViewProjection' matrix calculation in the vertex shader. You can do this by making uniform model, view, and projection matrix variables in the vertex shader. You can then globally calcluate view and projection matrices, and send them to the shader. You can then just calculate the model matrix for your cube and triangle (or whatever objects you need to render) individually, and send those model matrices to the shader as well.


View and projection matrix calculations; this could be in a separate 'camera' class:

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

Model matrix calculation; this code can go in a separate class, and you can instantiate it for each object that you want to render:

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

New vertex shader:

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

}
0
votes

Have two arrays of vertices. Lets say array1 for cube, array2 for circle. Create 2 vaos and 2 vbos. vao1 and vbo1 for cube. vao2 and vbo2 for circle.

Bind vao1, bind vbo1, fill up vbo1 buffer with array1. glUserProgram(program) which is a program for shaders, set up vertexattripointer.

call glDrawArray()

Do the same thing for other vao and vbo.