是否可以使用索引缓冲区对象(IBO)和函数' glMultiDrawElements'?

时间:2014-07-01 18:27:01

标签: opengl glsl

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

我已经合并了一个顶点数据批处理系统,其目的是收集共享相同着色器程序的所有几何体(所有对象)和相同的顶点缓冲对象(VBO)中的相同转换,从而最小化状态更改(绑定)并打电话。

我目前使用函数'glMultiDrawElements'来呈现特定批量的数据(因此单次绘制调用)。因此,例如,如果我在我的批处理3网格中,我有3个索引数组(每个网格一个)组织在'GLvoid **'数组(双数组)中。因此,为了渲染我的3个网格,我有一个独特的glMultiDrawElements调用,但是我必须直接将参数中的double元素数组传递给函数。

但我想(对于性能问题)是否可以将所有元素(双元素数组)存储在索引缓冲区对象(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可以做到这一点,但我不知道该怎么做。

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

你怎么看?

1 个答案:

答案 0 :(得分:7)

您当然可以使用glMultiDrawElements()的索引缓冲区。 OpenGL核心配置文件中不推荐使用客户端索引数组。因此,如果glMultiDrawElements()无法使用索引缓冲区运行,则无法再使用它了。

要了解它是如何工作的,我们需要看看glMultiDrawElements()的论点是什么意思。该呼叫基本上只是多个glDrawElemens()呼叫的快捷方式。签名是:

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开始。
  • 缓冲区中从索引40开始的30个索引。
  • 从缓冲区中的索引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);