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

9
  • 2
    "to have different indices per draw calls" Why does that require having a different "EBO"? Commented Sep 27, 2022 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. Commented Sep 27, 2022 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. Commented Sep 27, 2022 at 15:42
  • 2
    @Rabbid76: This question is about how to use different index buffers in different draw calls, not the same draw call. Commented Sep 27, 2022 at 15:57
  • 1
    You still need to provide an minimal reproducible example, because wherever the problem is, it isn't in the code you've posted. Commented Sep 27, 2022 at 16:03

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.