Voxel Renderer / Engine


Status:
Currently in progress...
Started:
26. April 2025
Working Hours:
~30h
Source Code:
github.com/Roqum/VoxelGameEngine

Buggy Chunk

Introduction

I’m deeply interested in game engine programming and am seriously considering transitioning fully from game development to engine development. I love the combination of solving math-heavy challenges, writing performance-critical code, and designing thoughtful systems. That’s what led me to start this project.

This voxel engine serves as my learning environment. My goal is to build a simple voxel world using procedural generation and to focus on optimizing the rendering pipeline as much as possible. There are many algorithms I’ve read about and am eager to experiment with, combine, and refine. At the end, I want to gain a deep understanding of how things work under the hood in modern game engines.

Project Goals & Milestones

To be more specific about my goals these are some milestones which I want to achieve with this project for now. I might add stuff in the future but for now I want to get a solid Voxel rendering. My ultimate goal is to render water - because it’s a difficult and its awesome.

Rendering Milestones
  • Set up Vulkan rendering pipeline
  • Render a voxel cube
  • Render a voxel chunk
  • Render a voxel world (procedurally generated)
  • Implement basic lighting
  • Add global illumination
  • Rendering water
Rendering Optimization
  • Implement face culling
  • Implement greedy meshing
  • Try out binary meshing
  • Implement Dynamic Chunk loading
  • octree-based chunk management (Not sure yet)
Memory Optimization
  • Improve voxel memory allocation

Current Setup

First step was to implement a functional render pipeline to get Vulkan working. So there was a lot to learn about Vulkan initialization and a lot of boiler plate coding to do. Following the vulkan-tutorial.com tutorial I got a grasp of how stuff is running pretty fast and could make some refinement alongway.

  • Window and surface using GLFW
  • Swapchain handling double-buffered presentation
  • Render pass and framebuffers
  • Graphics pipeline with custom vertex and fragment shaders
  • Vertex and index buffers for mesh data
  • Staging buffer using a dedicated transfer queue family (if available)
  • Uniform buffer for camera perspective and mesh rotation
  • Depth buffer for proper 3D depth rendering
  • Texture loading using stb_image
  • Command buffers and command pool for recording rendering commands
  • CPU-GPU Synchronization using semaphores and fences

Challanges I Faced

With a working rendering pipeline as a foundation, I finally got to the fun part: rendering my first voxel - a simple cube with 6 faces and 12 triangles.

That should be easy, I thought…

But I quickly realized something was wrong. The cube I imagined in my head didn’t match what appeared on screen. I assumed my coordinates were off, so I kept tweaking values - even drew it on paper - but still couldn’t get it right. Eventually, through trial and error, I got a cube that looked correct. Since I couldn’t understand why those coordinates worked, I felt pretty dumb. Maybe I had just mixed up the coordinate axes, I thought. So I decided to test that by rendering a 16×16 voxel chunk next.

But the results were even more confusing. No matter what vertices or indices I used, it looked totally broken:

Buggy Chunk

At that point, I knew something in my rendering pipeline was off, so I took a step back and started reviewing my setup. After hours of debugging, searching, and testing, I finally found the issue:

vkCmdBindIndexBuffer(commandBuffer, vkIndexBuffer, 0, VK_INDEX_TYPE_UINT16);

This line binds the index buffer - and it looked fine at first. But I had defined my indices like this:

	std::vector<uint32_t> indices;

Spot the issue? I told Vulkan to interpret the buffer as 16-bit indices, but actually gave it 32-bit data. That caused incorrect memory alignment and unpredictable rendering behavior.

Once I fixed it by switching to VK_INDEX_TYPE_UINT32, everything rendered correctly - and suddenly the cube coordinates I had in my mind made a lot more sense!

Buggy Chunk

This experience reminded me of the harsh reality of low-level graphics programming: no error messages, no warnings, just a broken rendering image.

But honestly, I love it.

Sure, it’s frustrating to spend hours tracking down a small bug. But finally solving it is incredibly rewarding. And especially in graphics programming, creating new something new out of pure code feels amazing. That’s why I love programming and especially game development.

What comes next?

Firstly, I need to refactor the mess. I just wanted to get stuff working and didn’t bother about organizing my code. But now it’s time to clean it up. Otherwise, continuing development will become a pain later on.

After that I want to implement an algorithm using perlin noise to generate a voxel world terrain to render.

And finally, if that is works, I’m going to focus on optimizing my rendering so I can make the world huge without performance losses.

These are my next steps, but it may take a while, since this is just my side project and I need to stay focused on my main project: a top down ARPG, which you can find here .