diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 8edabd93fe67ec..5e1fe8a74a8b07 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -912,12 +912,13 @@ const VEC2_HALF_NEGATIVE_Y: Vec2 = Vec2::new(0.5, -0.5); /// `Z` in view space, with range `[-inf, -f32::MIN_POSITIVE]` fn cluster_space_light_aabb( inverse_view_transform: Mat4, + view_inv_scale: Vec3, projection_matrix: Mat4, light_sphere: &Sphere, ) -> (Vec3, Vec3) { let light_aabb_view = Aabb { center: Vec3A::from(inverse_view_transform * light_sphere.center.extend(1.0)), - half_extents: Vec3A::splat(light_sphere.radius), + half_extents: Vec3A::from(light_sphere.radius * view_inv_scale.abs()), }; let (mut light_aabb_view_min, mut light_aabb_view_max) = (light_aabb_view.min(), light_aabb_view.max()); @@ -1287,6 +1288,8 @@ pub(crate) fn assign_lights_to_clusters( let mut requested_cluster_dimensions = config.dimensions_for_screen_size(screen_size); let view_transform = camera_transform.compute_matrix(); + let view_inv_scale = camera_transform.compute_transform().scale.recip(); + let view_inv_scale_max = view_inv_scale.abs().max_element(); let inverse_view_transform = view_transform.inverse(); let is_orthographic = camera.projection_matrix().w_axis.w == 1.0; @@ -1297,7 +1300,7 @@ pub(crate) fn assign_lights_to_clusters( .iter() .map(|light| { -inverse_view_row_2.dot(light.transform.translation().extend(1.0)) - + light.range + + light.range * view_inv_scale.z }) .reduce(f32::max) .unwrap_or(0.0) @@ -1319,6 +1322,8 @@ pub(crate) fn assign_lights_to_clusters( (false, 1) => config.first_slice_depth().max(far_z), _ => config.first_slice_depth(), }; + let first_slice_depth = first_slice_depth * view_inv_scale.z; + // NOTE: Ensure the far_z is at least as far as the first_depth_slice to avoid clustering problems. let far_z = far_z.max(first_slice_depth); let cluster_factors = calculate_cluster_factors( @@ -1343,6 +1348,7 @@ pub(crate) fn assign_lights_to_clusters( // it can overestimate more significantly when light ranges are only partially in view let (light_aabb_min, light_aabb_max) = cluster_space_light_aabb( inverse_view_transform, + view_inv_scale, camera.projection_matrix(), &light_sphere, ); @@ -1500,6 +1506,7 @@ pub(crate) fn assign_lights_to_clusters( let (light_aabb_xy_ndc_z_view_min, light_aabb_xy_ndc_z_view_max) = cluster_space_light_aabb( inverse_view_transform, + view_inv_scale, camera.projection_matrix(), &light_sphere, ); @@ -1530,12 +1537,14 @@ pub(crate) fn assign_lights_to_clusters( // center point on the axis of interest plus the radius, and that is not true! let view_light_sphere = Sphere { center: Vec3A::from(inverse_view_transform * light_sphere.center.extend(1.0)), - radius: light_sphere.radius, + radius: light_sphere.radius * view_inv_scale_max, }; let spot_light_dir_sin_cos = light.spot_light_angle.map(|angle| { let (angle_sin, angle_cos) = angle.sin_cos(); ( - (inverse_view_transform * light.transform.back().extend(0.0)).truncate(), + (inverse_view_transform * light.transform.back().extend(0.0)) + .truncate() + .normalize(), angle_sin, angle_cos, ) @@ -1676,7 +1685,8 @@ pub(crate) fn assign_lights_to_clusters( let angle_cull = distance_closest_point > cluster_aabb_sphere.radius; - let front_cull = v1_len > cluster_aabb_sphere.radius + light.range; + let front_cull = v1_len + > cluster_aabb_sphere.radius + light.range * view_inv_scale_max; let back_cull = v1_len < -cluster_aabb_sphere.radius; if !angle_cull && !front_cull && !back_cull {