简体   繁体   中英

OpenGL render multiple objects using single VBO and updata object's matrices using another VBO

So, I need the way to render multiple objects(not instances) using one draw call. Actually I know how to do this, just to place data into single vbo/ibo and render, using glDrawElements.

The question is: what is efficient way to update uniform data without setting it up for every single object, using glUniform...?

How can I setup one buffer containing all uniform data of dozens of objects, include MVP matrices, bind it and perform render using single draw call? I tried to use UBOs, but it's not what I need at all.

For rendering instances we just place uniform data, including matrices, at another VBO and set up attribute divisor using glVertexAttribDivisor, but it only works for instances.

Is there a way to do that I want in OpenGL? If not, what can I do to overcome overheads of setting uniform data for dozens of objects?

For example like this:

{
    // setting up VBO
    glGenBuffers(1, &vbo);
    glBindBuffer(vbo);
    glBufferData(..., data_size);

    // setup buffer
    for(int i = 0; i < objects_num; i++)
        glBufferSubData(...offset, size, &(objects[i]));

    // the same for IBO
    .........
    // when setup some buffer, that will store all uniforms, for every object
    .........
    glDrawElements(...);
}

Thanks in advance for helping.

If you're ok with requiring OpenGL 4.3 or higher, I believe you can render this with a single draw call using glMultiDrawElementsIndirect() . This allows you to essentially make multiple draw calls with a single API call. Each sub-call is defined by values in a struct of the form:

typedef  struct {
    GLuint  count;
    GLuint  instanceCount;
    GLuint  firstIndex;
    GLuint  baseVertex;
    GLuint  baseInstance;
} DrawElementsIndirectCommand;

Since you do not want to draw multiple instances of the same vertices, you use 1 for the instanceCount in each draw call. The key idea is that you can still use instancing by specifying a different baseInstance value for each one. So each object will have a different gl_InstanceID value, and you can use instanced attributes for the values (matrices, etc) that you want to vary per object.

So if you currently have a rendering loop:

for (int k = 0; k < objectCount; ++k) {
    // set uniforms for object k.
    glDrawElements(GL_TRIANGLES, object[k].indexCount,
                   GL_UNSIGNED_INT, object[k].indexOffset * sizeof(GLuint));
}

you would instead fill an array of the struct defined above with the arguments:

DrawElementsIndirectCommand cmds[objectCount];
for (int k = 0; k < objectCount; ++k) {
    cmds[k].count = object[k].indexCount;
    cmds[k].instanceCount = 1;
    cmds[k].firstIndex = object[k].indexOffset;
    cmds[k].baseVertex = 0;
    cmds[k].baseInstance = k;
}

// Rest of setup.

glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, objectCount, 0);

I didn't provide code for the full setup above. The key steps include:

  • Drop the cmds array into a buffer, and bind it as GL_DRAW_INDIRECT_BUFFER .
  • Store the per-object values in a VBO. Set up the corresponding vertex attributes, which includes specifying them as instanced with glVertexAttribDivisor(1) .
  • Set up the per-vertex attributes as usual.
  • Set up the index buffer as usual.

For this to work, the indices for all the objects will have to be in the same index buffer, and the values for each attribute will have to be in the same VBO across all objects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM