Voxel Engine-2024
Summary
My most recent project is a voxel solution built for the Unity Game Engine. Supports dynamic meshes, custom render distances, chunk / voxel sizes, and comes with a suite of custom inspectors / windows to aid in the world creation process. It takes full advantage of the Unity Burst Compiler, Jobs System, and C# Tasks to ensure the best performance possible.
I’ve also made note to have as little data as possible stored in each Vertex of the voxel meshes to allow the GPU to process each vertex more easily. I’m also not relying on a GameObject with a MeshRenderer and MeshFilter, but instead rendering each chunk directly to limit overhead. In addition each chunk mesh is calculated within a C# thread to ensure no hitching when generation is called.
Custom Tools
I put a lot of focus on developing useful and up to date tools for many of the features of my voxel solution. I futher developed my skills with Unity’s UIToolkit and the editor application to deliver better user experience with all the features of the solution. Here’s a list of some tools I provide.
Engine Editor window for accessing and modifying world / debug settings as well as previewing chunks individually.
Noise map inspector that renders a preview of the noise map and updates that preview when values have modified within the inspector.
Biome data inspector which displays all the noise maps associated with the biome.
Entity stats inspector to easily modify multiple aspects of an entity inside one inspector window.
In Editor mesh modifier that allows to you visually modify a voxel mesh by manipulating planes, and vertices while also displaying the vertex data inside the scene view.
Scene view handles to display important debug information such as FPS, CPU time, GPU Time, Player position etc…
The UI Tools provided were all built using Unitys UIToolkit and should be up to modern standards. In addition many of the inspectors are nested meaning no more clicking around individual scipts to make changes. For example the biome data inspector contains 3 noise maps, which all get nested inside the biome data inspector for easy access.
Jobs, Threads, and Optimization
I ensured most functions that could be turned into a Unity Job were; and better yet made sure they were compatible with the Burt Compiler. This allows those functions to be as performant as possible. Functions that couldn’t be burst compiled or turn into Jobs easily were instead threaded using C# Tasks. So even though they main not be as quick to execute as some Jobs, those functions would still not hault the main thread and allow for smoother gameplay.
Garbage collection was a big concern since everything is being computed at runtime. To manage this I took full advantage of native arrays where I could and avoid creating classes since they’d get added to the heap. I also Implimented an Object Pool for the chunks to ensure all chunks were being created during the inital loading of the game.
Rendering Pipeline
I put a lot of time into researching how to best optimize the rendering of chunks and landed on directly telling the GPU to render each chunk instead of using the typcal rendering methods provided with Unity. I stored each voxel in a dictionary with the position being the key and the value being an array of vertex data. This data is then fed to the mesh using a C# Task to allow the mesh to be built on a separate thread. The chunk collision is also calculated on a seperate thread.
I also determine which faces of each voxil is visible using a burst compiled job to ensure it’s calculated as quickly as possible. This lowers the load on the GPU since there’s significantly less vertex data being passed to the mesh’s vertex buffer and thus passed to the GPU for processing. The mesh is then rendered by calling Graphics.RenderMesh inside Update loop of the script. It’s passed atlased material which is instanced, the mesh itself, and the matrix representing the mesh’s TRS.