3D voxel storage texture
2026-07-10
Commit: 67bf4a0
A 3D texture is used for storing the voxel grid.
|
|
Reading voxels from the texture is more straightforward than the byte buffer:
|
|
The voxels are laid out in memory as v = x + y * grid_extent + z * grid_extent². This makes it difficult to utilize the cache, as the ray tracing may have to access neighboring voxels grid_extent² apart from each other in memory. Using a 3D texture should help as it is designed for spatially adjacent data access.
| Metric | Before | After | Delta |
|---|---|---|---|
| FPS | 37.9 | 41.8 | 10.3% |
| Avg on-GPU | 41.94 ms | 35.99 ms | -14.2% |
Using a 3D texture results in less time spent on the GPU, although the improvement is not huge at this stage. A huge number of voxels are still being traversed by the algorithm.
Pack voxels into byte buffer
2026-07-08
Commit: 25b6fd2
Voxels are stored as bytes instead of wasteful uints:
|
|
Voxels are palette indices, with an index of 0 indicating empty space (hence, in Magicavoxel files, only palette indices > 0 are valid). The palette has 255 entries, so using a byte per voxel is sufficient.
Although peak memory consumption is reduced, there is no impact on runtime performance on my M2 Macbook:
| Metric | Before | After | Delta |
|---|---|---|---|
| FPS | 39.97 | 40.17 | +0.20 / +0.5% |
| On-GPU avg | 37.07 ms | 39.22 ms | +2.15 ms / 5.8% slower |
| Peak app memory | 115.80 MiB | 104.09 MiB | -11.70 MiB / -10.1% |
| Metal allocated size | 61.50 MiB | 61.50 MiB | 0.00 MiB |
Naive DDA
2026-07-04
Commit: 0014bd0
Render Magicavoxel models with naive DDA.
- Adds ogt_vox.h for parsing Magicavoxel files.
- Introduces DDA rendering in the pixel shader.
Here’s the gist of the DDA algorithm. For a ray with origin o and direction d, the point at t along the ray is given by
p = o + t * d
The first x-intersection is given by
t = (ceil(o.x) - o.x) / d.x
and similarly for the first y-intersection:
t = (ceil(o.y) - o.y) / d.y
Subsequent x and y-axis intersections are 1 unit apart: t = (ceil(o.x) - o.x) / d.x + (1 / d.x). The DDA algorithm keeps track of which intersection occurred first, and steps along the axis in that direction. The algorithm loops through the grid cells until a stopping condition is met, such as exiting the grid.
Here is a gist of the DDA algorithm, implemented in the pixel shader:
|
|
monu2.vox from ephtracy/voxel-model:

Hello, SDL!
2026-06-28
Commit: 03feb13
All projects have to start somewhere. The rendering infrastructure is set up to render a fullscreen quad.
- SDL_GPU is used as the rendering abstraction. The Metal backend is used on macOS, and the Vulkan backend on Windows.
- SDL_shadercross is used to compile/transpile HLSL shaders to SPIR-V and Metal.
- Shaders are compiled at build time and embedded as byte arrays into the source code.
A window opens displaying the UV coordinates as the color.
