OpenGL 使用单个 VBO 渲染多个对象并使用另一个 VBO 更新数据对象的矩阵

0 c++ opengl matrix

因此,我需要一种使用一次绘制调用来渲染多个对象(而不是实例)的方法。实际上我知道如何做到这一点,只是使用 glDrawElements 将数据放入单个 vbo/ibo 并渲染。

问题是:使用 glUniform... 来更新统一数据而不为每个对象设置它的有效方法是什么?

如何设置一个缓冲区,其中包含数十个对象的所有统一数据(包括 MVP 矩阵)、绑定它并使用单个绘制调用执行渲染?我尝试使用 UBO,但这根本不是我需要的。

对于渲染实例,我们只需将统一数据(包括矩阵)放置在另一个 VBO 中,并使用 glVertexAttribDivisor 设置属性除数,但它仅适用于实例。

有没有办法在 OpenGL 中实现我想要的功能?如果不是,我该如何克服为数十个对象设置统一数据的开销?

例如这样:

{
    // 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(...);
}
Run Code Online (Sandbox Code Playgroud)

预先感谢您的帮助。

Ret*_*adi 5

如果您同意要求 OpenGL 4.3 或更高版本,我相信您可以使用glMultiDrawElementsIndirect(). 这实际上允许您使用单个 API 调用进行多个绘制调用。每个子调用由以下形式的结构中的值定义:

typedef  struct {
    GLuint  count;
    GLuint  instanceCount;
    GLuint  firstIndex;
    GLuint  baseVertex;
    GLuint  baseInstance;
} DrawElementsIndirectCommand;
Run Code Online (Sandbox Code Playgroud)

由于您不想绘制相同顶点的多个实例,因此instanceCount在每次绘制调用中使用 1。关键思想是您仍然可以通过为baseInstance每个实例指定不同的值来使用实例。因此,每个对象都会有不同的gl_InstanceID值,您可以使用实例化属性来表示每个对象想要改变的值(矩阵等)。

因此,如果您当前有一个渲染循环:

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));
}
Run Code Online (Sandbox Code Playgroud)

您可以使用参数填充上面定义的结构体的数组:

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);
Run Code Online (Sandbox Code Playgroud)

我没有提供上述完整设置的代码。关键步骤包括:

  • cmds数组放入缓冲区,并将其绑定为GL_DRAW_INDIRECT_BUFFER.
  • 将每个对象的值存储在 VBO 中。设置相应的顶点属性,其中包括将它们指定为 的实例化glVertexAttribDivisor(1)
  • 像往常一样设置每个顶点的属性。
  • 像往常一样设置索引缓冲区。

为此,所有对象的索引必须位于同一索引缓冲区中,并且每个属性的值必须位于所有对象的同一 VBO 中。