2
$\begingroup$

In newer OpenGL it's encouraged to use shaders and pass the model-view-projection as parameters. But what if I drew a complex object, and wanted to translate/rotate it and draw many copies?

For example I wanted to do something like this :

glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
{
    shader.bind();

    glm::mat4 projectionMatrix = glm::perspective(20.0f, (float)SCRWIDTH/(float)SCRHEIGHT, 0.0f, 100.0f);
    glm::mat4 viewMatrix = glm::lookAt(
        glm::vec3(0, 0, 2), 
        glm::vec3(0, 0, 0), 
        glm::vec3(0, 1, 0)
    );
    glm::mat4 modelMatrix = glm::toMat4(quaternion);
    glm::mat4 MVP = projectionMatrix * viewMatrix * modelMatrix;
    GLint loc_MVP = glGetUniformLocation(shader.id(), "MVP");    
    glUniformMatrix4fv(loc_MVP, 1, GL_FALSE, &MVP[0][0]);

    glTranslatef(1,0,0);
    gluSphere(...);
    glTranslatef(2,0,0);
    gluSphere(...);

    shader.unbind();
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

But my experiment shows that the glTranslatef doesn't work here, all the spheres are rendered at (0,0,0). I don't want to call the shader 100 times, each time calculating a different MVP matrix. Is there any easy way around this?

The vertex shader looks like this :

#version 330 core

uniform mat4 MVP;
in vec3 in_Position;

void main(void) {
    gl_Position = MVP * vec4(in_Position, 1.0);
}
$\endgroup$
1
$\begingroup$

Update the uniform and draw again.

auto tMVP = glm::translate(MVP, glm::vec3(1,0,0));
glUniformMatrix4fv(loc_MVP, 1, GL_FALSE, &tMVP[0][0]);
gluSphere(...);

tMVP = glm::translate(MVP, glm::vec3(2,0,0));
glUniformMatrix4fv(loc_MVP, 1, GL_FALSE, &MVP[0][0]);
gluSphere(...);

Using glTranslate would have done the exact same thing behind the scenes.


You can instead use instancing instead where you upload all the transforms into a buffer and then tell the gpu to use that.

However the more portable way involves allocating a vertex attribute for the data. This involves using glVertexAttribDivisor.

So a full mat4 would need 4 vertex attributes. You can decrease that to 2 by separating out the translation as a vec3 and the rotation as a quaternion and pass the view-perspective as a uniform and combining it all in the vertex shader.

$\endgroup$
  • $\begingroup$ Cool!, I didn't know that the variables could be different for each vertices in the same pass. This worked for me. $\endgroup$ – seilgu Aug 13 at 10:32
  • $\begingroup$ Can you answer one more question? Does this work will depth culling? I drew 100 spheres near and far, but the far ones can sometimes block the near ones. $\endgroup$ – seilgu Aug 13 at 14:57
  • $\begingroup$ you need a depth buffer to do proper per fragment depth testing. $\endgroup$ – ratchet freak Aug 13 at 15:46
  • $\begingroup$ Found the culprit. It's because my perspective zNear was set to 0. $\endgroup$ – seilgu Aug 13 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.