Sunday, July 19, 2015

xna - How can I implement voxel-based lighting with occlusion in a Minecraft-style game?


I am using C# and XNA. My current algorithm for lighting is a recursive method. However, it is expensive, to the point where one 8x128x8 chunk calculated every 5 seconds.




  • Are there other lighting methods that will make variable-darkness shadows?

  • Or is the recursive method good, and maybe I am just doing it wrong?


It just seems like recursive stuff is fundamentally expensive (forced to go through around 25k blocks per chunk). I was thinking about using a method similar to ray tracing, but I have no idea how this would work. Another thing I tried was storing light sources in a List, and for each block getting the distance to each light source, and using that to light it to the correct level, but then lighting would go through walls.


My current recursion code is below. This is called from any place in the chunk that does not have a light level of zero, after clearing and re-adding sunlight and torchlight.


world.get___at is a function that can get blocks outside of this chunk (this is inside the chunk class). Location is my own structure that is like a Vector3, but uses integers instead of floating point values. light[,,] is the lightmap for the chunk.


    private void recursiveLight(int x, int y, int z, byte lightLevel)
{
Location loc = new Location(x + chunkx * 8, y, z + chunky * 8);
if (world.getBlockAt(loc).BlockData.isSolid)

return;
lightLevel--;
if (world.getLightAt(loc) >= lightLevel || lightLevel <= 0)
return;
if (y < 0 || y > 127 || x < -8 || x > 16 || z < -8 || z > 16)
return;
if (x >= 0 && x < 8 && z >= 0 && z < 8)
light[x, y, z] = lightLevel;

recursiveLight(x + 1, y, z, lightLevel);

recursiveLight(x - 1, y, z, lightLevel);
recursiveLight(x, y + 1, z, lightLevel);
recursiveLight(x, y - 1, z, lightLevel);
recursiveLight(x, y, z + 1, lightLevel);
recursiveLight(x, y, z - 1, lightLevel);
}

Answer



Someone said to answer your own question if you figured it out, so yeah. Figured out a method.


What I am doing is this: First, create a 3d boolean array of "already changed blocks" overlayed over the chunk. Then, fill in sunlight, torchlight, etc (just lighting up the block that its on, no flood filling yet). If you changed something, hit the "changed blocks" in that location to true. Also go though and change every solid block (and therefore no need to calculate lighting for) to "already changed".


Now for the heavy stuff: Go though the entire chunk with 16 passes (for each light level), and if its 'already changed' just continue. Then get the light level for the blocks around itself. Get the highest light level of them. If that light level equals the current passes' light level, set the block you are on to the current level, and set "already changed" for that location to true. Continue.



I know its kind of complicated, I tried to explain my best. But the important fact is, it works and is fast.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...