godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

New inbuilt function, Sample Directional Shadows in Vanilla Godot Light Function #8872

Open EnlightenedOne opened 8 months ago

EnlightenedOne commented 8 months ago

Describe the project you are working on

I am working on a tutorial sample game that serves as both a collation of all my shaders and also a testament to what is feasible with Vanilla Godot. My goal is to run it on a Steam Deck.

I really want to stress that my goal is to not recompile the engine as part of this challenge. No rule against merging fixes into vanilla along the way. The question is what can be done by the hobbyist who doesn't want to drift from the core engine? I think Godot is fantastic so far but the inbuilt lighting (whilst pretty) is inflexible and without recompiling Godot stylisation is limited.

Describe the problem or limitation you are having in your project

Here is a screenshot showing a flat water surface with vertex displacement only from the reflection and the specular channel of the shadow to colour the shadow in red on the water surface (just to highlight it, all vanilla Godot 4.2): image

Now compare the shadow to the planar reflection with fragment noise added (diffuse_lambert, specular_schlick_ggx): image

Note the significant improvement breaking up the edges of shapes in the reflection texture brings. What I want is to apply the similar treatment to the shadow map in realtime.

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

I want to displace the sampling of the Directional Light's PSSM Shadow texture in the spatial light function to break up shadows and create a parallax effect on the surface of water.

Essentially this link details a proof of how I would approach the issue https://medium.com/@ShaderError/godot-custom-shader-built-ins-functions-part-2-3-4a1772c12dfe: image

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

A minimum viable product for this is to add a light id passthrough and move the directional shadow logic into a reusable function that can be called from the light fragment as a built in method as specified in the custom shader guide. The link here described the proposed changes necessary to the engine https://medium.com/@ShaderError/godot-custom-shader-built-ins-functions-part-2-3-4a1772c12dfe if there is any interest I am happy to roll a PR for this. I appreciate the lighting is strict for a reason, lightest touch, smallest overhead is the goal.

I think long term exposing more lighting functions so they are reachable from the spatial light function greatly enhances the core offering of Godot and enables easier stylisation.

Once devs can access shadow data from the light fragment they can:

  1. Displace shadows without altering depth information
  2. Identify shadow attenuation vs attenuation (https://github.com/godotengine/godot-proposals/issues/2905 this proposal aims to expose more light data to the lighting function. It is lighter touch, wont allow sampling but makes attenuation better)
  3. Hypothetically some form of parallax on the PSSM should be feasible although without caching it as a texture this may be impratically slow, something like this would be ideal https://godotshaders.com/shader/parallax-occlusion-mapping-with-self-shadowing (probably better served by writing to depth in a shadow pass instead).

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

No workaround exists.

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

Adding inbuilt functions to sample and reuse stages of glsl pipeline is core. It might potentially be useful as a starting point for moving the inbuilt light calculations into a more reusable space and allowing users to tap into the built in light functions.

EnlightenedOne commented 2 months ago

Here is a side by side comparison modifying a Light shader with the shadows sampling code exposed:

if (LIGHT_IS_DIRECTIONAL) { attenuation = ATTENUATION; } ShadowAttenuationExisting

if (LIGHT_IS_DIRECTIONAL) { attenuation = sample_directional_shadow(LIGHT_INDEX, verty + vec3(normal_uv.x, 0.0, normal_uv.y)); } ShadowNormalDisplacement

I am going to raise a PR on this if only to get a feel for the feasibility of having the changes merged. The LIGHT_INDEX is exposed conditionally on all compute_light flows for the light shader and is cheap and cheerful to add with no cost if not used. The biggest impact is a fairly sensible reshuffle of the scene_forward_lights_inc.glsl so its easier to tap into stuff in the light shader.

Calinou commented 2 months ago

I'm not sure why the specular of the directional light is visible through the shadow on your example (with the default shader). Logically, it should not be visible. Does it happen if the water material is fully opaque?

EnlightenedOne commented 2 months ago

It is the Light's Shadow Opacity, if its reduced at all from 1.00 then you get specular leaking in.

EnlightenedOne commented 2 months ago

Here is a still with the shadow opacity at 1, this clears specular but in many lighting conditions the shadows can be harsh: image

For reference in the gif above it is set at 0.75 opacity.