是否可以将索引缓冲区对象 (IBO) 与函数“glMultiDrawElements”一起使用?

Posted

技术标签:

【中文标题】是否可以将索引缓冲区对象 (IBO) 与函数“glMultiDrawElements”一起使用?【英文标题】:Is it possible to use Index Buffer Objects (IBO) with the function 'glMultiDrawElements'? 【发布时间】:2014-07-01 18:27:00 【问题描述】:

我使用 OpenGL 和 GLSL 开发了一个小型 3D 引擎。

我已经合并了一个顶点数据批处理系统,其目标是在一个唯一的顶点缓冲区对象 (VBO) 中收集共享相同着色器程序和相同转换的所有几何图形(所有对象),从而最大限度地减少状态更改(绑定)并绘制电话。

我目前使用函数 'glMultiDrawElements' 来渲染特定批次的数据(因此是一次绘制调用)。因此,例如,如果我的批次中有 3 个网格,我也有 3 个索引数组(每个网格一个),它们组织在一个“GLvoid **”数组(一个双数组)中。所以要渲染我的 3 个网格,我有一个独特的 glMultiDrawElements 调用,但我必须直接将参数中的双精度数组传递给函数。

但我想知道(关于性能问题)是否可以将所有元素(元素的双数组)存储在索引缓冲区对象 (IBO) 中(就像可以使用 glDrawElements 一样 -> 这里是一个简单的数组元素)并在 glMultiDrawElements 调用之前绑定它......我不这么认为,因为它是一个双数组而不是一个简单的数组,但也许(我希望如此)我错了。

这是一个使用 glDrawElements 的示例(伪代码):

[...]

//Setup data

[...]

#define OFFSET_BUFFER(offset) ((char*)NULL + offset)

foreach (pMeshGeometry from meshes) //iterates for each mesh of the scene

    pMeshGeometry->GetIndexBuffer().Lock(); //Bind IBO
    
         glDrawElements(pMeshGeometry->GetPrimitiveType(),       
             pMeshGeometry->GetIndexBufferSize(), pMeshGeometry->GetIndexBuffer().GetType(), OFFSET_BUFFER(0)); //Render a specific mesh eccording to indices
    

目前我以这种方式使用 glMultiDrawElement:

glMultiDrawElements(GL_TRIANGLES, &this->m_CountElementArray[0], GL_UNSIGNED_INT,
                    (const GLvoid **)&this->m_IndexAttribArray[0], this->m_CountElementArray.size()); //I enter the array of pointer directly in parameter

所以,也许下面的例子应该是可能的:

#define OFFSET_BUFFER(offset) ((char**)NULL + offset) //Something like this

glMultiDrawElements(GL_TRIANGLES, &this->m_CountElementArray[0], GL_UNSIGNED_INT,
                    OFFSET_BUFFER(0), this->m_CountElementArray.size()); //Something like this

因此,如果无法做到这一点,我想到了函数“glDrawRangeElements”。对于我将 3 个网格放入唯一 VBO 的示例,我只需要在每个 glDrawRangeElements 调用之前绑定一个 IBO(这里,每个网格有 3 个绘制调用 -> 所以是一个 glDrawRangeElements 循环)。所以这里显然可以使用 IBO。

这种方法肯定行得通,但我认为它不是最好的方法!我认为可以使用带有 IBO 的 glMultiDrawElements 来做到这一点,但我不知道该怎么做。

或许这真的不可能。也许在参数中直接输入索引数组的事实比使用 glDrawRangeElements 及其 IBO 的方法更快,因此在这种情况下使用 IBO 可能已被弃用,因此不适用。

你怎么看?

【问题讨论】:

使用一些换行符。这个巨大的段落几乎无法阅读。 你到底在问什么?即使有换行符我也不确定。 对不起,我以为很清楚。总而言之,我想知道是否可以将 IBO 与 glMultiDrawElements 一起使用,而不是直接在参数中使用双索引数组。 @user1364743:没有“双数组”。有一组指针/偏移量。有ARB_multi_draw_indirect。但我不确定这是否是您要求的。 对不起,我想说的是指针数组而不是双数组。我已经使用“glDrawElements”方法更新了我的问题,添加了一个源代码(伪代码)示例。正如您在主循环之前看到的,我将所有顶点数据设置为 VBO,将所有索引数据设置为 IBO。之后,在主循环中我使用宏 OFFSET_BUFFER。这行得通。是否可以为函数“glMultiDrawElements”使用这样的宏(因此使用 IBO 而不是直接使用指针数组)? 【参考方案1】:

您当然可以将索引缓冲区与glMultiDrawElements() 一起使用。客户端索引数组在 OpenGL 核心配置文件中已弃用。所以如果glMultiDrawElements() 不能使用索引缓冲区,就没有办法再使用它了。

要了解它是如何工作的,我们需要看看glMultiDrawElements() 的参数是什么意思。该调用基本上只是多个glDrawElements() 调用的快捷方式。签名是:

void glMultiDrawElements(GLenum mode, const GLsizei* count, GLenum type,  
                         const GLvoid** indices, GLsizei primcount); 

除了一些错误检查细节之外,这个调用相当于:

for (int i = 0; i < primcount; ++i) 
    glDrawElements(mode, count[i], type, indices[i]);

现在请记住,如果绑定了索引缓冲区,glDrawElements() 的最后一个参数是缓冲区的相对偏移量。所以glMultiDrawElements() 对应的第 4 个元素是缓冲区中的一个偏移数组。第二个参数是一个匹配的计数数组。

人们经常使用宏来隐藏glDrawElements()最后一个参数的繁琐类型转换。使用这个:

#define BUFFER_OFFSET(offset) (static_cast<char*>(0) + (offset))

例如,假设我们有一个索引缓冲区绑定,并且我们想通过一次调用绘制索引数组的 3 个子范围:

20 个索引,从缓冲区中的索引 10 开始。 30 个索引,从缓冲区中的索引 40 开始。 缓冲区中从索引 90 开始的 10 个索引。

我将使用无符号短裤 (GLushort) 作为索引类型。因此,索引缓冲区将在过去的某个时间填充来自GLushort indexA[100] 的数据。设置和绘制调用如下所示:

GLsizei count[3] = 20, 30, 10;
GLvoid* indices[3] = 
    BUFFER_OFFSET(10 * sizeof(GLushort)),
    BUFFER_OFFSET(40 * sizeof(GLushort)),
    BUFFER_OFFSET(90 * sizeof(GLushort)),
;
glMultiDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_SHORT, indices, 3);

【讨论】:

非常感谢您的完整回答。我相信你的解决方案是最好的,而且非常合乎逻辑。我要把它集成到我的程序中。祝你有美好的一天。再见!

以上是关于是否可以将索引缓冲区对象 (IBO) 与函数“glMultiDrawElements”一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

VAO 是不是同时记住 EBO/IBO(元素或索引)和 VBO?

使用 IBO 制作动画 - 好还是坏?

OpenGL顶点数组对象不记录VBO和IBO绑定

在 iOS 上使用具有多个 VBO 和 IBO(多个对象)OpenGLES 2 的 VAO 进行绘制

OpenGL 基本 IBO/VBO 不工作

存储几何的索引