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

Posted

技术标签:

【中文标题】OpenGL 使用单个 VBO 渲染多个对象,并使用另一个 VBO 更新对象的矩阵【英文标题】:OpenGL render multiple objects using single VBO and updata object's matrices using another VBO 【发布时间】:2015-03-17 23:33:17 【问题描述】:

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

问题是:使用 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(...);

提前感谢您的帮助。

【问题讨论】:

为什么 UBO 不是您需要的?听起来它可以解决你的问题。另一方面,您确定这是您程序的瓶颈吗?对我来说听起来有点像过早的优化...... 它没有解决问题,因为在哪里会有很多对象,不是 10 或 100,而是更多。而 UBO 用于设置着色器统一块,即需要为每个对象更新它。(即使将所有统一数据存储在单个 UBO 中)所以,Reto Koradi 给出了一个很好的答案,我认为它解释了所有我想要。 【参考方案1】:

如果您可以要求 OpenGL 4.3 或更高版本,我相信您可以使用 glMultiDrawElementsIndirect() 进行一次绘制调用来渲染它。这允许您通过一个 API 调用实质上进行多个绘制调用。每个子调用由以下形式的结构中的值定义:

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

由于您不想绘制相同顶点的多个实例,因此在每次绘制调用中使用 1 表示 instanceCount。关键思想是您仍然可以通过为每个实例指定不同的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));

您将改为使用参数填充上面定义的结构的数组:

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

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

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

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

【讨论】:

是的,是的,谢谢,这是我想要的,而且似乎对我来说很好用。很好的答案!

以上是关于OpenGL 使用单个 VBO 渲染多个对象,并使用另一个 VBO 更新对象的矩阵的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL - 会使用多个 VBO 减慢渲染速度吗?

OpenGL 多个 VBO 只渲染一个

opengl vbo 纹理

opengl vbo建议[关闭]

opengl交错vbo不渲染到屏幕

opengl渲染4k数据提高效率