Skip to content
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

Meshes with dynamic lighting lose ambient lighting on edges of LightmapGI bounds #88101

Open
atirut-w opened this issue Feb 8, 2024 · 10 comments

Comments

@atirut-w
Copy link
Contributor

atirut-w commented Feb 8, 2024

Tested versions

  • Reproducible in v4.2.1.stable.official [b09f793f5], v4.1.3.stable.official [f06b6836a], and v4.0.4.stable.official [fc0b241c9]

System information

Godot v4.2.1.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated AMD Radeon RX 6700 XT (Advanced Micro Devices, Inc.; 27.20.21034.37) - AMD Ryzen 7 5700X 8-Core Processor (16 Threads)

Issue description

When a dynamically lit mesh moves into the bounds of a LightmapGI, it completely lose ambient lighting (turns black) around the edge and returns to normal after completely going into or out of the edges.

2024-02-08.20-45-04.mp4

Steps to reproduce

  1. Set up a scene with lightmap GI
  2. Create a mesh with dynamic GI
  3. Move it in and out of the LightmapGI's bounds as indicated by the generated lightmap probes

Minimal reproduction project (MRP)

Lightmap.zip

@Calinou
Copy link
Member

Calinou commented Feb 8, 2024

My guess is that the time-based interpolation starts with purely black samples, so it gradually fades from black when a dynamic object enters the lightmap's bounds. It should probably initialize itself with the first sample found. You can confirm this theory by increasing the Rendering > Lightmapping > Probe Capture > Update Speed setting to a higher value.

In the meantime, you can add LightmapProbe nodes in each corner of the level so that dynamic object lighting extends beyond the static meshes. Automatic probe generation will adapt itself to add probes in these areas if they're large enough.

@atirut-w
Copy link
Contributor Author

atirut-w commented Feb 8, 2024

You can confirm this theory by increasing the Rendering > Lightmapping > Probe Capture > Update Speed setting to a higher value.

The "black area" is actually very defined, and moving the object in that area does not fade it to lighter ambient colors.

@patwork
Copy link
Contributor

patwork commented Mar 6, 2024

Hi, I have created a small project containing several test scenes to verify the correct generation and display of LightmapGI and LightmapProbes.

Godot LightmapGI Tests

The scene that tests dynamic objects is located in Test 02 scene.

In addition to the issue of objects going outside the Probes area, there is another one related to the fact that dynamic objects are illuminated entirely with only one Probe, even if their size causes their parts to be in differently illuminated locations.

@atirut-w
Copy link
Contributor Author

atirut-w commented Mar 6, 2024

cts are illuminated entirely with only one Probe, even if their size causes their parts to be in differently illuminated locations.

How do other engines behave in similar conditions? I think sampling from multiple probes could impact performance considerably. We might need to also consider probe density if we want to have that as an option in the engine.

@patwork
Copy link
Contributor

patwork commented Mar 6, 2024

OK, so to test this I installed Unity (v2018 because the later ones refuse to log into the license server for some reason) and I have to admit that I am now very disappointed that lighting dynamic objects works in a similar way. Regardless of the size, the moment the center of the object comes within range of the Lightprobes, the whole object starts receiving additional lighting from them.

In the example below, all objects except the cylinder are marked as static. The light "sun" and blue light in the box also only affect the lightmaps. One lightprobe placed in the box receives blue light directly.

unity-probes

When the cylinder is lowered so that it comes "within range" of the blue lightprobe, it turns blue.

unity-probes2

unity-probes3

unity-probes4

On the plus side for Unity, it should be noted that the change of light when entering the range of lightprobes is smooth and there are no sudden changes as in Godot currently.

Unity also tried to fix the problem by introducing Light Probe Proxy Volume, where a selected dynamic object with such a component added has the ability to interpolate lighting between lightprobes.

https://docs.unity3d.com/Manual/class-LightProbeProxyVolume.html

@Calinou
Copy link
Member

Calinou commented Mar 6, 2024

In addition to the issue of objects going outside the Probes area, there is another one related to the fact that dynamic objects are illuminated entirely with only one Probe, even if their size causes their parts to be in differently illuminated locations.

If you want a dynamic object to receive lighting from multiple lightmap probes, you have to split it into several smaller meshes.

If you need more precise indirect lighting on dynamic objects, you could use LightmapGI and VoxelGI/SDFGI at the same time, with dynamic objects having the Disabled bake mode.

Either way, this is proposal territory 🙂

Edit: Volumetric lightmaps can be an alternative too: https://twitter.com/Guerro323/status/1774573177106059337

@permelin
Copy link
Contributor

permelin commented Mar 7, 2024

Dynamic objects losing ambient light at the edge is caused by the fact that the object is assigned to the lightmap the moment its AABB intersects with the AABB of the lightmap, but then when the renderer goes to find the appropriate tetrahedron of probes to use, it bails out early if the center of the object is not inside the lightmap.

The area between where the edge and the center of the object intersects with the lightmap is a no man's land.

Vector3 center = p_instance->transform.xform(p_instance->aabb.get_center()); //use aabb center
Vector3 lm_pos = to_bounds.xform(center);
AABB bounds = RSG::light_storage->lightmap_get_aabb(lightmap->base);
if (!bounds.has_point(lm_pos)) {
continue; //not in this lightmap
}

@patwork
Copy link
Contributor

patwork commented May 4, 2024

@permelin given your experience with lightprobes (#90701 #90702), will you also try to address this problem?

@Calinou
Copy link
Member

Calinou commented May 7, 2024

It seems the fix would be fairly simple: instead of checking just one point in the bounds, check for all points from the AABB and only continue; if none of them is within the bounds.

@permelin
Copy link
Contributor

permelin commented May 7, 2024

@permelin given your experience with lightprobes (#90701 #90702), will you also try to address this problem?

I hadn't planned to do so.

It seems the fix would be fairly simple: instead of checking just one point in the bounds, check for all points from the AABB and only continue; if none of them is within the bounds.

I think you could just remove this check. IIRC, this code path will not be hit unless the lightmap has been assigned to the object, and that will only happen if the lightmap and object already intersect. Quitting early is redundant.

The real problem is the blend factor a bit further down. If the center of the instance AABB is not inside the lightmap, the blend will be zero, which means no light anyway. So that algorithm must be changed.

Again, if I remember correctly, the blend is actually there to handle overlapping lightmaps. It's possible that a solution would simply be to check if there is only a single lightmap assigned to the object and if so, set the blend to 1. (Though a perfect solution would blend at the bounds with whatever ambient/indirect light is outside the lightmap for a seamless transition.)

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

No branches or pull requests

4 participants