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

Add light volume nodes as a fast way to render global illumination #11448

Open
Arazalera opened this issue Dec 30, 2024 · 15 comments
Open

Add light volume nodes as a fast way to render global illumination #11448

Arazalera opened this issue Dec 30, 2024 · 15 comments

Comments

@Arazalera
Copy link

Arazalera commented Dec 30, 2024

Describe the project you are working on

Projects involving procedural buildings and open world, focused on realism

Describe the problem or limitation you are having in your project

I've noticed that various Godot games often look subar is because the lighting is too flat, because global illumination is not used
But GI in godot seems to be difficult

  • LightmapGI is physically accurate, but requires UV2, and can only work on one mesh, and builtin UV2 unwrap only works on 1 mesh, can't really be used on moving objects
  • VoxelGI seems to be embarrassingly useless as light phases through everything
  • SDFGI looks good, but can have artifacts, expensive, and cannot work for moving objects

The first 2 in total amount to nothing, and 3 seems unnecessary

I was inspired by a trip to Wellington, and with overcast weather, and frequency of shopfront roofs, I've noticed how the light gradually gets darker as it goes deeper into the building

Image
141 Manners St, Wellington, Google Earth

While it can be expected of raytracing, but it visually resembles a gradient, so it's far easier to make a 3D gradient of sorts that modifies the lighting in it

It can also be noted in other games like DayZ, where no form of GI is used, and interiors look unusually bright and dull

Image
Image
by TopeREC on youtube

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

Light volumes, being volumes that modify meshes' ambience/radiance and reflection panorama, color, and intensity should be added

A light volume would be one or multiple SDF-like shapes that have an intensity from 0 to 1 at a point and a color of the shape, and the ambient lighting of a mesh would be mixed with it's color, and would have an order in which volumes are applied

ambient = lerp(ambient, volume.ambient, sdf_intensity)

It could also be able to modify the ambient and reflection panorama for more precision

While less physically accurate, It would have an advantage over SDFGI by being easy to use for buildings in an open world, destructible buildings, and moving objects (vehicles for example), and having fast performance, physical accuracy doesn't really affect how realistic it looks, it's more important to just have global illumination without artifacts

It would also be resistant to artifacts, and a volume could also ignore directional lights, avoiding shadow artifacts and light leaking, and is really reliable way to making something look interior than directional shadows

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

A LightVolume would be a Node3D to do that, it's properties would be:

shape : the shape of the volume of a LightVolumeShape class

color : the color by which the ambient and reflective lighting would be multiplied by

intensity : the brightness of the volume multiplies color by intensity

order : the order in which the volume is applied, volumes of a lower node index are applied first, and volumes of a lower order are applied first
the calculated color is calculated by
ambient = lerp(ambient, volume.ambient, sdf_intensity)

The LightVolumeShape class is inherited by different shapes:

LightVolumeShapeBox : an SDF box

LightVolumeShapeSphere : an SDF sphere

LightVolumeShapeCapsule : an SDF capsule

LightVolumeShapeCylinder : an SDF cylinder

LightVolumeShapePolygon : an extruded 2D polygon, also has an option to only calculate intensity in 2D

There could also be a Mesh shape

All shapes also have an inflate parameter, which adds or subtracts to the distance, a bevel parameter, corresponding to SDF bevel, and a bevel_scale parameter, which also scales the distance by a vector

Volumes are sampled per-probe (LightVolumeProbe), per-vertex, or per-pixel

LightVolumeProbe is a Node3D that samples the GI at it's origin and applies it to all child meshes

For example ambient lighting has a color of (1, 1, 1), then we add a box volume with color (0, 0.5, 0), this would be the volume's intensity

Image

And this is the total color

Image

Then a second volume is added, with this intensity

Image

The second volume has a higher node index and is applied after the first volume

Image

Also note that the intensity of volumes is passed through a smoothing function like builtin smoothstep(0, 1, x)

Image
The volume intensity is also smoothed

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

No, it requires modifying built-in lighting

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

It likely needs to be core, as it requires modifying built-in lighting

@Calinou
Copy link
Member

Calinou commented Jan 2, 2025

This already exists as ReflectionProbe 🙂

Reflection probes can have a custom ambient color defined, and they will blend together if they overlap. You can make them only affect the ambient color without affecting reflections by setting their Intensity property to 0 too. This effectively makes them act like some kind of area light. godotengine/godot#99958 adds more control over ReflectionProbe blending.

If you want something that looks closer to an actual light source, you'll want a dedicated area light implementation instead, which is being tracked in #11450.

@Arazalera
Copy link
Author

Arazalera commented Jan 2, 2025

Those are not sufficient,

  • a ReflectionProbe can only be a box,
  • fading distance cannot be controlled,
  • A reflection probe should be modifying reflections, not ambient lighting,
  • it is unnecessary to have a reflection Cubemap everywhere, in every probe
  • If you set interior to true, it gets completely hard edges
  • Blending mode and order between reflectionprobes cannot be modified

@Calinou
Copy link
Member

Calinou commented Jan 2, 2025

A reflection probe should be modifying reflections, not ambient lighting,

The term "reflection probe" is kind of outdated to be fair. At this point, it's a global illumination solution, just one where ambient lighting is set globally across the entire probe. This is why the documentation covers them in the global illumination section.

  • fading distance cannot be controlled,

godotengine/godot#99958 adds a fade distance property.

  • If you set interior to true, it gets completely hard edges

godotengine/godot#100241 fixes this.

  • it is unnecessary to have a reflection Cubemap everywhere, in every probe

We can probably skip the reflection rendering if Intensity is set to 0 and the ambient light is set to a custom color. (It's still needed if using environment color, as this sets ambient light to the average color of the reflection.)

Blending mode and order between reflectionprobes cannot be modified

godotengine/godot#100241 intelligently sets priority according to probes' size and positioning relative to each other. It should work as you expect automatically.

@Calinou Calinou changed the title Light volumes, a fast way for global illumination, particularly in open world, and vehicles Add light volume nodes as a fast way to render global illumination Jan 2, 2025
@Arazalera
Copy link
Author

Arazalera commented Jan 2, 2025

It still doesn't provide control over the shape of the volume, in this proposal the volume can
It might be easier to understand this with a practical example

For example, some kind of building

Image

Then we make a dark box volume without bevel/radius

Image

The volume doesn't leak outside, but it also looks bad in doors and windows as it changes color immediately

Image

So white sphere volumes are then placed at the doors and windows

Note that blending mode and order are very important here, as the white spheres must replace the color of the dark box volume and must be applied after the box volume

The dark volume is gradually replaced by white, but the environment is not affected because it is already white

Image

To close the door, a sphere volume is very simply disabled

@lander-vr
Copy link

lander-vr commented Jan 3, 2025

The proposed feature doesn't really make sense to me. Unless I'm misunderstanding, the behavior you're describing sounds exactly like what ReflectionProbes offer:

"...volumes that modify meshes' ambience/radiance and reflection panorama, color, and intensity should be added"

I think there would be more value in exploring how we can improve how reflection probes GI contributions are calculated and applied, so it's not working with a single averaged color but with its own low-res buffer (could probably be derived from the reflection buffer), applying ambient lighting in a more spatially aware way. This could be interesting to think about keeping #3013 in mind.

I do agree having only an AABB shape for reflection probes isn't ideal, and having a sphere reflection probe has several benefits (Like even distortion). Having a custom mesh shape seems very unrealistic since the calculations necessary to determine whether or not something is within their volume are very complex/expensive.

Your example of placing sphere volumes in doorways would still result in severe leakage around the doorways on surfaces where you wouldn't want light. You would likely get better results by faking the door ambient lighting with a spotlight, and adjusting its strength based on whether the door is open or closed.

@Arazalera
Copy link
Author

Also, ReflectionProbes could be modified and instead a reflectionprobe is placed in a volume or a number of volumes, and the reflection map is applied to objects in the volume

You would likely get better results by faking the door ambient lighting with a spotlight

I proposed this specifically to avoid dirty hacks and inconsistent lighting, so it should be avoided

Your example of placing sphere volumes in doorways would still result in severe leakage around the doorways on surfaces where you wouldn't want light

A volume could instead have multiple shape nodes with boolean operators, so the sphere shapes are be limited to the inside of box shapes, removing any leaking

I think there would be more value in exploring how we can improve how reflection probes GI contributions are calculated and applied, so it's not working with a single averaged color but with its own low-res buffer

A Reflection probe could also calculate a radiance map then

@WickedInsignia
Copy link

WickedInsignia commented Jan 6, 2025

Agreeing with others here that reflection probes (especially with some upcoming PRs that fix a few of the aforementioned issues) mostly fulfill this use-case. Any additional features from this technique don't seem granular enough to make a meaningful quality difference. I believe the result would be far too generalized, and as Lander mentioned it might be better to increase the quality of reflection probes than introduce a new technique I haven't seen in action anywhere else.

It's also very user-intensive. A system like Unity's Adaptive Light Probe Volumes would make much better use of artists' time and achieve overall better quality, while still being highly efficient.
Modern GI techniques are moving towards plug-and-play automated approaches, I'm not sure that the time spent to develop a heavily manual GI would be worth the trouble.

Regarding VoxelGI: I've had a lot of luck leaving the sudiv level fairly low for any given area and setting the Normal bias anywhere between 0.6 - 0.9.
The former ensures the GI is more holistic, since greater subdiv levels can reduce the reach and intensity of bounced light. The latter greatly reduces the leaking around corners in my scenes, along with reducing the overly "soft" look.
For me it's been the more reliable GI solution in my tests. The main issue I encounter is the one described HERE, and it's supposedly performance-hungry but I haven't seen anything comprehensive on this yet.

Image

@Arazalera
Copy link
Author

reflection probes (especially with some upcoming PRs that fix a few of the aforementioned issues) mostly fulfill this use-case. Any additional features from this technique don't seem granular enough to make a meaningful quality difference. I believe the result would be far too generalized

Not really

A very advantageous use case is open world, since it can work easily with terrain, and it works easily with procedural buildings, tunnels, and moving vehicles

I have made a mockup in Godot using global shader variables to allow for 4 volumes at once, and also volumes have a direction

Image
Image
Image
Image
Image
Image
Image
Image
Image

Light Volume Test2.zip

@WickedInsignia
Copy link

It certainly fulfills the goal of being a highly generalized and mostly artifact-free GI solution. Just having some sort of gradual light transition over surfaces can be an improvement over flat ambient light.

My concerns stem from these points:

  • There are other GI solutions that greatly increase quality/ease of use for a little less performance that may be more worth the maintainability long-term. Also as hardware capabilities increase a technique this inaccurate but efficient is becoming less and less relevant for most use-cases.
  • It depends very heavily on screen-space effects to fill the gaps, negating the performance gains to a certain degree.
  • It's not very useful to the core purpose of a dynamic GI solution if it isn't sampling the environment. If a user is defining the main properties such as colour and position, it doesn't adapt very well to changing conditions. How relevant is a volume for a window or doorway if the building is no longer there? You could change the values as conditions change (such as day to night) but that is a lot of work that has the potential to multiply exponentially with the complexity of a scene.
  • It needs a comprehensive UI solution to be usable and to avoid clutter, and even then would be a total nightmare to manage. Dealing with Reflection Probe bounds is difficult enough in low numbers.

Ultimately it's a neat idea but I'm not sure it's one that we should be too hooked-up on. There are many potential ways to handle GI but they're not all made equal, and I err towards proven solutions rather than reinventing the wheel (which turns into many headaches when one realizes that what looked good on paper is a travesty in practice). This technique in practice would require very intensive user involvement for very little gain in dynamicity and accuracy.

@Arazalera
Copy link
Author

There are other GI solutions that greatly increase quality/ease of use for a little less performance that may be more worth the maintainability long-term. Also as hardware capabilities increase a technique this inaccurate but efficient is becoming less and less relevant for most use-cases.

In godot, none really work, VoxelGI leaks light a lot, LightmapGI requires UV2, and built in UV2 unwrap can only be used on 1 mesh, and also they don't work on moving nodes

This could be very useful in something like a train simulator, for example:

  • Proper indirect lighting in train cart interiors, removing that look like in the DayZ screenshots where everything is glowing a solid color with no gradient
  • indirect lighting in buildings placed in an open world, and indirect lighting also affects terrain, and darkened areas between buildings
  • Some shading around train carts onto terrain
  • indirect lighting in tunnels, where it's really long, but doesn't need to be detailed
  • Shading around and under bridges and overpasses
  • Shading on train station canopy

It's not very useful to the core purpose of a dynamic GI solution if it isn't sampling the environment. If a user is defining the main properties such as colour and position, it doesn't adapt very well to changing conditions. How relevant is a volume for a window or doorway if the building is no longer there? You could change the values as conditions change (such as day to night) but that is a lot of work that has the potential to multiply exponentially with the complexity of a scene.

If a building is removed, all it's volumes are also removed, and day night cycles could be improved with more blending modes (like multiplying global ambient lighting, or replacing ambient lighting with a multiplier of global ambient lighting)

It needs a comprehensive UI solution to be usable and to avoid clutter, and even then would be a total nightmare to manage. Dealing with Reflection Probe bounds is difficult enough in low numbers.

It would also be suitable for procedural generation anyways, and better gizmos, snapping, and seethrough can be made, and it isn't difficult to use

@WickedInsignia
Copy link

In godot, none really work, VoxelGI leaks light a lot, LightmapGI requires UV2, and built in UV2 unwrap can only be used on 1 mesh, and also they don't work on moving nodes

Ah sorry, I meant techniques that haven't been implemented in Godot but would be a better expenditure of effort/support. Manually placed irradiance probes (with some automated features) have been used in many games since the last gen and would be performant, for example. Unity's Adaptive Probe Volumes are a variation of this.
Voxel GI can extract reasonable quality in plenty of circumstances and lightmapGI can unwrap entire scenes on import. We're not left totally without options, fortunately.

If a building is removed, all it's volumes are also removed, and day night cycles could be improved with more blending modes (like multiplying global ambient lighting, or replacing ambient lighting with a multiplier of global ambient lighting)

My point about the technique being manual-work-heavy relates to this. Many of the dynamic use-cases are negated by how much work it would take to account for them. The technique can account for this but would become an absolute pain at scale.

Iteration speed is also important. Lightmapping takes some initial setup but experimenting with lighting and geometry configurations after that are a breeze. This technique requires a lot of manual work to setup lighting, and then requires heavy reworks if geometry or conditions change.

I'm driving the point a bit, but just expressing that as neat as this looks on paper I have strong doubts it would be useful in practice.
"Why not just develop it anyway?" would be a fair question but features require some level of developer interest to be maintained.
It might be more productive to roll these features into the reflection probes instead, since they're currently seeing active improvements and are a proven/maintained solution.

@Calinou
Copy link
Member

Calinou commented Jan 14, 2025

and built in UV2 unwrap can only be used on 1 mesh

For most use cases, you are supposed to do it in the Import dock by setting the GI mode to Static Lightmaps. This handles the whole 3D scene at once (or mesh, since 4.4).

Using the UV2 unwrap option in the Mesh menu at the top of the 3D editor viewport is only meant to be done for prototyping purposes (remember that this UV2 can't be saved if you do it on an imported scene anyway). For primitive meshes, what it'll do is enable the Add UV2 property on the PrimitiveMesh resource instead.

@Arazalera
Copy link
Author

Arazalera commented Jan 15, 2025

The light leaking of VoxelGI is noted in godotengine/godot#80123

For most use cases, you are supposed to do it in the Import dock by setting the GI mode to Static Lightmaps. This handles the whole 3D scene at once

it still requires the entire scene to be in 1 file

And it is useful for moving/dynamic nodes/vehicles and destructible buildings, and the customization capability is also important, as visual discrepancy and artifacts can be removed, which is important in moving/dynamic nodes

@Calinou
Copy link
Member

Calinou commented Jan 15, 2025

it still requires the entire scene to be in 1 file

You can import multiple 3D scenes in the same scene (and they can be nested as with any other Godot scene). Once you import a 3D scene, it's seen like any other scene by Godot.

If you have dynamic objects in your game, they are generally separate 3D scenes any way. In other words, you wouldn't be modeling a moving car within your level scene, but you'd save it to a separate scene file. Your level scene would instead only contain a marker that tells the car where to spawn.

@Arazalera
Copy link
Author

I think I had some kind of misconception that UV2 of meshes needed to be baked according to each other, and not that the fitting is done by LightmapGI, atleast 1 thing resolved

Although what is useful and can be done easily with volumes, are things like:

  • Very large forests (10km or more) using 64bit floating origin:

It can be done by having a heightmap for terrain, and a heightmap for tree height, a volume would shaden everything from the ground, to ground+tree height, it can be done by SDFGI, but it really is unnecessary

  • Very long tunnels - can be somewhat done by SDFGI, but it has a distance limit and after that it doesn't work, too much distance of identical light values for lightmapGI

  • Vehicles, SDFGI is rather deferred and cannot work on moving nodes, lightmapGI also cannot

  • Moving doors, lightmapGI cannot account for that

  • Destructible buildings, lightmapGI cannot account for that

  • In open world, objects are placed in runtime, and other GI methods can't really work for that

So it has quite a lot of applications

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