Repository: godotengine/godot (language: cpp)

Issue:
Occlusion Culling falsely culls large objects visible through small openings when touching the edge of the viewport
### Tested versions

Reproducible in 4.3-beta2 and 4.2.2-stable.


### System information

Arch Linux w/ X11 and  Windows 10 using Radeon RX6800XT

### Issue description

Occlusioin culling falsely culls a large object that is mostly obsured, but still visible through an opening in a wall in certain spatial configurations.

Here the red block is visible (expected):
![Clipboard - July 10, 2024 11_13 PM-1](https://github.com/godotengine/godot/assets/491704/7a4f73d3-83eb-4994-965b-54b23aa8d03b)

And here it is culled (unexpected):
![Clipboard - July 10, 2024 11_13 PM](https://github.com/godotengine/godot/assets/491704/d1112876-f44f-4b7b-aeba-0ade636d1540)

Video demonstrations in Liblast project:
https://youtu.be/xQW2PNNUggw (Godot 4.2.2-stable)
https://youtu.be/kfxirWBtZQ4 (Godot 4.3-beta2)

### Steps to reproduce

0. Open Blender
1. Model a big block (like 20x20x8 meters)
2. Model a wall with an opening in the middle (like 30x10, with the opening like 1x2)
3. Import both models into Godot
4. Enable occlusion culling and bake occluders
5. Look at the big block through the opening in the wall, so that the wall edges aren't visible in the viewport, but the opening touches the left or right edge of the screen (or even a corner).
6. At a certain combination of camera position and rotation the big block will be culled while still very much visible through the opening in the wall.

### Minimal reproduction project (MRP)

Run the game to see an animated demo.

[MRP.zip](https://github.com/user-attachments/files/16175699/MRP.zip)


Discussion / hints:
The problem is the AABB is incorrect for the red box. If I set a custom AABB it works.

(Not sure where the responsibility lies for creating the AABB in 4.x, so leave for others to investigate.)
Running the MRP locally, it seems like this is also a self-occlusion problem. The red and blue boxes are getting occluded by their own baked occluders. Setting the red and blue boxes to invisible and re-baking solves the problem as well. 

So likely some sort of precision issue that makes it so baked occluders are improperly occluding the object they were baked from. The issue also goes away when removing the wall in the centre of the scene. So there is some interaction between the angle of view, the size of the gap, and the AABB of the source mesh
I have started to try and resolve this
> I have started to try and resolve this

I'd be very happy to see your solution someday. Overculling is a problem that I feel that not enough people are trying to address in this engine.

Fix patch:
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 9f95307cf1c5..5c23f9bb0ff9 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -173,7 +173,9 @@ void RaycastOcclusionCull::RaycastHZBuffer::sort_rays(const Vector3 &p_camera_di
 					}
 					int k = tile_i * TILE_SIZE + tile_j;
 					int tile_index = i * tile_grid_size.x + j;
-					mips[0][y * buffer_size.x + x] = camera_rays[tile_index].ray.tfar[k];
+
+					Vector3 ray_dir(camera_rays[tile_index].ray.dir_x[k], camera_rays[tile_index].ray.dir_y[k], camera_rays[tile_index].ray.dir_z[k]);
+					mips[0][y * buffer_size.x + x] = camera_rays[tile_index].ray.tfar[k] * p_camera_dir.dot(ray_dir); // Store z-depth in view space.
 				}
 			}
 		}
diff --git a/servers/rendering/renderer_scene_occlusion_cull.h b/servers/rendering/renderer_scene_occlusion_cull.h
index 690bab5c9a47..1ab50c720a73 100644
--- a/servers/rendering/renderer_scene_occlusion_cull.h
+++ b/servers/rendering/renderer_scene_occlusion_cull.h
@@ -71,7 +71,7 @@ class RendererSceneOcclusionCull {
 				return false;
 			}
 
-			float min_depth = (closest_point - p_cam_position).length();
+			float min_depth = -closest_point_view.z;
 
 			Vector2 rect_min = Vector2(FLT_MAX, FLT_MAX);
 			Vector2 rect_max = Vector2(FLT_MIN, FLT_MIN);
@@ -82,12 +82,9 @@ class RendererSceneOcclusionCull {
 				Vector3 corner = Vector3(p_bounds[0] * c.x + p_bounds[3] * nc.x, p_bounds[1] * c.y + p_bounds[4] * nc.y, p_bounds[2] * c.z + p_bounds[5] * nc.z);
 				Vector3 view = p_cam_inv_transform.xform(corner);
 
-				if (p_cam_projection.is_orthogonal()) {
-					min_depth = MIN(min_depth, -view.z);
-				}
-
 				Plane vp = Plane(view, 1.0);
 				Plane projected = p_cam_projection.xform4(vp);
+				min_depth = MIN(min_depth, -view.z);
 
 				float w = projected.d;
 				if (w < 1.0) {


Test patch: