Well, I’ve been working hard on cascades and directional voxel data lately. Here’s how that’s going. I’ve included some technical details for the nerds who are curious about them ![]()
So, the core functionality of cascades is working now. So, instead of having one volume at 256x256x256 resolution, there are 8 cascade levels, and each level is 128x128x128 resolution. Overall it uses about the same amount of memory as before (around 500 mb at “high” resolution, even with the direcitonal voxel data that I’ll explain below). Only one cascade is revoxelized per-frame, so the overall work of voxelization is reduced when compared to not having cascades. Each cascade’s volume covers an area twice the size of the previous cascade.
Voxelization with cascades works like this. First, the lowest level cascade (the smallest one) is completely voxelized. Then, on the next frame, the next highest cascade is voxelized, but voxelization is skipped in the area inside this cascade that the previous one overlaps. Instead, the data from the previous cascade is filtered and injected in this overlap area. This means that there’s still somewhat of a “mipmapping” going on in areas where cascades overlap. This saves a bit of work, too. The process is repeated for each cascade level and starts over when the last cascade has been updated. Voxelization is also super-sampled x4 (x16 effective SSAA) to ensure smooth results in areas that don’t overlap.
Cone tracing is still rather naive to cascades at this point. I’m pretty much tracing in the exact same way as before (since the distribution of data is much like what you get with mipmaps, where each higher level cascade is half the resolution spatially). Though, for whatever reason, I got very incorrect results without tweaking the cone width calculations, which still puzzles me. I’m still struggling to get the cone tracing to look like it did before cascades (which, again, is very strange since the data layout is so similar spatially).
So, here’s the result of some tests with the Courtyard scene. Direct sunlight injection is still a little buggy, so the scene is only lit by skylight for demonstrative purposes. Here’s how the scene looks up-close.
8 levels of cascades yields some pretty insane render distance, as you can see here. And for clarification, I did not adjust the Voxel Space Size property between these two images.
You can see that the cone tracing itself doesn’t look as good (which I’m still trying to figure out), but cascades are really doing their job at improving the render distance of GI so far.
So, I had a few options regarding directional voxel data. With other voxel-based GI solutions out there, representing directional light data is done by storing separate light values for each of the 6 “faces” of each voxel. I saw two problems with this.
First, this requires storing 6x the amount of data when compared to non-directional light data. That’s a pretty big price to pay, considering that memory usage is usually a constraint with voxel-based GI. I’ve seen solutions where, with classic mipmaps, only the lower levels have directional data, saving memory in that regard, but this doesn’t translate to cascades.
The second problem is that whenever you have a surface that is perfectly aligned to an axis in the volume, directionality is represented perfectly, but if it’s even a little bit off, directionality becomes pretty inaccurate. I’ll try to whip up a diagram to explain why.
When light is represented in discrete directions like this, perfectly aligned walls will work well, but even slightly misaligned walls would leak quite a bit.
So the solution that I came up with was to use Spherical Harmonics instead. 2nd-order Spherical Harmonics requires 4 coefficients per color channel. That’s less data than the above method, and it has the benefit of representing arbitrary light directions.
So, how does it look? For comparison, here’s the Sponza Atrium without directional voxel data. The colored curtains are being lit by sunlight, and light is bouncing incorrectly behind the curtains due to a lack of directional data. This is a classic problem I see all the time with voxel-based GI solutions.
Here’s how it looks with directional voxel data enabled.
The drawback is that cone tracing becomes more expensive since 4 color values have to be fetched instead of 1 (for the SH coefficients). That’s a pretty big drawback.
So, overall the cost of voxelization has decreased, but the cost of tracing has increased. Of course, I’m not happy with the results yet, both visually and performance-wise. I don’t really know how all this is going to turn out. Maybe Spherical Harmonics voxel data is the future! Maybe I’ll have to look for another solution. All I can say is that I’ll keep working on it and let you guys know how it goes!





