godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.24k stars 21.04k forks source link

Ambient light not included in backlight material #79859

Closed WrobotGames closed 11 months ago

WrobotGames commented 1 year ago

Godot version

v4.1.stable.official [970459615]

System information

Godot v4.1.stable - Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1060 6GB (NVIDIA; 31.0.15.3667) - Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz (8 Threads)

Issue description

When using backlight/translucency ambient light (the background/hdri) is not used for the backlight effect. This results in, for example foliage materials looking really 'harsh' in Godot.

The ambient light is included by default in the following software: blender cycles, blender eevee, unity hdrp and unreal engine. (This has been tested) Ambient light being included is basically the expected behavior, and that's why this is an issue that this isn't there is Godot.

Example: plane against the sky, the reproduction project.

A white plane with 100% backlight against the sky. Godot 4: afbeelding Blender Eevee: afbeelding

Example: foliage (no download due to copyright)

In materials where the backlight is used, for example foliage, the lighting will often look too 'harsh' when the ambient light isnt used in the backlight. Godot 4 grass with 100% backlight: afbeelding Blender Eevee grass with 100% backlight: afbeelding The lighting in Eevee looks notably more soft, and a bit brighter. This is the expected outcome with a 100% backlight. Its not possible to get this result in Godot without writing your own light shader. (Which is really complicated and should be the case here)

Steps to reproduce

  1. Create a plane.
  2. disable the sun, so only the sky light is used.
  3. observe how enabling backlight on the plane does nothing.

Do the same thing in other software and there will be a difference by enabling it. (Tested in: Cycles, Eevee, Unity, Unreal)

Minimal reproduction project

Backlight_Godot4.zip Backlight_Blender.zip

n0tank3sh commented 1 year ago

Suppose I am getting this right. Do we need to bake the ambient light emission from the ground affected by directional light? I may be wrong here.

clayjohn commented 1 year ago

I guess this is more of an "enhancement" suggestion. Right now "backlight" is a setting that only applies to direct lights in Godot, it doesn't add anything to indirect lights/GI.

For direct lights we add an extra term ((1/PI) - BRDF) * backlight. So I tried adding a comparable term to the ambient sky light

diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index 8a7008492e..28e989b3c2 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -1153,6 +1153,9 @@ void fragment_shader(in SceneData scene_data) {
                        vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
 #endif //USE_RADIANCE_CUBEMAP_ARRAY
                        cubemap_ambient *= scene_data.IBL_exposure_normalization;
+#if defined(LIGHT_BACKLIGHT_USED)
+                       cubemap_ambient += (vec3(1.0 / M_PI) - cubemap_ambient) * backlight;
+#endif
                        ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
                }
        }

It certainly softens the lighting the way that backlight does, but I'm not sure if it is the correct approach. Can you test with the above patch and let me know if the result matches your expectations?

WrobotGames commented 1 year ago

This patch seems to be a solution. It is not perfect, but it is good enough of a solution. (so yes, the results match my expectations) As can be seen from this image, the patch solves the main problem. afbeelding

Nitpicking and comparison

afbeelding (Note: unreal uses a different HDRI and I don't know how to disable the specular reflection in Unity HDRP) There are still some differences between the pieces of software, but those seem to be coming from the way the mesh is lit by the HDRI, rather then how the backlight is calculated. Example: The underside of the sphere isn't black in Godot, and the sides are not that blue, this can be seen in the backlight. afbeelding

Foliage

The patch had really improved the look of the grass. Without patch: afbeelding With patch: afbeelding

Issues

Some issues I've encountered:

clayjohn commented 1 year ago

Thanks for checking! Your analysis shows that the lines of code I posted are definitely not correct. So more investigation will be needed to figure out what a proper backlight formula for sky lighting looks like

WrobotGames commented 1 year ago

All the translucency in blender seems to do is to sample the diffuse from the inverted normal, multiplying that by the backlight color and then adding it to the rest of the ambient light. When only plugging in the translucency node into the output, blender will give inverted shading: afbeelding

This is what that would look like in the godot shader. I've duplicated the cubemap samplers and renamed them to cubemapambient2. Yes this code is a bit messy and unoptimized. (/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forwardclustered.glsl:1149)

    if (scene_data.use_ambient_light) {
        ambient_light = scene_data.ambient_light_color_energy.rgb;

        if (scene_data.use_ambient_cubemap) {
            vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
            vec3 ambient_dir2 = scene_data.radiance_inverse_xform * normal * vec3(-1.0, -1.0, -1.0);
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
            vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
            vec3 cubemap_ambient2 = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir2, MAX_ROUGHNESS_LOD)).rgb;
#else
            vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
            vec3 cubemap_ambient2 = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir2, MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
            cubemap_ambient *= scene_data.IBL_exposure_normalization;
            cubemap_ambient2 *= scene_data.IBL_exposure_normalization;
#if defined(LIGHT_BACKLIGHT_USED)

            cubemap_ambient += cubemap_ambient2 * backlight;
#endif
            ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
        }
    }

This code gives the right results in what seems to be the right way. Example: Sphere with (and without) 100% red backlight, only lit by HDRI. afbeelding

The backlight effect goes away when enabling sdfgi, because it does not use this bit of code. I do not know (yet) how to make that work. For proper results the backlight should be implemented everywhere. (reflectionsprobes, voxelgi etc)

WrobotGames commented 11 months ago

I am closing this issue, because the more I look into it, the more I realize a potential fix is a lot more complicated. I feel like this discussion should be moved to the Godot proposals repo.

For example: Bevy introduced backlighting in its 0.12 update. https://bevyengine.org/news/bevy-0-12/

Diffuse transmission is implemented via a second, reversed and displaced fully-diffuse Lambertian lobe, which is added to the existing PBR lighting calculations. This is a simple and relatively cheap approximation, but works reasonably well.

This sounds fairly simple, but when I tried the demo it seemed like there are some changes made to the lighting of the non backlit side, when changing the backlighting intensity. Some kind of correction for the added lighting. (Energy Conservation.)

Also, the backlighting currently implemented doesn't seem to handle normals correctly, with the sides of a sphere being lit even though the light is at an 90 degree angle. (This behavior does not occur in other game engines / in blender)