3
\$\begingroup\$

I'm currently looking in to how voxel engines works, with a view to possibly making one, myself. I see a lot of material, like "Let's Make a Voxel Engine", which talks about how to go about reducing the draw calls. What I can't seem to understand is how it actually saves draw call counts, on the basis of the logic being something like the following:

// Without chunks:

foreach voxel in myvoxels
  DrawIfVisible()


// With chunks:

foreach chunk in mychunks
    DrawIfVisible()

    // which then does ...
foreach voxel in myvoxels
    DrawIfVisible() 

Surely, you save nothing by doing this; you still make a draw call for each visible voxel, and a visible voxel needs a draw call in either scenario. The only real saving I can see is that the logic that evaluates a chunk will be able to determine if a large number of voxels are visible or not, effectively, saving a bit of "is this chunk visible" CPU time. It's the draw calls that interest me; the fewer of those, the faster the application.

In case it makes any difference, I will probably be using XNA with DirectX for my engine, so don't consider my choice of example in the link above as my choice of technology. However, this question is such that I doubt it would matter.

\$\endgroup\$
  • \$\begingroup\$ Related: gamedev.stackexchange.com/questions/45148/… \$\endgroup\$ – MichaelHouse Mar 11 '13 at 15:19
  • \$\begingroup\$ @Byte56 similar but not what i'm asking. That question refers to LOD and draw calls (saving by reducing detail), this question is trying understand the logic prior to the draw calls and understand "chunk" savings (saving by rendering less). \$\endgroup\$ – War Mar 11 '13 at 16:41
  • \$\begingroup\$ Yep, that's why it's only related and not a duplicate. It's primarily there so that when people are learning about chunks, they can look to the right and see the "Linked" section to get relevant questions about the subject. \$\endgroup\$ – MichaelHouse Mar 11 '13 at 16:43
  • \$\begingroup\$ The ideal setup is that you make a single draw call per chunk, not per voxel. As Byte56 stated below, each mesh is an entire chunk. With that system in place, you'll have to experiment with chunk sizes for performance trade-offs. Smaller chunks: less expensive updates per chunk, but more of them in a given space, and more draw calls. Larger chunks: more expensive chunk updates but fewer of them and potentially fewer draw calls. \$\endgroup\$ – ChrisC Mar 11 '13 at 18:44
  • \$\begingroup\$ That makes sense, the setup i have at the moment is something like this: class Model { Chunk[] Chunks } class Chunk { block[,,] Blocks } The model class is abstract so I can then create something like "class TerrainModel : Model" which contains a chunk[] ... good approach? i don't know ... i was also thinking about animation and building say limbs for people a limb could be a chunk in a PersonModel object, then I can for example rotate just that chunk to move the persons arm. \$\endgroup\$ – War Mar 13 '13 at 13:36
4
\$\begingroup\$

If you're testing each voxel individually for visibility each time, I suppose you won't save much. However, typically you'd want to build a buffer that represents the surface of your voxel world.

You don't just "draw" voxels, you need to build a mesh that represents the voxels. Building that mesh is expensive, so you want to reduce the number of times you need to do that. The first way is by only rebuilding the mesh when voxels change. So you build a buffer containing the vertices that represent your voxels. Then you draw that buffer every frame.

Now that you have a buffer, you need to rebuild it every time something changes. That means throwing the entire thing away and rebuilding it all. But building is expensive, so maybe there's a way we can reduce the amount of waste when something changes? Yes, we can just rebuild the world around the voxels that changed. If we only have to update the chunk around the changed voxel, we save ourselves a lot of time in rebuilding something we already had.

Now the terrain is chunked into smaller parts, we can just draw the chunks that are visible to us and we can just rebuild the chunks that have voxels change in them. We can do even more now that we have chunks. For example, with chunks that are far away, we can build buffers for those that have reduced polygon count, so they're not so expensive to draw.

All in all, chunks allow us to have more control over what we draw and when. It enables us to implement performance features we wouldn't be able to otherwise.

\$\endgroup\$
  • \$\begingroup\$ so the real challenge in this scenario is not about reducing draw calls ... its about reducing the amount of times I check if something is drawable / needs rebuilding. Since the draw calls in theory should remain constant (based on the number of chunks in the cameras view box) \$\endgroup\$ – War Mar 11 '13 at 16:33
  • \$\begingroup\$ when i say constant, i mean that the view distance is fixed and has an upper limit, and that what you can get in there is finite in terms of chunk count so you will likely only ever make "Number of Chunks In View" draw calls. The bonus being that draw distance could be reduced by say walking in to a small room or cave thus drastically reducing the chunks in view count. \$\endgroup\$ – War Mar 11 '13 at 16:37
  • \$\begingroup\$ It does have the benefit of reducing draw calls, but it's not where the most significant bonuses are. It also makes the infinite worlds possible by being able to unload chunks that are beyond the draw distance. The draw calls don't have to remain constant. You can use a viewing frustum to check if a chunk is in bounds so you only draw those visible to the camera. But yes, typically the draw count is just whatever the number of chunks is within the bounds of the draw distance. \$\endgroup\$ – MichaelHouse Mar 11 '13 at 16:38
  • \$\begingroup\$ Cheeers for the help @Byte56 but there's still something i don't understand ... if a chunk is in view you still have to assess every voxel in it for visibility so you gain nothing from it ... in fact in some respects it's less efficient because you have to make an extra layer of call depth in your logic surely? (not that you'd lose much but in 3d apps all time is precious) \$\endgroup\$ – War Mar 11 '13 at 16:45
  • \$\begingroup\$ The visibility check is not done from the camera's perspective. You check to ensure the voxel is not buried in other voxels. It depends on your rendering style, but for example if you look at the marching cubes algorithm, you're building faces to represent a voxel volume. Once those faces a built, you don't have to check the visibility again, you just draw the buffer that was created. Let the graphics card discard the faces that aren't visible. \$\endgroup\$ – MichaelHouse Mar 11 '13 at 16:53

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.