Repository: godotengine/godot (language: cpp)

Issue:
SpotLight3D shadow has significant peter-panning with default settings (4.4-dev7 and 4.4-beta1)
### Tested versions

Reproducible in: 4.4-dev7 and 4.4-beta1
Not reproducible in: 4.4-dev6 and before

### System information

Godot v4.4.dev7 - Windows 11 (build 22631) - Single-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4070 (NVIDIA; 32.0.15.6603) - 13th Gen Intel(R) Core(TM) i7-13700K (24 threads)

### Issue description

Shadow casting SpotLight3D has noticeable peter-panning effect with default bias value. Example below:

![Image](https://github.com/user-attachments/assets/501f57ff-6c38-477d-aa8e-0e2e695ab5ba)

### Steps to reproduce

Create a new 3D scene
Add floor into the scene and add mesh on the floor
Add SpotLight3D and enable shadows
Point the spotlight towards mesh and observe shadow it casts

### Minimal reproduction project (MRP)

N/A


Discussion / hints:
Confirmed, bisecting
Bisected to https://github.com/godotengine/godot/pull/100319, CC @Flarkk

It doesn't seem like this was an intentional or expected change, can confirm this with a very normal setup, see MRP

[MRP.zip](https://github.com/user-attachments/files/18494408/MRP.zip)
What's the bias of the spotlight in the MRP ? 0.1 or 0.001 ? (I don't have my computer at hand right now).

I remember there was a hard coded factor somewhere for spotlights bias I had trouble with until I figured it out at the time of writing #100319
It's default settings, which should be sufficient in this case I'd say, everything is bog standard
I'm wondering whether the standard value could have changed and make this issue apparent now but not at the time of my PR. 

This is because I remember that at this time the bias input was multiplied or divided by 100 or something for spotlights somewhere before being passed to the rendering logic (don't know why). If this has changed since then it can have a material impact.

Will check when I get back to my computer.
But if the default value changed it wouldn't show up, because the default isn't stored, so a project created in 4.4.beta1 would still show up the same at the time the PR was merged, it *could* have changed while you were working on it though, but it was apparent at the time of merger

It occurs immediately at the PR, and at the merged branch (i.e. without rebase), but not immediately before it
Oh yes it's not stored, you're right.
Will take a look.
I found that the problem is the 0.005 I chose for the fixed light znear, thinking that the default light range was 1 and that this would behave like before (znear = 0.005 * range).

But the default range is actually 5, so znar has to be 0.025 to behave as before with the default setup.

With that fixed I noticed something else : peter-panning now visibly grows with the spotlight's range even at reasonable values. This is not the case with Omnis that now behave exactly like before.

I'm now investigating whether it could be related to the hardcoded bias factor set to spotlights I mentioned above :
https://github.com/godotengine/godot/blob/a7146ef807a7676f51a8a63cf4441ad647f4be66/servers/rendering/renderer_rd/storage_rd/light_storage.cpp#L963-L967

Will issue a PR once I resolved this too.

Fix patch:
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 8e393c07bdb2..6fb8fe6ca339 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -733,7 +733,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
 		vec4 v = vec4(vertex + normal_bias, 1.0);
 
 		vec4 splane = (spot_lights.data[idx].shadow_matrix * v);
-		splane.z += spot_lights.data[idx].shadow_bias / (light_length * spot_lights.data[idx].inv_radius);
+		splane.z += spot_lights.data[idx].shadow_bias;
 		splane /= splane.w;
 
 		if (sc_use_light_soft_shadows() && spot_lights.data[idx].soft_shadow_size > 0.0) {
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 0b98fc6769e7..ef79ef8cda7a 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -2510,7 +2510,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
 				}
 
 				real_t radius = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_RANGE);
-				real_t z_near = MIN(0.005f, radius);
+				real_t z_near = MIN(0.025f, radius);
 				Projection cm;
 				cm.set_perspective(90, 1, z_near, radius);
 
@@ -2600,7 +2600,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
 
 			real_t radius = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_RANGE);
 			real_t angle = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_SPOT_ANGLE);
-			real_t z_near = MIN(0.005f, radius);
+			real_t z_near = MIN(0.025f, radius);
 
 			Projection cm;
 			cm.set_perspective(angle * 2.0, 1.0, z_near, radius);


Test patch: