3
\$\begingroup\$

I'm trying to convert my 3D block work rendering from immediate mode and display lists to VBOs. I'm having trouble understanding how to properly handle creating the buffers: I really don't know how many vertices, colors, and texture coordinates I'll be passing in until after I've already iterated all of the block faces I expect to render.

How can I create a VBO when I don't know it's size? Every single tutorial/example uses a fixed amount.

\$\endgroup\$
5
\$\begingroup\$

You can't. VBOs are not dynamically-resizing resources, so you have to create them with a fixed size (you can create the buffer objects beforehand, but you will need to fix the size of the buffer when you call glBufferData).

You don't really want to be creating and discarding buffer resources every frame (which is what you are implying here, because that would be one of the more direct ways to go about an immediate-mode-to-VBO transition).

You could consider instancing. Essentially here you'd be creating a single VBO to represent a cube and rendering it multiple times for every actual cube (using different, unique-per-cube transformation matrices). This might feel like a lot to bite off right now, though (and is not without its caveats, especially if you have multiple different textures and other effects you'd want applied to different cubes). You should keep it in the back of your mind, though.

As an intermediate step, consider a simple batching approach where you create N VBOs, each of which can hold M cubes:

  • Create your M VBOs when the program starts, each with enough space to hold M cubes. Make sure you create them with the dynamic usage hint, since you're going to be re-writing their content every frame.
  • Every frame, lock a VBO and start to fill it up with cube data as you traverse the scene. When you write enough cubes that you are at the end of the VBO, unlock it and lock the next one in your set. Repeat until you run out of cubes or you run out of VBOs.
  • Render all the VBOs you filled up. Make sure that if you partially fill the last VBO (as you usually will), you account for that when submitting the VBO.

You will need to tweak the specific values of N and M experimentally, most likely. You can also handle the case where you guess low (when you run out of VBO space but still have more cubes to draw) by allocating another M-sized VBO right there, and adding it to your reusable VBO pool so you'll also use it next frame. This will probably result in a stutter that frame, but it's also probably better than just not drawing the remaining cubes.

Now, your question mentioned blocks so I'm gathering you're working on a Minecraft-like game. There are a lot of other questions on this site having to do with Minecraft-like scene representation and rendering that you might find useful:

...and so on. Some of them discuss topics above-and-beyond what you specifically asked, but they may be useful to you.

\$\endgroup\$
  • \$\begingroup\$ So would it make sense to create some sort of pool of FloatBuffers that have enough room for the maximum number of possible blocks per chunk, then I fill that up with every block rendered in that chunk, and then immediately call the render code? Or would not filling a float buffer to complete capacity be a problem? Not only am I trying to solve this problem, but am trying to fully wrap my ahead around VBOs. To help clarify - I have a wide variety of different block textures on a sprite sheet and will eventually have light values applies to blocks. \$\endgroup\$ – BotskoNet Dec 6 '13 at 16:12
  • \$\begingroup\$ You don't need to fill a buffer to capacity, you can render from a subset of the data in the buffer. You may also want to look at the pros and cons of interleaved vertex data. \$\endgroup\$ – Josh Dec 6 '13 at 16:20
  • \$\begingroup\$ So would this work: Create a "pool" of a (maybe 10) float buffers large enough to hold VBOs for max blocks per chunk. Grab an empty buffer each time I need to render a chunk. Add all vertex, color, texture coord floats (for solid blocks in that chunk) into the buffer (interleaved), then render whatever was added for that chunk (glBufferDataARB/glDrawArrays etc) and repeat for every chunk? Since I have a 1000s of blocks of only a few types, instancing sounds perfect. How do I change only the vertices though? Having trouble finding good tutorials \$\endgroup\$ – BotskoNet Dec 6 '13 at 20:53
  • \$\begingroup\$ "How to use instancing?" or similar would be a separate question, I think. I linked one tutorial on the subject, there are many if you search. \$\endgroup\$ – Josh Dec 6 '13 at 20:58
  • \$\begingroup\$ Would the code flow I mentioned be a good route? \$\endgroup\$ – BotskoNet Dec 6 '13 at 23:01
2
\$\begingroup\$

When using OpenGL, generating VBOs is just calling the function glGenBuffer. You don't need to know the size of the buffer until you're ready to add the data to the buffer. So what you can do is generate your buffers, then calculate the vertices you'll need (and all that other stuff). Once you've calculated the number of vertices, you can call glBufferData with the correct size.

Unfortunately I've only used OpenGL in C++ (using glm usually), but I believe the same concepts apply to Java as well. Here's an example of what you can do, assuming you store your vertex info in something similar to a std::vector:

//Generate your buffers
GLuint vboID;
GLuint uvboID;
glGenBuffers(1, &vboID);
glGenBuffers(1, &uvboID);

// Calculate your vertex info here
// We'll assume you use the following for storing your data
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> uvs;

// Once you've calculated your vertex info, load it into your buffers
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * vertices.size(), &vertices[0], GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

glBindBuffer(GL_ARRAY_BUFFER, uvboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * uvs.size(), &uvs[0], GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

// Draw your geometry here
glDrawArrays(GL_TRIANGLES, 0, vertices.size());

Of course, this is a somewhat simplified example, as I didn't include any code for setting your current active program or using a Vertex Array Object, but it illustrates the solution to the problem you're having. Note that you don't have to know how big your vertex data is until your call to glBufferData. Keep in mind there's a lot I didn't cover here (such as making sure you don't call glBufferData over and over again if your data didn't change).

Also, I'm a completely self taught programmer, so I'd appreciate it if anyone points out anything that could be done in a more efficient manner. I learned OpenGL from this site:

http://www.opengl-tutorial.org/

You may also want to check out this site as well, as it also covers OpenGL fairly well:

http://ogldev.atspace.co.uk/index.html

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.