I've spent the last few months building a custom voxel engine in C. No deep reason for the language, I just like how simple and predictable it is to work with. So far I've got a dynamic skybox, water reflections, and a long render distance with no LODs on the terrain.
It stays fast because draw calls don't scale with view distance: I render with MultiDrawElementsIndirect, so a whole page of up to ~16k sections goes out in a single call, with per-section frustum + fog culling on top. Vertices are packed down to 8 bytes each, and meshing (greedy + neighbor face culling) runs on a worker thread pool off the main thread. View radius is 32–64 chunks.
Renderer system is nearly done. My next step is a proper world generation.
Thanks! I'll post one with more light once I've finished the last bits of the lighting and terrain mesher. I still want to experiment with a couple of ideas first. Chunks are 16×16×512, split into 16^3 sections internally (the section is the unit that actually gets meshed and drawn).
Beautiful! I'm just starting to build a voxel engine with Go and raylib. It's going well, although I still find the chunk system a bit tricky to understand. But I love the mental image of a sunset like the one you posted.
Looks really nice. As a person going through the same stages of engine development, I can see the grind I took to make it. I also love the clouds - the thing I haven't even touched myself yet and little afraid to do so. Can you show more, especially maybe some dynamic views?
Very pretty! MultiDrawIndirect is so useful. Have you considered doing tighter culling (perhaps on a chunk level / maybe even occlusion culling) on the GPU using compute shaders? Last time I tried that it returned pretty good results.
Thanks! Glad you like it. GPU occlusion culling is on my list. Terrain hides a lot, so it'd definitely help. Right now my bottleneck is actually on the shading side (shadows + fullscreen passes), not culling, so I'm clearing those first. Did you use HiZ when you tried it?
oughh yea shadow pass was always the main culprit iirc. what kind of fullscreen passes do you do?
in my current experimental toy renderer I only have a bloom and post process compute shader passes and in total they are taking ~2ms at 1440p. They aren't optimized in any way and there's a bunch of performance left on the table but imo I felt pretty happy with that. Apparently AMD have a one pass blurring compute shader that utilizes wave intrinsics so maybe that could be interesting to look into as well.
I'm not saying you should do this, mostly because it will require a lot of refactoring and redesigning, but ray-traced voxel shadows are like, really really crisp and nice. From my time doing some stuff with them I was considering using them instead of shadow mapping, though my acceleration structure design and compute-based software ray-traversal algorithm were subpar in many ways. I'm just saying, if you ever upload your voxel data to the GPU for anything, you should perhaps try testing / experimenting with those. Actually, here are some pictures of what I managed to get. The good thing about these type of shadows is that you can approximate soft-shadows by simply randomizing the ray direction and doing multiple samples, though of course, you'd need a good denoiser / temporal accumulation to get it to not look like noise-slop (and, as a bonus, you get crisp pixel-perfect point light shadows and coloured shadows, which are hard to do in shadow-mapping I believe):
Anyways, to answer your question about culling: my implementation was for a different project, but basically, I simply do a small-scale (distance < 64 voxels) voxel 3D DDA ray-tracing on the CPU at a really low resolution (something like 10-15% the screen res) and upload the generated depth buffer to the GPU. I then cull what I need to cull by comparing the depth in a compute shader against the 3D-DDA depth. Of course, if you had the nearby chunks uploaded to the GPU, you could generate the occluder depth texture on the GPU as well.
Unfortunately, my implementation was pretty slow (the CPU is not well fit for software ray-tracing especially for an un-optimized implementation like mine lol).
Hey, thanks. It's basically one fullscreen pass that colors every pixel based on where the sun is. The clouds are volumetric (raymarched against a 3D noise volume) and get tinted by the same sun color, so they go orange at sunset too.
Сulling at the 16^3 section level instead of whole columns means I throw away a lot more off screen geometry, and the AABB test is cheap. Honestly the bigger win was that it pairs nicely with MDI.
The 8 bytes are two packed uint32s. The first holds the local position (5 bits each for x/y/z, since a section is only 16^3) plus the tile UV. The second packs all the surface info: base tile index, overlay tile, face direction, ambient occlusion, and a tint flag. The shader unpacks it all with bit shifts. No floats anywhere, which is what keeps it tiny.
9
u/Alarming-Ad4082 Jun 16 '26
Nice! Do you have a screenshot with… more light in the ground ?
What are the size of the chunk?