opengl glDrawElementsBaseVertex的工作原理是什么?

fiei3ece  于 2022-11-23  发布在  其他
关注(0)|答案(4)|浏览(270)

我有一个平面和一个索引缓冲区,或者EBO,索引标记在图像上:

现在如果我叫:

glDrawElementsBaseVertex(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, 0);

我得到这个:

这我明白。进一步说,如果我这样做:

glDrawElementsBaseVertex(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0, 0);

这也是有道理的,但是当我改变其他参数时,我的理解就完全福尔斯了。如果我这样做:

glDrawElementsBaseVertex(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, 3);

它看起来像这样:

所以通过把参数3传递给basevertex参数(最后一个),它开始使用的索引不是从3个位置到索引,甚至不是从3个三角形到索引,而是从大约6个三角形开始,或者更准确地说,索引号是18,我不能理解这种行为。
此外,我还读到了这些函数中“索引”的论证的矛盾含义:

void glDrawElements(GLenum mode,  GLsizei count,  GLenum type,  const GLvoid * indices);
void glDrawElementsBaseVertex(GLenum mode​, GLsizei count​, GLenum type​, GLvoid *indices​, GLint basevertex​);

我读到过indices指针让你可以通过提供一个指针来直接引用索引缓冲区,如果它为null,那么索引缓冲区就从当前绑定的GL_ELEMENT_ARRAY_BUFFER中获取。
indices指定一个字节偏移量(强制转换为指针类型)到绑定到GL_ELEMENT_ARRAY_BUFFER的缓冲区中,以开始阅读索引。
而在另一个版本中它说:
indices指定指向存储索引的位置的指针。
如果我调用glDrawElementsBaseVertex,并将倒数第二个参数(indices)设置为(void*)3,则第一个三角形将被绘制为红色。如果我指定(void*)6,则没有三角形被突出显示。如果我指定(void*)9,则第二个三角形将被突出显示。
我无法理解这些,那么是不是这个参数indices不是一个可选的指针,指向你希望使用的索引,而不是使用当前绑定的元素数组缓冲区?

x3naxklr

x3naxklr1#

如果您希望索引具有偏移量,只需按如下方式使用glDrawElements:

glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)(sizeof(GLuint)*6));

它将绘制3个元素,索引偏移量为6。
glDrawElements的最后一个参数可以是两个不同的内容:
1.如果没有绑定到GL_ELEMENT_ARRAY_BUFFER的索引缓冲区,则它是指向索引存储位置的指针。
1.如果您确实有一个绑定到GL_ELEMENT_ARRAY_BUFFER的索引缓冲区,则它是该数组的偏移量**(以字节**为单位)。

a64a0gku

a64a0gku2#

你还应该向我们展示你是如何给顶点编号的。从截图中看,顶点是按行对齐的。在这种情况下,它正确地呈现了第三个单元格中的顶点,因为第三个顶点就在那里。
如果要渲染单元格2的前半部分,则应将基础顶点设置为1,因为顶点2位于该位置。
同样,如果要呈现单元格2的后半部分,则应将基指针设置为1,将索引指针设置为3。

fnx2tebb

fnx2tebb3#

回复有点晚了,但我刚刚遇到了调用这个GL函数的需要,我也在与它的行为作斗争。
我敢打赌,你在第一张图片中包含的数字并不是索引缓冲区中的实际索引值,而只是表明你指定顶点的顺序。假设你的顶点缓冲区是这样组织的:

其中我添加的红色数字是顶点缓冲区数组索引,实际的索引缓冲区内容应该以{0,1,6,1,7,6,...}开始。
因此,在 basevertex 等于零的情况下,调用glDrawElementsBaseVertex()将执行以下操作:
当i=0时,索引缓冲区中的元素0(值为0)将顶点缓冲区中的元素0(上图中的红色数字)作为第一个三角形的第一个顶点;当i=2时,索引缓冲区中的元素2(值为6)将顶点缓冲区中的元素6作为第一个三角形的第一个顶点。
引用glDrawElementsBaseVertex()的文档
“* 由相应绘制调用传输的第i个元素将从每个启用数组的元素索引[i] +基顶点中获取。*”
是的,这有点令人困惑,但这意味着在从索引缓冲区中检索索引值后,会将 basevertex 添加到索引缓冲区中。因此,如果 basevertex 为3,则会有以下行为:
当i=0时,从索引缓冲区中检索元素0。和以前一样,它的值为0,但现在添加了3。因此,顶点缓冲区中的元素3当i=2时,索引缓冲区中的元素2被检索出来,它的值为6,但要加上3,因此顶点缓冲区中的第九个元素用于完成第一个三角形。
希望这对你有帮助。

b5buobof

b5buobof4#

glDraw元素

我们有顶点位置。

float positions[] =
{
    -0.5f, -1.0f, 0.0f,
    -1.5f,  1.0f, 0.0f,
    -2.5f, -1.0f, 0.0f,
     2.5f, -1.0f, 0.0f,
     1.5f,  1.0f, 0.0f,
     0.5f, -1.0f, 0.0f
};

我们也有两个三角形网格的位置索引。

uint8 indices[] =
{
    0, 1, 2,
    3, 4, 5
};

要绘制这两个网格,我们使用glDrawElements,如下所示:

// --------------------------------------------------------
// The first triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseIndex  = 0;
} mesh_1;

glDrawElements(
    /* mode   = */ GL_TRIANGLES,
    /* count  = */ mesh_1.numIndices,
    /* type   = */ GL_UNSIGNED_BYTE,
    /* offset = */ (void*)( sizeof( uint8 ) * mesh_1.baseIndex ) );

// --------------------------------------------------------
// The second triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseIndex  = 3;
} mesh_2;

glDrawElements(
    /* mode   = */ GL_TRIANGLES,
    /* count  = */ mesh_2.numIndices,
    /* type   = */ GL_UNSIGNED_BYTE,
    /* offset = */ (void*)( sizeof( uint8 ) * mesh_2.baseIndex ) );

请注意,以下行必须具有相同的数据类型:

uint8 indices[] =
/* type       = */ GL_UNSIGNED_BYTE,
/* offset     = */ (void*)( sizeof( uint8 ) * mesh_1.baseIndex ) );

数据类型必须是下列类型之一:uint8uint16uint32中的一个或多个。
结果将是这个图像。
triangles_1

glDrawElements基础顶点

如果我们想以这种方式绘制多个不同的网格,可能每个网格都将从零开始存储索引。例如,使用assimp库加载一个场景,我们将得到以下索引集:

uint8 indices[] =
{
    0, 1, 2,
    0, 1, 2
};

让我们也绘制这些网格。

// --------------------------------------------------------
// The first triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseVertex = 0;
    int32 baseIndex  = 0;
} mesh_1;

glDrawElementsBaseVertex(
    /* mode       = */ GL_TRIANGLES,
    /* count      = */ mesh_1.numIndices,
    /* type       = */ GL_UNSIGNED_BYTE,
    /* offset     = */ (void*)( sizeof( uint8 ) * mesh_1.baseIndex ),
    /* basevertex = */ mesh_1.baseVertex );

// --------------------------------------------------------
// The second triangular mesh.

struct
{
    int32 numIndices = 3;
    int32 baseVertex = 3;
    int32 baseIndex  = 3;
} mesh_2;

glDrawElementsBaseVertex(
    /* mode       = */ GL_TRIANGLES,
    /* count      = */ mesh_2.numIndices,
    /* type       = */ GL_UNSIGNED_BYTE,
    /* offset     = */ (void*)( sizeof( uint8 ) * mesh_2.baseIndex ),
    /* basevertex = */ mesh_2.baseVertex );

结果将是相同的图像。
triangles_2

奖金

当我们加载了场景,我们需要以某种方式获得我们需要绘制的数据,下面的代码将帮助我们:

int32 numVertices = 0;
int32 numIndices  = 0;

for ( auto& mesh : scene.meshes )
{
    mesh.numIndices = mesh.numTriangles * 3;

    mesh.baseVertex = numVertices;
    mesh.baseIndex  = numIndices;

    numVertices += mesh.numVertices;
    numIndices  += mesh.numIndices;
}

例如,通过加载“Low Poly UFO Scene“场景,结果将是此图像。
scene

相关问题