diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml
index 06e2f83f055b..b6fad99121b8 100644
--- a/doc/classes/Camera3D.xml
+++ b/doc/classes/Camera3D.xml
@@ -167,6 +167,7 @@
The distance to the far culling boundary for this camera relative to its local Z axis.
+ [b]Note:[/b] When [member Environment.fog_enabled] is [code]true[/code], this is used as a base for open world fog fading (with fog starting at 50% of the Z far distance).
The camera's field of view angle (in degrees). Only applicable in perspective mode. Since [member keep_aspect] locks one axis, [code]fov[/code] sets the other axis' field of view angle.
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index c3d1dc4ab63b..b1405dca636a 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -99,6 +99,7 @@
If [code]true[/code], fog effects are enabled.
+ [b]Note:[/b] Enabling fog automatically fades starting from 50% of the [member Camera3D.far] distance, regardless of [member fog_depth_density]. This is used for open world fog fading. To disable this, increase the camera's [member Camera3D.far] property.
The height at which the height fog effect begins.
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index e4628b2d5a15..11acce34bacd 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -530,7 +530,16 @@ vec4 fog_process(vec3 vertex) {
}
}
- float fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data.fog_density));
+ // Quadratic fade off to the Z far distance (for open world fog fading).
+ // Not physically accurate, but prevents visible sudden cutoffs near the Z far clip plane.
+ // This starts at 50% of the Z far distance, which is a good compromise between visibility
+ // and smoothness.
+ float fog_amount_quad = pow(smoothstep(scene_data.z_far * 0.5, scene_data.z_far, length(vertex)), 2.0);
+
+ // Exponential fog (physically accurate).
+ float fog_amount_exp = 1.0 - exp(min(0.0, -length(vertex) * scene_data.fog_density));
+
+ float fog_amount = max(fog_amount_quad, fog_amount_exp);
if (abs(scene_data.fog_height_density) >= 0.0001) {
float y = (scene_data.camera_matrix * vec4(vertex, 1.0)).y;