I decided to make a voxel engine using octrees. I am instancing entire cubes and sending the sizes and positions to the vertex shader.

I currently have 2 major problems:

Problem 1 - Because the cubes are instanced I can't remove hidden faces which means that some pixels are being overwritten lots of times. I have backface culling enabled but it doesn't fully help - also I noticed depending on the direction I look the lag is more / less which means sorting it might help, but then I don't really want to be sorting my cubes every frame.

Problem 2 - A 64*64*64 octree takes around 1/2 a second to fill and compress which is too slow, probably needs to be 1/10th of that speed. My method for that is as follows:

I have a generation function bool isCube(glm::vec3). Then, I create a full octree and fill up the base nodes with 0 or 1 depending what isCube returns for that given position. Finally, I compress it by checking if all leaves are the same, remove them, and store that value in the node above.

For reference the current isCube function looks like this:

bool isCube(glm::vec3 pos)
    if (glm::length(pos) < 300)
        return true;
    return false;

What sort of measures can I take to reduce fillrate?

What is the best way to go about generating landscape with octrees?


1 Answer 1


The overhead of instantiation on the GPU is huge relative to those 12 triangles in each cubes. GPUs don't like small (12-triangles) batches. The faster the GPU the worse this overhead gets compared to the raw amount of triangles it can render.

Instead bake everything into larger "chunks" of geometry VBOs. You can even bake-in ambient occlusion/shadows (Eg: under the trees and mountain shade).

This way you can eliminate hidden faces.

For the rendering itself there is no need for an octree-structure, a simple list of active chunks VBOs that can be quickly traversed to render is sufficient.

An Octree comes in handy when processing visibility (or collisions): which chunks to add/remove from the active list but not for the rendering loop itself.

Separating the two has multiple advantages. One is that you may need to queue loading and geometry generation of chunks according to visibility to be run in separate thread(s).

This means they're visible but not ready to render.

You don't want to burden the render loop with this "Is my chunk ready?" check as you need to do the check to start loading (and unloading) anyway.

enter image description here

For VR (two eyes, similarly located) you can create a frustum box that encompasses both eyes and only does 1 visibility check instead of two. There will be some unnecessary GPU render commands for each eyes but the percentage is so small that avoiding the check CPU-side for an entire eye is more beneficial.

enter image description here

  • \$\begingroup\$ hmm this is very interesting, for reference what size chunks did you use and how long does it take to generate the mesh so that its ready to render? \$\endgroup\$ May 21, 2018 at 18:46
  • \$\begingroup\$ How long depends on the processor speed and the amount of content and if the chunk needs to be generated first (spawn trees, etc) or just loaded from file and converted to polygons. A beagleboard (single-core ARM Cortex-A8 at 720Mhz, en.wikipedia.org/wiki/BeagleBoard ) can manage it in decent time (video: youtu.be/Q0cUgpgmpvg?t=63) of course for this machine I reduced the field of view quite a bit. The screenshots were on an quad-core i7 2.2Ghz with GTX 560M (generates >10 chunks per second). The chunks were set to 16x16x16 (4096 cubes per chunks). \$\endgroup\$ May 21, 2018 at 19:08
  • \$\begingroup\$ @TheoWalton if you skip to the end of the videos (9:30 youtu.be/Q0cUgpgmpvg?t=570 ) you can see the chunks getting generated as I walk toward a new area. I'd say the BeagleBoard manages about 10chunks/secs. It's hard to tell because some of the chunks are invisible empty sky chunks. I didn't print stats for those. Only Rendered Chunks (rc), Standby Chunks (sc), and Unloadable Chunks (uc) (the recycling cache pool) and the total number of voxels (cubes) loaded ((sc+uc) * chunk size) \$\endgroup\$ May 21, 2018 at 19:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .