diff --git a/crates/bevy_pbr/src/meshlet/cull_meshlets.wgsl b/crates/bevy_pbr/src/meshlet/cull_meshlets.wgsl index bb8a7c13c18f2..ef8109a94651c 100644 --- a/crates/bevy_pbr/src/meshlet/cull_meshlets.wgsl +++ b/crates/bevy_pbr/src/meshlet/cull_meshlets.wgsl @@ -52,8 +52,9 @@ fn cull_meshlets(@builtin(global_invocation_id) thread_id: vec3) { let aabb = project_view_space_sphere_to_screen_space_aabb(bounding_sphere_center_view_space, bounding_sphere_radius); let depth_pyramid_size_mip_0 = vec2(textureDimensions(depth_pyramid, 0)); - let width = (aabb.z - aabb.x) * depth_pyramid_size_mip_0.x; - let height = (aabb.w - aabb.y) * depth_pyramid_size_mip_0.y; + // we halve the size because the first depth mip resampling pass cut the full screen resolution into a power of two conservatively + let width = (aabb.z - aabb.x) * 0.5 * depth_pyramid_size_mip_0.x; + let height = (aabb.w - aabb.y) * 0.5 * depth_pyramid_size_mip_0.y; let depth_level = max(0, i32(ceil(log2(max(width, height))))); // TODO: Naga doesn't like this being a u32 let depth_pyramid_size = vec2(textureDimensions(depth_pyramid, depth_level)); let aabb_top_left = vec2(aabb.xy * depth_pyramid_size); @@ -63,9 +64,15 @@ fn cull_meshlets(@builtin(global_invocation_id) thread_id: vec3) { let depth_quad_c = textureLoad(depth_pyramid, aabb_top_left + vec2(0u, 1u), depth_level).x; let depth_quad_d = textureLoad(depth_pyramid, aabb_top_left + vec2(1u, 1u), depth_level).x; let occluder_depth = min(min(depth_quad_a, depth_quad_b), min(depth_quad_c, depth_quad_d)); - - let sphere_depth = -view.projection[3][2] / (bounding_sphere_center_view_space.z + bounding_sphere_radius); - meshlet_visible &= sphere_depth >= occluder_depth; + if view.projection[3][3] == 1.0 { + // Orthographic + let sphere_depth = view.projection[3][2] + (bounding_sphere_center_view_space.z + bounding_sphere_radius) * view.projection[2][2]; + meshlet_visible &= sphere_depth >= occluder_depth; + } else { + // Perspective + let sphere_depth = -view.projection[3][2] / (bounding_sphere_center_view_space.z + bounding_sphere_radius); + meshlet_visible &= sphere_depth >= occluder_depth; + } } #endif @@ -76,23 +83,31 @@ fn cull_meshlets(@builtin(global_invocation_id) thread_id: vec3) { // https://zeux.io/2023/01/12/approximate-projected-bounds fn project_view_space_sphere_to_screen_space_aabb(cp: vec3, r: f32) -> vec4 { - let c = vec3(cp.xy, -cp.z); - // No need to clip near plane because frustum culling already checked that + let inv_width = view.projection[0][0] * 0.5; + let inv_height = view.projection[1][1] * 0.5; + if view.projection[3][3] == 1.0 { + // Orthographic + let min_x = cp.x - r; + let max_x = cp.x + r; - let cr = c * r; - let czr2 = c.z * c.z - r * r; + let min_y = cp.y - r; + let max_y = cp.y + r; - let vx = sqrt(c.x * c.x + czr2); - let min_x = (vx * c.x - cr.z) / (vx * c.z + cr.x); - let max_x = (vx * c.x + cr.z) / (vx * c.z - cr.x); + return vec4(min_x * inv_width, 1.0 - max_y * inv_height, max_x * inv_width, 1.0 - min_y * inv_height); + } else { + // Perspective + let c = vec3(cp.xy, -cp.z); + let cr = c * r; + let czr2 = c.z * c.z - r * r; - let vy = sqrt(c.y * c.y + czr2); - let min_y = (vy * c.y - cr.z) / (vy * c.z + cr.y); - let max_y = (vy * c.y + cr.z) / (vy * c.z - cr.y); + let vx = sqrt(c.x * c.x + czr2); + let min_x = (vx * c.x - cr.z) / (vx * c.z + cr.x); + let max_x = (vx * c.x + cr.z) / (vx * c.z - cr.x); - let p00 = view.projection[0][0]; - let p11 = view.projection[1][1]; + let vy = sqrt(c.y * c.y + czr2); + let min_y = (vy * c.y - cr.z) / (vy * c.z + cr.y); + let max_y = (vy * c.y + cr.z) / (vy * c.z - cr.y); - var aabb = vec4(min_x * p00, min_y * p11, max_x * p00, max_y * p11); - return aabb.xwzy * vec4(0.5, -0.5, 0.5, -0.5) + vec4(0.5); + return vec4(min_x * inv_width, -max_y * inv_height, max_x * inv_width, -min_y * inv_height) + vec4(0.5); + } }