Foundry Dev Blog #17 - New Lighting System & Art Backlog
Here's another quick update on how things are going.
New Lighting System
Caves are finally dark! Our engine, Unity, has no support for realtime global illumination - at least not in a way we would need it for our voxel worlds. So we spent some time writing our own voxel based light propagation algorithm. This was quite an adventure but we eventually managed to pull it off. The system needs a bit more polishing but it's already working without any noteworthy bugs or issues.
The main problem before was that our day and night cycle happened inside our caves. That was because ambient light was calculated globally instead of based on position and surrounding geometry. The new algorithm supports sunlight (intensity will change during the time of day) and normal light from things like light poles (intensity stays the same, no matter the time of day). Since we check where sunlight can go, caves will be dark by default.
A cave created with the new explosives
For the technical geeks out there, I'll explain how it works. Our native (C++) code has control over the terrain/voxel data, so everything is done there. Our voxel world is divided into chunks of 32x32x256 blocks (with 256 being the height) and we also calculate the lighting data per chunk.
To do so we start with the assumption that everything is pitch black. Then we simulate the sunlight, we do a very simple form of voxel-ray-casting from the sky towards the bottom, and mark every block sunlight reaches with maximum sun-brightness, a value that later is modified by the current time of day to fade the brightness.
After this we let the sunlight "bleed" in surrounding non-sunlight-exposed blocks and reduce the brightness gradually until it fades to fully dark.
As a final step we simulate our "always-on" lights (player built lights) the same way and add those in. The final result is two light values (sun brightness, custom light brightness) per block in a large array.
But how does it actually get applied in rendering? The solution is pretty straightforward, we have a global lightmap as a Texture3D, which is injected into our deferred lighting pass and sampled to calculate the global illumination for a given position.
Filling the lightmap however is a bigger problem, Unity does not allow partial updating of a Texture3D, except for single-pixel-edits, so we had to cut Unity out of the equation by accessing the DX11 API directly inside our native code. The system is designed with an abstraction layer, so adding Vulkan/DX12 can be easily done.
Yet that is not all of it, if the lightmap would be centered around the player or the most centered currently loaded chunk, we would have to regenerate the whole lightmap from scratch when the player moves to the next chunk, which would be way too costly. The solution around this is a sliding window algorithm, basically an array with a read cursor and the data wrapped around it accordingly. The access point is again calculated inside the shader.
We are missing two more things: The underground mining system - containing a Depot, Tracks, Minecarts and a Rail Miner - and the Character Elevator. We continue to not give any further ETAs (see last blog post) but progress is good. And while the Update 0.3 is delayed I really want to put emphasis on that we are now adding more things that require no art to be done, so Update 0.3 is getting even better. The lighting system for example was already not guaranteed to be in 0.3 when we first announced it.
Meanwhile finished art is the Freight Elevator and three door types.
So long, - mrmcd