Binding Multiple VBOs and EBOs to VAO?

Hi thanks for clicking into my post first. I am recently learning OpenGL from scratch (Just finish the multiple boxes tutorial in learnopengl.com).

The tutorial is easy to understand. But when it comes to real scenario, things are becoming different. It only shows how one VBO binding to one VAO. In theory, VAO is just the way organizing similar VBOs together (same attributes and shaders). Now suppose I have 10k houses with different locations and shape, but they share the same shaders and attributes. Obviously, I could allocate 10k VAOs and 10k VBOs and 10k 10k EBOs to make 10k drawCalls to accomplish this. However, everyone would punch on my face in the forum if I did that I believe. Instead, I want to put these 10k VBOs & EBOs inside one VAO. However, I don’t know what is going wrong but it simply doesn’t render anything. (or I set camera in the wrong position?)

TL;DR
More details: I read data from disk dynamically and assigning them to "std::vector<std::vector<Vertex>> houses, there Vertex is just a simple class that represents the vertex (sizeof(Vertex) = 32, including 8 floats of data to represent position, color & textureCoord, honestly I wonder thy it’s only 32 bytes, coz the data itself has 4*8 bytes already, and a class should have some overhead. but it is continuous block of memory so I think it is fine). Now the code to bind my data is like this:


std::vector<std::vector<Vertex>> houses
std::vector<std::vector<unsigned int>> housesIndices
GLuint *VBOs;
GLuint *EBOs;
GLuint VAO;
GLShader shader;
unsigned int totalIndices = 0;

glGenVertexArrays(1, &VAO);
shader = GLShader("myshader.vs", "myshader.fs"); // I used the Shader class at learnopengl.com

VBOs = new GLuint[houses.size()];
EBOs = new GLuint[houses.size()];
glBindVertexArray(VAO);
for(size_t i = 0; i < houses.size(); ++i){
  glGenBuffers(1, &VBOs[i]);
  glBindBuffer(GL_ARRAY_BUFFER, VBOs[i]);
  glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*houses[i].size(), &houses[i][0], GL_STATIC_DRAW);

  glGenBuffers(1, &EBOs[i]);
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[i]);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*housesIndices[i].size(), &housesIndices[i][0], GL_STATIC_DRAW);

  totalIndices += housesIndices[i].size();
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
  glEnableVertexAttribArray(0);

  // this is to aoivd if I accidentally set my vertex in clockwise order
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  glBindVertexArray(0);
}

// in rendering loop:
shader.use();
glm::mat4 model = glm::mat4(1.0f);
shader.setMat4("model", model);

// xmin is the min value of vertices in these houses, same for xmax, ymin, ymax
// I just wanna have a top down view of these houses, the z value is guaranteed to be small, around 0
// camera_dir is (0, 0, -1.0f) camera_up_ is(0, 1.0f, 0)
camera_pos_ = glm::vec3((xmin+xmax)/2, (ymin+ymax)/2, 50.0f);
view = glm::lookAt(camera_pos_, camera_pos_ + camera_dir_, camera_up_);
shader.setMax4("view", view);

// projection
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
shader.setMat4("projection", projection);

glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, totalIndices, GL_UNSIGNED_INT, 0);

The shader I use is just having “aPos” as one attribute and three uniform mat4 representing MVP matix. In fragshader itonly outputs red color

Is it the correct way to bind VBOs, EBOs & VAO? Please correct me if I did something wrong in my code.

Note: Qt5.6, (I set OpenGL version to 3.3 in Qt format), Ubuntu 16.04, C++14

A VAO holds all of the state associated with each attribute array (enabled, buffer, offset, stride, format, divisor), and also the element (index) buffer.

If you have multiple “objects” which all have the same shaders and thus use the same attributes, the sensible approach is to store all of the attributes and indices in a single set of buffers (one element buffer and at most one buffer per attribute), so you can draw all of the objects with a single glDrawElements() call, or arbitrary subsets using multiple glDrawElements() calls each using a subrange of the element array or a single glMultiDrawElements() call. All of that state can go in a single VAO.

If each object has its own set of buffers, then it also needs its own VAO, and will need a separate draw call.

[QUOTE=GClements;1292006]A VAO holds all of the state associated with each attribute array (enabled, buffer, offset, stride, format, divisor), and also the element (index) buffer.

If you have multiple “objects” which all have the same shaders and thus use the same attributes, the sensible approach is to store all of the attributes and indices in a single set of buffers (one element buffer and at most one buffer per attribute), so you can draw all of the objects with a single glDrawElements() call, or arbitrary subsets using multiple glDrawElements() calls each using a subrange of the element array or a single glMultiDrawElements() call. All of that state can go in a single VAO.

If each object has its own set of buffers, then it also needs its own VAO, and will need a separate draw call.[/QUOTE]
Thanks, I may try to make my std::vector<std::vector<Vertex>> to a continuous block or array. But I still have another question that in my case, why it cannot render anything? In other word, when I bind a new VBO via glBindBuffer() and bind data via glBufferData(), would it override previous VBO in GL_ARRAY_BUFFER? Or it simply attaches behind previous bound data.

Each glVertexAttribPointer() call is overwriting the state from the previous call, so you end up with the VAO containing the state from the last such call. Similarly for the glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) calls.

Thank you! That’s the most valuable point I have learned for the whole week.

1 Like