godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.14k stars 93 forks source link

Change the behavior of static lights in SDFGI to be similar to static lights in Lightmap GI #8098

Open Jamsers opened 1 year ago

Jamsers commented 1 year ago

Describe the project you are working on

A 3D game (demo) that uses SDFGI as its only global illumination solution.

Describe the problem or limitation you are having in your project

In an SDFGI lit game there isn't a cheap, low fidelity way to illuminate an area in the same way that you could with lightmaps: placing static lights that get completely baked into the lightmap, with no real time component.

Imagine a long row of torches in a corridor that you would want to look generally correct.

Obviously realtime lights with shadows for every single torch is a no go, in any game or engine.
Realtime lights with no shadows for every single torch is doable in deferred renderers, but would look flat.

But there's a very cheap, yet good looking solution for this in lightmapping - have a static, completely baked light source for every single torch. These lights cost nothing in terms of (realtime) performance, and look good and correct - static objects will be correctly shadowed by such lights, and these lights would have correct bounce lighting. Dynamic objects, while not casting shadows, will still look good alongside them due to lightmap probes.

This is a common technique used in lightmapped games, for things like fake bounce lights in buildings, or fake fill lights in caves, for example. The advantage of this technique is that while the light fidelity is coarse, the light still looks very correct and grounded. And there's practically no performance cost when the game is running, so you can put in as many fill lights as you'd need.

Currently, there seems to be not that much of a functional difference between dynamic lights and static lights in SDFGI, other than the fact that static lights' SDFGI contribution doesn't update when it's moved. And some general oddness.

We can change the behavior of static lights in SDFGI to fill in this functionality.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Change the behavior of static lights in SDFGI to be similar to static lights in Lightmap GI. Static lights in Lightmap GI practically only exist in the Lightmap data, and are applied to dynamic objects through the Lightmap probes. There is no light or shadows being processed in real time for these lights. This saves performance and provides a cheap way to illuminate an area when precise high fidelity lighting isn't needed, but physical correctness (i.e. big static structures should still generally occlude light) is still wanted. We can make the way static lights work with SDFGI similar to this.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

When the user sets a light's bake mode to static, the light will stop rendering directly and will only render in the SDFGI representation. Dynamic objects will no longer cast shadows from this light, but can still receive light and shadows through the SDFGI probes. The general fidelity of the light will become coarse and low quality due to being limited by the SDFGI fidelity itself - but this is desired.

How static lights currently work with SDFGI:

https://github.com/godotengine/godot-proposals/assets/39361911/32503981-5f68-4f62-8d7f-bc61c517a32a

Average FPS was 60.

A mockup of how SDFGI static lights would work under this proposal, using static emissive meshes:

https://github.com/godotengine/godot-proposals/assets/39361911/e899c920-8663-4ac8-9639-b28acfc58e82

Average FPS was 90.

"Why not just disable shadows for the lights to save performance then?"

Firstly, even without shadows direct lights still aren't as cheap as you'd think - remember we aren't in a deferred renderer. But, while this does get halfway to our goal (turning off shadows still keeps shadows in the SDFGI representation), the direct light is still rendered... without shadows.

Screenshot (71)

Compare this look to the mockup:

Screenshot (70)

So that approach doesn't give back as much performance as it could... while looking worse. No bueno.

If this enhancement will not be used often, can it be worked around with a few lines of script?

There isn't a workaround I've found that can mimic this functionality. The closest I've found was to create a mesh with the global illumination mode set to static, then give that mesh a highly emissive material. The way the light works is exactly how I imagine static lights would work in my proposal, but I haven't found a way to hide the mesh while keeping the light.

Is there a reason why this should be core and not an add-on in the asset library?

This concerns SDFGI and lights, core systems.

Calinou commented 1 year ago

Like VoxelGI, SDFGI only cares about indirect lighting, not direct light. Support for direct light rendering in SDFGI would need to be implemented first for this to be possible. There is a shader called sdfgi_direct_light_glsl but I assume it's only used by emissive materials, not light nodes.

Also, the SDFGI cell grid will usually be too coarse to accurately represent shadows for small details (such as torches or street lamps in your example). There are also issues with making SDFGI pop-in more visible with this approach, as it would handle both direct and indirect light.

Firstly, even without shadows direct lights still aren't as cheap as you'd think - remember we aren't in a deferred renderer.

Real-time lights without shadows are cheap with a Forward+ renderer – their cost depends on the screen area they fill. They become expensive when you enable shadows, or when using another rendering method which doesn't use clustering.

If performance is still an issue, remember you can disable shadows and lights at a distance using the Distance Fade properties. Depending on how your level is laid out (and how much fog it has), it's often possible to use distance fade properties with barely any pop-in.

Static lights in Lightmap GI practically only exist in the Lightmap data, and are applied to dynamic objects through the Lightmap probes. There is no light or shadows being processed in real time for these lights

This is actually not the case for dynamic objects.

Either way, this should be an additional bake mode if this is implemented, not a replacement for the existing Static bake mode which has many uses. For instance, you can still adjust the direct light at run-time, which is useful for flickering torches. Indirect lighting will not update in this case, but the difference would be barely visible.

The way the light works is exactly how I imagine static lights would work in my proposal, but I haven't found a way to hide the mesh while keeping the light.

We could perhaps have a way to have hidden emissive meshes that affect VoxelGI/SDFGI/LightmapGI, as this is useful for fake area lights of arbitary shape.