/r/VoxelGameDev

Photograph via snooOG

A game development subreddit for discussing the creation of voxel games, and voxel engines.

A game development subreddit for discussing the creation of voxel games, and voxel engines.

Filter by flair

Discussion - Question - Media
Article - Tutorial - Resource

Rules

  1. Keep posts relevant to other developers and titles representative of the content (no click-bait) - topics of interest include algorithms and code snippets, artistic techniques, free assets, tools or engines, and game design. All posts should be voxel-related and should be of interest to (or require feedback from) other developers, not just gamers. Titles, including the titles of linked content such as videos and articles, should be representative of the content and free of click bait.
  2. Avoid advertising and promotional posts - this subreddit is generally not the place to advertise or promote your game or product. Avoid submitting links to game trailers, press releases, app store pages, etc. However, you may showcase your work from an artistic or technical perspective (but see the next rule), and occasional posts of game development products will be permitted.
  3. Limit the frequency of 'showcase' posts - It is easy for useful development resources to get lost among people showing off their work. Therefore please ensure that any showcase posts show significant progress from previous posts, and use Voxel Vendredi for more incremental updates. Provide information about your development process, languages, tools or tips for other developers. Don't just cross-post content from gaming subreddits.
  4. No rude/offensive posts or personal attacks
  5. No AI, no blockchains (NFTs, cryptocurrencies, Web3 etc.) - No posts or comments about AI, blockchains (NFTs, cryptocurrencies, Web3 etc.) and projects which use them. We may remove posts/comments which refer to projects which actively use AI or blockchains.
  6. Voxel Vendredi - The weekly Voxel Vendredi thread is more relaxed and ideal for showing off you progress updates and screenshots, as well as asking for feedback or play testing. It starts every Friday - 'vendredi' in French - and runs over the weekend. Shameless plugs, progress updates, screenshots, videos, art, assets, promotion, tech, findings and recommendations etc. are all welcome.

Wiki

Resources

Open Source Voxel Engines:

Information:

Related Subreddits

External Links

/r/VoxelGameDev

14,085 Subscribers

0

Make a roblox like game

I had this idea for some time however idk how to make a game like it because I have not that much experience any tutorials or suggestions

4 Comments
2024/10/29
17:49 UTC

5

How to import Magica Voxel Materials in Unreal Engine using Voxy

0 Comments
2024/10/28
13:01 UTC

25

Ethan gore scares me

did the math, his engine can render the earth 64 times at a res of 1mm per voxel. Wtf processes is he doing

17 Comments
2024/10/28
11:27 UTC

23

Tiling on a custom generated mesh.

3 Comments
2024/10/28
03:32 UTC

9

What is the best language to code a voxel game that is simple

I tried ursina but it's super laggy even when I optimize it

is there a language that is as simple and as capable as ursina

But is optimized to not have lag and the ability to import high triangle 3D models

please don't suggest c++ I have a bad experience with it

29 Comments
2024/10/27
21:38 UTC

8

Terrain voxelizer tool?

I've been trying to look online for this but all I could find is how to create procedural terrains like Minecraft, or smooth voxel terrains.

What I'm actually looking is a non procesual, teardown like voxel terrain, that won't me much but a simple voxelized terrain. I'm thinking maybe there's a tool out there where I could simply export a blender, or better a GAEA generated terrain and apply vixelization to it?

1 Comment
2024/10/27
10:17 UTC

62

Just added Smelting, the first animal (Cows), and more!

21 Comments
2024/10/25
15:46 UTC

5

Voxel Vendredi 25 Oct 2024

This is the place to show off and discuss your voxel game and tools. Shameless plugs, progress updates, screenshots, videos, art, assets, promotion, tech, findings and recommendations etc. are all welcome.

  • Voxel Vendredi is a discussion thread starting every Friday - 'vendredi' in French - and running over the weekend. The thread is automatically posted by the mods every Friday at 00:00 GMT.
  • Previous Voxel Vendredis
4 Comments
2024/10/25
00:00 UTC

34

An interactive viewer I made for my voxel store

7 Comments
2024/10/24
20:15 UTC

8

I made my first voxel engine, this is what it looks like so far (youtube video)

6 Comments
2024/10/24
18:30 UTC

76

Applied better shadows and collision to my game - Skybase

11 Comments
2024/10/20
16:21 UTC

5

How can I speed up my octree traversal?

Hey, I've recently implemented my own sparse voxel octree (without basing it on any papers or anything, though I imagine it's very similar to what's out there). I don't store empty octants, or even a node that defines the area as empty, instead I'm using an 8 bit mask that determines whether each child exists or not, and then I generate empty octants from that mask if needed.

I've written a GPU ray marcher that traverses it, though it's disappointingly slow. I'm pretty sure that's down to my naive traversal, I traverse top to bottom though I keep track of the last hit node and continue on from its parent rather than starting again from the root node. But that's it.

I've heard there's a bunch of tricks to speed things up, including sorted traversal. It looks like it should be easy but I can't get my head around it for some reason.

As I understand, sorted traversal works through calculating intersections against the axis planes within octants to determine the closest nodes, enabling traversal that isn't just brute force checking against all 8 children. Does it require a direction vector, or is it purely distance based? Surely if you don't get a hit on the four closest octants you won't on the remaining four furthest either too.

Can anyone point me towards a simple code snippet of this traversal? Any language will do. I can only seem to find projects that have things broken up into tons of files and it's difficult to bounce back and forth through them all when all I want is this seemingly small optimisation.

Thanks!

8 Comments
2024/10/20
14:58 UTC

47

How i render Voxels (Lum)

Lum is this thing:

I wanted to share some technical details about Lum renderer, specifically optimizations. Creating a good-looking renderer is easy (just raytrace), but making it run on less than H100 GPU is tricky sometimes. My initial goal for Lum was to make it run on integrated GPUs (with raytraced light)

I divide everything into three categories:

  1. Blocks - 16^(3) arrays of voxels, grid-aligned
  2. Models (objects) - 3D arrays of voxels, not grid-aligned
  3. Everything else that's not visible to voxel systems (in my case: grass, water, particles, smoke)

"Voxel" sometimes refers to a small cube conceptually, and sometimes to its data representation - material index referencing material in a material palette. Similarly, "Block" can mean a 16^(3) voxel group or a block index referencing block palette
 

This is more of a Voxel + GPU topic. There is some more about GPU only at the end

Common BVH tree (Bounding Volume Hierarchy) structures are fast, but not fast enough. For voxels, many tree-like implementations are redundant. I tried a lot of different approaches, but here is the catch:
Memory dependency. Aka (in C code) int a = *(b_ptr + (*shift_ptr)). shift_ptr has to be read before b_ptr because you don’t know where to read yet

My thought process was:

  1. The easiest and almost the fastest traversal structure is a simple 3D array [image]
  2. But my voxel game (future game. Classic "i want to make a game but i'm making engines") has multiple repeating blocks
  3. So, don’t store the same block multiple times - use a reference (I call it a palette)
  4. But "dereferencing" is expensive("scoreboard stall"), so going further (grouping blocks into big_blocks of 2^(3) and referencing them) is not worth it
  5. Especially if the highest-level structure with references already fits into L1 cache. The final structure is a 3D array of references to 16^(3) blocks via "int id" id = 0 is empty, which is somewhat important

so there are three main data structures used in the voxel system:

3D array of int32 with size [world_size_in_blocks.xyz], storing references to blocks in the world
Array of blocks (block is [16^(3)])with size[MAX_BLOCKS], storing voxel material references in a block palette*
Array of material structures with size [MAX_MATERIALS], storing the material definitions used by voxels
*for perfomance reasons array is slightly rearranged and index differently than trivial approach

But what about models?

  1. Tracing them separately with respect to transformation matrices would be too expensive (what is the point then?)
  2. So, we need to integrate them into blocks with references
  3. However, objects are not aligned with blocks, and the same block intersection with different models in different places results in different data
  4. The solution was to create temporary blocks (which also have only one reference in most (currently, 100%) cases)
  5. Where to create blocks? "Blockify" (like voxelize but not to voxel but to 16^(3) voxels) every object on the CPU (very cheap), and clone every touched block to a new temporary block (on GPU). Blockification can be done on the GPU, but is actually slower (atomics and reductions needed)
  6. But new temp block doesn’t have any object data yet - it is just a copy. Now we map (copy) each model voxel to corresponding temp block voxel with respect to model transformation (on the GPU)

So now we have the general data structure built. But what’s next? Now we need to generate rays with rasterization. Why? Rasterization is faster than ray tracing first hit for voxels (number of pixels < number of visible voxels). Also, with rasterization (which effectively has totally different data structures from the voxel system), we can have non-grid-aligned voxels.

I do it like this (on my 1660 Super, all the voxels are rasterized (to gBuffer: material_id + normal) in 0.07 ms (btw i’m 69% limited by pixel fill rate). There is total ~1k non-empty blocks with 16^3 = 4096 voxels each):

  1. Blocks are meshed into 6 sides
  2. 3 visible sides for each are rendered
  3. Each side is a separate draw call
  • You may say "What a stupid way to render! Draw calls are expensive!"
    • Yes, they are. But 0.07ms
    • They are less expensive in Vulkan than in OpenGL / DirectX<12 / commonly used game engines
    • They move computations higher in the pipeline, which is worth it in the end (btw, this is the primary optimization - doing less work)
    • Though I agree that my way of handling data that does not change per frame is not the best. To be improved (what changes is order of blocks (sorted for faster depth testing) and which blocks are not culled)
    • For anyone wondering, vertices are indexed (turned out to be faster than strip)
  1. Normals are passed with pushed constants (~fast small UBO from Vulkan) to save bandwidth (memory) and VAF (Vertex Attribute Fetch)
  2. To save even more VAF and memory, the only(!) attribute passed to the VS is "u8vec3 position" - three unsigned chars. This is not enough for the global position (cause range of [0, 255]), so the "global shift of an origin block/model" is also passed (via pushed constants)
  3. Quaternions are used instead of matrices (for rotation - but no rotation for blocks)
  4. I also use hardware attributes for passing data to the fragment shader (u8vec3 material_normal packed into flat uint32), but there is no big difference

Now the sweet part:

  1. The gBuffer needs normals and materials. Normals are passed with push constants. Where is the material? It is not in attributes
  2. It turns out, if you include the material in the mesh data, there are too many vertices (and each triangle is ~20 pixels, which is a bad ratio), because now vertex attribute are different for sides of different-material voxels
  3. But if only they could be merged (like greedy meshing, effectively a contour)... Then full blocks could be just cubes (8 vertices) (structurally complicated blocks still contain more vertices). But where is the material then?
  4. Material data is sampled directly from block data using vec3 local_position, which is position of a fragment interpolated from position of a vertex in a local block (or models, same used for them) space
  5. Yes, this is a lot more work in the fragment shader (and memory read waiting on memory read). But it was almost empty anyways, and the vertex shader was the bottleneck atm
  6. And now, even if every voxel is different, for a solid cube of them, there will be 6 sides, 6 vertices each. This is what brought times down from 0.40 ms to 0.07 ms.

The idea to do this appeared in my brain after reading about rendering voxels with 2D images, rasterized layer by layer, and my approach is effectively the same but 3D.

So, now we have a fast acceleration structure and a rasterized gBuffer. How does Lum raytrace shiny surfaces in under 0.3 ms? The raytracer shader processes every pixel with shiny material (how it distinguishes them is told in the end):

  1. I tried multiple algorithms - bit packing (bit == 1 means the block is present; in theory, this reduces bandwidth, but in practice, it increases latency), distance fields (state-of-art algorithm is only O(number_of_voxels_total)), precise traversal
  2. But the fastest is the good old "pos += step_length * direction" With step_length = 0.5, it even looks good while running ~50% faster
  3. Every approach benefited from branching on "if the block is empty, then teleport to its edge and keep traversal from there" (which is like skipping high-level nodes in tree. In some sence, my structure is just a highly specialized hardcoded octo tree) *there is a lot of shader magic, but it is way too off-topic*
  4. Hit processing is very simple (tho i'm good at physics, and thus possibly biased) - most generic BRDF

Non-glossy surfaces are shaded with lightmaps and a radiance field (aka per-block Minecraft lighting, but ray traced (and, in the future, directional) with almost the same traversal algorithm) and ambient occlusion.

more GPU

no matter what API you are using

  1. Shortly:
    • USE A PROFILER
    • Seriously. "Shader profiler" is a thing too
    • Keep register usage low (track it in profiler)
    • Don’t use fragment shader input interpolation too much (passing too many vec4s will likely limit throughput to about ~1/3). You can try to pack flat int data into a single flat int (track it in profiler)
    • Be careful with a lot of indirect memory access; don’t be CTA-limited in compute shaders (honestly, just stick to 64 invocations) (track it in... You guessed it, profiler)
    • Don’t trust drivers with unrolling loops (and sometimes moving textureSize from loop, lol) (track instruction count in profiler). Add restrict readonly if possible. Some drivers are trash, just accept it
    • Move stuff up in the pipeline
    • Respect memory (small image formats, manual compression if possible, 8/16-bit storage for UBO/PCO/SSBO)
    • Prefer samplers for sampling (rather than imageLoad)
    • Subpasses are key to integrated GPUs (and Nvidia, and the future-promised AMD)
  2. Subpasses are good, even for desktop. Downscale + low-res + upscale is probably slower than carefully designed per-pixel full-res subpass:
    • Subpasses are effectively a restriction on where you read pixels and where you write them (only pixel-to-matching-pixel allowed, as if every pixel does not see its neighbors).
    • This allows the GPU + driver to load/unload shaders instead of loading/unloading image memory (which just stays on chip for a spatially coherent group of pixels, like 16x16).
    • This is crucial for mobile (smartphones, Switch, integrated GPU's) who can afford only slow memory. Example: if you load / unload the whole 2mb image just to add a value to a pixel - they suffer.     4) With subpasses, they would just add a value to already loaded into fastest cache pixel to known-in-advance position, and just go to next shader without unloading cache
  3. Compute shaders are worse for per-pixel effects than subpasses:
    • Compute shaders can't be part of subpasses (unless you are on a Huawei) - most important
    • stencil test lightning-fast for discarding pixels without even starting the shader*, which compute does not support too (i use it to mark glossy shaders and test against the flag) (*gpu's rasterizes by putting pixels into a groups of invocations, and stencil test is before that, so instead of early-return and just idle invocation you get 100% load)
    • they do not support hardware depth testing. There is a lot of stages when depth testing can happen. And early depth testing does not result in any pixels processed by fragment too Why is depth testing important for post processing? Unreal engine uses depth to index material and then test against equality, for example
    • they dont supprot hardware-blending (that has logic! I use it for min/max for example)
    • they are not automatically perfectly balanced (driver knows better)
    • they do not (in a fast way) support quad* operations like derivatives (*gpu's process pixels in groups of 4 (2x2 quads) to know how much does the value (e.g. UV) change to know how far sampled pixels from (as example) a texture are and if they seem too far away, smaller mipmap is used)
    • compute shaders may overcompute (single fullscreen triangle will not). But triangle has to be rasterized, which is also some work. Win for compute in here i guess
  4. changing state is less expensive than more work in other words, sorting by depth is better than sorting by state

Everything said should be benchmarked in your exact usecase

Thanks for reading, feel free to leave any comments!
 
please star my lum project or i'll never get a job and will not be able to share voxels with you

5 Comments
2024/10/20
07:23 UTC

72

Improved the combat in my Voxel Game! Does it look good? - Voxtopolis

4 Comments
2024/10/19
20:44 UTC

26

Stress testing my SVO renderer

9 Comments
2024/10/18
21:26 UTC

8

Voxel Vendredi 18 Oct 2024

This is the place to show off and discuss your voxel game and tools. Shameless plugs, progress updates, screenshots, videos, art, assets, promotion, tech, findings and recommendations etc. are all welcome.

  • Voxel Vendredi is a discussion thread starting every Friday - 'vendredi' in French - and running over the weekend. The thread is automatically posted by the mods every Friday at 00:00 GMT.
  • Previous Voxel Vendredis
18 Comments
2024/10/18
00:00 UTC

4

Wurm Online terrain

https://preview.redd.it/jl93vzbcibvd1.png?width=1424&format=png&auto=webp&s=ad8c353d0eab60fea5d378d3eb1ac0fd94761bd2

https://preview.redd.it/2ycuuzbcibvd1.png?width=630&format=png&auto=webp&s=5817f6e86e72637188a3fe54c446dda8aa6ba98f

https://preview.redd.it/zue8wkdcibvd1.png?width=1342&format=png&auto=webp&s=a6ee4864d5fcc42ae064beff6d7e1c82ca251f24

I've been trying to somehow create a terrain with mesh generation much based in the system of the MMORPG Wurm Online, between each tile, there's other tiles with diferent sizes (corners and borders), any tips to bring it into reality coding for Unity?

1 Comment
2024/10/17
13:26 UTC

1

Making simple cubes with beveling

So I am very newbee to the whole voxel generation thing, I dont intend on auto generating. Or anything correlated to LOD. I just want to spawn a fixed size grid made of white beveled cubes. And I was wondering if there is any optimization technique I should look into any tips?

3 Comments
2024/10/17
11:41 UTC

29

Finally got my SVO to work correctly

0 Comments
2024/10/16
23:28 UTC

25

Thought I'd set the chunk size to 1x1 to see what happens. Looks really cool but definitely not the best for performance.

21 Comments
2024/10/16
00:53 UTC

11

Meshing chunks when neighbour voxels aren't known

I am making a Minecraft clone and I want to add infinite world generation and make it threaded. I want the threads to act like a pipeline with a generation thread then pass it to a meshing thread. If a chunk is being meshed while some of its neighbours haven't been generated yet and don't have any data to use with culling, it will just assume to cull it. The problem is when the neighbours have been generated, the mesh won't be correct and might have some culling where it isn't supposed to.

A solution to this that I can think of is to queue all neighbours for remeshing once a neighbour is generated. This does mean there will be chunks remeshing over and over which seems like it will be slow. How can I solve this?

10 Comments
2024/10/15
22:24 UTC

100

Experimenting with voxel planets for a space game

9 Comments
2024/10/15
20:49 UTC

61

First steps with real-time path tracing

7 Comments
2024/10/15
02:16 UTC

14

Github repo + Demo!

I just released the first demo of my project, its just a basic voxel ray tracer but this will be in future the repo for my voxel engine, the 1.0 release should be out later this week/month if your interested.
https://github.com/MountainLabsYT/Quark

I haven't shared the code for the voxel ray tracer because its messy. but I'm currently trying to optimise it. Once again, full release of the basic engine later this month.

for now i'm just going to include some basic voxel raytracing code for anybody getting started with voxel raytracing.

4 Comments
2024/10/13
20:17 UTC

19

Hey Reddit! We’ve just made Super Citycon, our new mobile city-building game, available for open testing on Android! 🏙️ If you’re into city games, we’d love for you to try it out and share your thoughts. Your feedback would be super helpful! Link is in the comments. Thanks, and happy building!

17 Comments
2024/10/13
13:11 UTC

6

Sparse octree creation

What would be the fastest approach to create a spare octree from a grid of voxels? Are there any parallelizable algorithms so i can perform the computation on GPU with a compute shader? In my project i have to generate multiple octrees at runtime and i need something fast.

1 Comment
2024/10/13
07:25 UTC

7

Question about perlin worms/worm caves

Right, so I am working on making some interesting cave generation, and I want there to be winding tunnels underground punctuated with large/small caverns. I know pretty much how Minecraft generates its worm caves, and that's by doing "abs(noise value) < some value" to create a sort of ridge noise to make noodle shapes like these:

noodles

and make the white values mean that there is air there. I have done this:

public static VoxelType DetermineVoxelType(Vector3 voxelChunkPos, float calculatedHeight, Vector3 chunkPos, bool useVerticalChunks, int randInt, int seed)
    {
        Vector3 voxelWorldPos = useVerticalChunks ? voxelChunkPos + chunkPos : voxelChunkPos;

        // Calculate the 3D Perlin noise for caves
        float caveNoiseFrequency = 0.07f;  // Adjust frequency to control cave density
        float wormCaveThreshold = 0.06f;
        float wormCaveSizeMultiplier = 5f;
        float wormCaveNoise = Mathf.Abs(Mathf.PerlinNoise((voxelWorldPos.x + seed) * caveNoiseFrequency / wormCaveSizeMultiplier, (voxelWorldPos.z + seed) * caveNoiseFrequency / wormCaveSizeMultiplier) * 2f - 1f) 
                        + Mathf.Abs(Mathf.PerlinNoise((voxelWorldPos.y + seed) * caveNoiseFrequency / wormCaveSizeMultiplier, (voxelWorldPos.x + seed) * caveNoiseFrequency / wormCaveSizeMultiplier) * 2f - 1f) // *2-1 to make it between -1 and 1
                        + Mathf.Abs(Mathf.PerlinNoise((voxelWorldPos.z + seed) * caveNoiseFrequency / wormCaveSizeMultiplier, (voxelWorldPos.y + seed) * caveNoiseFrequency / wormCaveSizeMultiplier) * 2f - 1f);// instead of between 0 and 1

        float remappedWormCaveNoise = wormCaveNoise;

        remappedWormCaveNoise /=3;

        if (remappedWormCaveNoise < wormCaveThreshold)
            return VoxelType.Air;

        // Normal terrain height-based voxel type determination
        VoxelType type = voxelWorldPos.y <= calculatedHeight ? VoxelType.Stone : VoxelType.Air;

        if (type != VoxelType.Air && voxelWorldPos.y < calculatedHeight && voxelWorldPos.y >= calculatedHeight - 3)
            type = VoxelType.Dirt;

        if (type == VoxelType.Dirt && voxelWorldPos.y <= calculatedHeight && voxelWorldPos.y > calculatedHeight - 1)
            type = VoxelType.Grass;
        
        if (voxelWorldPos.y <= -230 - randInt && type != VoxelType.Air)
            type = VoxelType.Deepslate;

        return type;
    }

and that generates caves like this:

i know it's close to the surface but still

it's alright, but it doesn't go on for that long and it is slightly bigger than I would like. This is mostly because I'm scaling up the ridge noise by like 5 times to make the tunnels longer and less windy and decreasing the threshold so that they're not so wide. The types of caves I want that would be long constant-width windyish tunnels, and I know that that can be generated by using perlin worms, right? Those are generated by marking a starting point, taking a step in a direction according to a perlin noise map, carving out a sphere around itself, and then repeating the process until it reaches a certain length, I think. The problem I have with this is that when a chunk designates one of its voxels as a worm starting point, then carves out a perlin worm, it reaches the end of the chunk and terminates. The worms cannot go across chunks. Could this be solved by making a perlin worms noise map or something? idk. Please provide assistance if available :D

6 Comments
2024/10/12
20:35 UTC

Back To Top