3

I am trying to render multiple models sharing the same VAO and vertex format (following top answer on this post Render one VAO containing two VBOs), however I cannot get it to work with GL_ELEMENT_ARRAY_BUFFER nor can I find any resource/example to help me. Is it even possible to do that or does the element array buffer work in a way that is incompatible with glVertexAttribFormat/glBindVertexBuffer and sharing VAOs? or am I missing the ELEMENT_ARRAY_BUFFER equivalent of glBindVertexBuffer?

My VAO is first created this way:

glCreateVertexArrays(1, &sharedVao);
glBindVertexArray(sharedVao);

glEnableVertexAttribArray(0);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(0, 0);
// (just 1 for the example but there is more)

glBindVertexArray(0);

Then my model buffers are created as follow:

glBindVertexArray(sharedVao); // tried with and without binding vao first, no success

glCreateBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertex), vertices.data(), GL_STATIC_DRAW);
// (just 1 for the example but there is more)

glCreateBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangles.size() * sizeof(triangle), triangles.data(), GL_STATIC_DRAW);

glBindVertexArray(0);

And finally I render as follow:

glBindVertexArray(sharedVao);

for (auto const& model : models)
{
    glBindVertexBuffer(0, model.vbo, sizeof(vertex));
    // (just 1 for the example but there is more)

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.ebo);
    // also tried glVertexArrayElementBuffer(sharedVao, model.ebo);

    glDrawElements(GL_TRIANGLES, model.triangleCount * 3, GL_UNSIGNED_INT, nullptr);
}

Note that it does work if I start rendering the same VAO with glDrawArray (so without element array buffer).

This C++ GLSL Multiple IBO in VAO may be of valuable use, but still not sure what it means for sharing VAO formats for multiple models... (also realized that calling it IBO gives me more results than EBO...).

EDIT: this question was originally closed as supposedly a duplicate of Rendering meshes with multiple indices but it is not. Unlike this other question I am not talking about having different indices for different data (ex: indices for positions, indices for normals, indices for texture coords, etc.) but to have different indices per draw calls, while still using the same VAO format (the same way it is done with VBO and glBindVertexBuffer in Render one VAO containing two VBOs).

Victor Drouin
  • 597
  • 2
  • 15
  • 2
    "*to have different indices per draw calls*" Why does that require having a different "EBO"? – Nicol Bolas Sep 27 '22 at 15:37
  • 2
    @VictorDrouin: "*changes VAO's format every draw which is quite costly*" Index buffers are not part of the "VAO's format". So if your question really is about changing index buffers, then you seem confused about what that means. – Nicol Bolas Sep 27 '22 at 15:38
  • 1
    @VictorDrouin: "*each would have their own vertices (and vertex buffers for different data)*" Are you sure that's something you want to do? If they share the same vertex format, maybe they should share the same buffer storage. – Nicol Bolas Sep 27 '22 at 15:42
  • It's not clear to me what "suggestion 1.2" refers to. – Nicol Bolas Sep 27 '22 at 15:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248384/discussion-between-victor-drouin-and-nicol-bolas). – Victor Drouin Sep 27 '22 at 15:46
  • 2
    @Rabbid76: This question is about how to use different index buffers in **different** draw calls, not the same draw call. – Nicol Bolas Sep 27 '22 at 15:57
  • 1
    You still need to provide an [mcve], because wherever the problem is, it isn't in the code you've posted. – Nicol Bolas Sep 27 '22 at 16:03
  • Hmm I believe you are correct; I tested with a much simpler example and it is working. So I guess the answer to my question is "yes it is possible to rebind EBO for each draw call with `glBindBuffer` similar to how we rebind VBOs with `glBindVertexBuffer`". Feel free to do so. Else I may answer myself later today with a clear an simple example because I feel the use of `glVertexAttribFormat` and `glBindVertexBuffer` is a bit obscure and lacking examples on the internet. – Victor Drouin Sep 27 '22 at 16:30
  • @NicolBolas I deleted a bunch of my reply to this thread as there was confusion due to someone deleting their reply to which I was referring to. It will help further readers I believe. – Victor Drouin Sep 27 '22 at 16:35

1 Answers1

0

Multiple draw calls with shared Vertex Array Object

The purpose of this method is to avoid the cost of changing VAO format (see glVertexAttribPointer and glVertexAttribFormat: What's the difference? or https://www.youtube.com/watch?v=-bCeNzgiJ8I&t=1860) by sharing VAO and only rebinding buffers for every draw.

A clear example that doesn't make use of Element Buffer Array can be seen here: Render one VAO containing two VBOs

Create shared VAO (could be only once per program):

GLuint sharedVao = 0;
glCreateVertexArray(1, &sharedVao);
glBindVertexArray(sharedVao);

glEnableVertexAttribArray(0);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
// binding each attribute from its own buffer so attrib_index == buffer_index
glVertexAttribBinding(0, 0);

glEnableVertexAttribArray(1);
glVertexAttribFormat(1, 2, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(1, 1);

Create mesh buffers (would be only once per mesh):

struct Mesh
{
    GLuint m_ebo = 0;
    std::array<GLuint, 2> m_vbos = 0;
    GLuint m_triangleCount = 0;
};

// Binding shared VAO here is mandatory as operations accessing or modifying EBO's
// state are not guaranteed to succeed if it wasn't bound to a VAO.
// However, for every new model, binding the EBO will unbind the previous, and we
// will need to rebind EBO to shared VAO for every draw call.
glBindVertexArray(sharedVao);
glCreateBuffer(1, &mesh.m_ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.m_ebo);
glBufferData(
    GL_ELEMENT_ARRAY_BUFFER,
    triangles.size() * sizeof(Triangle),
    triangles.data(),
    GL_STATIC_DRAW);
glBindVertexArray(0);
mesh.m_triangleCount  = triangles.size();

glCreateBuffers(2, mesh.m_vbos.data());
glBindBuffer(GL_ARRAY_BUFFER, mesh.m_vbos[0]);
glBufferData(
    GL_ARRAY_BUFFER,
    positions.size() * sizeof(glm::vec3),
    positions.data(),
    GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, mesh.m_vbos[1]);
glBufferData(
    GL_ARRAY_BUFFER,
    textureCoords.size() * sizeof(glm::vec2),
    textureCoords.data(),
    GL_STATIC_DRAW);

Render loop:

// Bind shared VAO only once
// If drawing with different set of vertex data bound, use glEnableVertexAttribArray
// or glDisableVertexAttribArray before draw calls
glBindVertexArray(sharedVao);

for (auto const& mesh : meshes)
{
    glBindVertexBuffer(0, mesh.m_vbos[0], 0, sizeof(glm::vec3));
    glBindVertexBuffer(1, mesh.m_vbos[1], 0, sizeof(glm::vec2));

    // This is the key difference with existing example on sharing VAO:
    // EBO must be rebound for every draw (unless 2 draws share the same primitive indices)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.m_ebo);

    glDrawElements(GL_TRIANGLES, mesh.m_triangleCount * 3, GL_UNSIGNED_INT, nullptr);
}

Victor Drouin
  • 597
  • 2
  • 15