Skip to content

Fix LightmapGI shadow leaks #107254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

passivestar
Copy link
Contributor

@passivestar passivestar commented Jun 7, 2025

Fixes shadow leaks

Before After
before after

Test project: shadowleaks.zip

Lightmapper already does unocclusion pass to hide dark semi-occluded texels (inspired by bakery approach), but all of that work becomes undone when you enable denoiser. Denoiser is unaware of what's occluded and what isn't so it samples from occluded black texels which results in shadow leaks. This PR solves the issue by using unocclusion pass as a mask to prevent denoiser from sampling occluded texels.

Lightmap Occlusion mask Blender Cycles for comparison
lightmap2 mask cycles

Note

Under some conditions it's easy to mistake shadowleaks for AO. However unlike AO this doesn't happen in all crevices, only in places where geometry intersects. It's also resolution-dependent, angle-independent, and can leak asymmetrically on 2 adjacent surfaces. If you're interested in additional baked AO for creative purposes (and which you have control over) I opened a proposal for that last year

Note

OIDN has this issue too, though to a lesser extent (leaks are much thinner). It's third-party though so not much can be done. I recommend using JNLM, it gives great results

⚠️ Important information for testing:

Keep in mind that your geometry needs to have backface culling enabled for godot lightmapper to work properly. While this is important for the fix in this PR to work properly, this isn't specific only to this PR. If you have double-sided materials godot won't be able to do even the basic 1-texel unocclusion that it already does.

Here's comparison of one-sided vs double-sided material in the current master branch with denoiser disabled. Notice that there are both light leaks and shadow leaks here caused by light reaching surfaces that it shouldn't and unocclusion algorithm not being able to do its job properly:

culling

If you're importing textured meshes from Blender you can make sure backface culling is enabled in the material properties:

culling2

@passivestar passivestar requested a review from a team as a code owner June 7, 2025 10:43
@AThousandShips AThousandShips added this to the 4.5 milestone Jun 7, 2025
@Chubercik
Copy link
Contributor

Chubercik commented Jun 7, 2025

Side-by-side comparison to see the difference better:

https://imgsli.com/Mzg2NDA5

image

@passivestar
Copy link
Contributor Author

passivestar commented Jun 7, 2025

I'm not sure why that CI check failed when it ran fine here: https://github.com/passivestar/godot/actions/runs/15506456707/job/43661953613

edit: all good 👍

@jcostello
Copy link
Contributor

I thought that was intentional baked ambient occlusion haha

@H3XAGON3ST
Copy link

It works as expected. This PR does a good job of removing leaks, which has a very positive impact on the overall visual quality.

SSAO: Off
SSIL: Off

Before

image
image

After

image
image

However, for some reason, this PR caused artifacts at the meshes junctions(?), which are most noticeable when the glow is turned on.
(I apologize if I didn't make myself clear. I'm not good at English.)
image
image

@passivestar
Copy link
Contributor Author

However, for some reason, this PR caused artifacts at the meshes junctions(?), which are most noticeable when the glow is turned on

It's a bit hard to make out what's going on from those screenshots, any chance you could share a small reproduction project for that problem so I could take a look?

@H3XAGON3ST
Copy link

H3XAGON3ST commented Jun 8, 2025

Sure, here's a reproduction project:
shadow-unleak-bug.zip.
I managed to reproduce this in a new project. It looks like a large number of NaN values somehow appear in the lightmap.

image

@passivestar
Copy link
Contributor Author

@H3XAGON3ST if I rebake the lightmap in your project the artifacts go away, so this appears to be a hardware-related issue. I'll see what I can find, thanks for testing

image

@akien-mga
Copy link
Member

See also #107179 (comment) which likewise has NaN only on the reporter's system.

@H3XAGON3ST
Copy link

H3XAGON3ST commented Jun 8, 2025

That's weird. I did some more testing to understand what this error might be and noticed that the artifacts are affected by the type of compression of the EXR file. Choosing "Basic universal" completely fixes the artifacts. 4.5 dev 5 works fine without artifacts.
There were probably already issues before this PR, it just made them more visible.🫠

VRAM Compressed (Default)

image

VRAM Uncompressed

image

Basis Universal

image

@H3XAGON3ST
Copy link

@passivestar can you check the different texture compression modes? I noticed that in your test project, by default, the lightmap texture uses the BPTC_RGBFU format. This is the only format where I don't have artifacts. When the compression mode is changed to VRAM Uncompressed, the texture starts using the RGBHalf format, and artifacts appear even without rebaking the lightmap.
image

@passivestar
Copy link
Contributor Author

Can reproduce the problem with VRAM Uncompressed

@BlueCube3310
Copy link
Contributor

The lightmap textures now have transparent spots with the color values set to NAN, which causes these artifacts.

@passivestar
Copy link
Contributor Author

passivestar commented Jun 8, 2025

Lightmap before/after division by zero fix, transparent pixels are gone:

tp

@H3XAGON3ST Let me know if it works for you now

@H3XAGON3ST
Copy link

Yes, there are no more artifacts after the last changes👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants