Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.7k stars 251 forks source link

Sunlight in the caves #622

Open ddel-rio opened 7 months ago

ddel-rio commented 7 months ago

Describe the bug Within a terrain surrounded by walls, sunlight can pass through them and cast strange shadows. This also occurs at the surface when the sun is "below the ground".

To reproduce In the MRP dig a cave and wait for dusk.

mrp.zip

Expected behavior The walls should prevent any external light source from entering.

Screenshots Captura de pantalla 2024-04-04 141741 Captura de pantalla 2024-04-04 141845

Environment

Zylann commented 7 months ago

When using shadowmapping with lights, this is a known side effect of how Godot implements them. Godot projects shadows when lights encounter front faces. So while the terrain surface will project shadows under overhangs or caves (when ground is still loaded), no shadows will be projected if the sun comes from below (only encountering backfaces) or when there is no surface anymore to project them in deep caves, or on the edges of unloaded chunks.

This is not a trivial issue to solve, and that might mean you have to do some compromises at the moment.

Some ideas:

Zylann commented 7 months ago

I pushed a branch shadow_occluders which adds an option to use the hack of the same name mentionned earlier (https://github.com/Zylann/godot_voxel/commit/0e6fafdfe643c0e65811b6536f2ccbf6285f8f4b).

It adds 6 extra properties to VoxelMesherBlocky that toggle these occluders for each desired chunk side. If the sun only looks down, the bottom occluder is not necessary. If the sun only looks angled towards the same side, then the opposite occluder is also not needed. If the sun is perfectly vertical, half of lateral occluders might not be needed either etc. Not bullet proof, edges of the map can still get lit in some cases as mentionned.

Not yet in the main branch because I'm not sure if it's useful enough (you/others have to test it to find out if it works well enough), and also because I'm waiting for Godot 4.2.2 to be released before adding new features to the main branch.

ddel-rio commented 7 months ago

Thank you very much! I've been busy with other things that have more priority in the game so I haven't delved into this topic. But I just tested the branch and it works quite well. In my case now the engine behaves exactly as I expected and I have not found any bugs, so I have nothing to object to.

JekSun97 commented 7 months ago

Thank you very much! I've been busy with other things that have more priority in the game so I haven't delved into this topic. But I just tested the branch and it works quite well. In my case now the engine behaves exactly as I expected and I have not found any bugs, so I have nothing to object to.

Could you send me a before and after screenshot?

ddel-rio commented 7 months ago

Could you send me a before and after screenshot?

Of course, I also made an mrp to test the new feature:

mrp.zip

Before

With the best configuration I found:

Captura de pantalla 2024-04-15 142422 By digging too deep, light leaks around the edges.

After

Using shadow occluders.

Captura de pantalla 2024-04-15 142500 Now the light edges have disappeared.

JekSun97 commented 7 months ago

@ddel-rio Thanks a lot)

Zylann commented 7 months ago

Regarding your answer, I thought of another idea that wouldn't require rendering shadow occluders, and might have the advantage of rendering a bit faster. However this is a workaround to do on your side, and since Godot is not exposing enough things, it is more work:

Instead of making everything double-sided, change light shadow settings. You can tell it to project shadows from back faces instead of front faces with shadow_reverse_cull_face. But that still has the issue that there will be leaks. A workaround to leaks would be to somehow detect in the shader when we are rendering a shadowmap, and move vertices slightly towards the sun to artificially create "thickness" between the actuall walls of the cave and what the shadow will "hit". Unfortunately, Godot doesn't expose anything (like a #define?) that would allow you to do this in your shader. The closest hack I could think of is to detect if the projection matrix is orthogonal. Also, Godot doesn't expose a uniform to get sun direction from within the vertex shader. You would have to set this yourself.

Haven't tested that, just an idea