Rename "point light" to "clusterable object" in cluster contexts. (#13654)
We want to use the clustering infrastructure for light probes and decals as well, not just point lights. This patch builds on top of #13640 and performs the rename. To make this series easier to review, this patch makes no code changes. Only identifiers and comments are modified. ## Migration Guide * In the PBR shaders, `point_lights` is now known as `clusterable_objects`, `PointLight` is now known as `ClusterableObject`, and `cluster_light_index_lists` is now known as `clusterable_object_index_lists`.
This commit is contained in:
		
							parent
							
								
									ab2add64fa
								
							
						
					
					
						commit
						ad6872275f
					
				| @ -16,9 +16,9 @@ use bevy_transform::components::GlobalTransform; | |||||||
| use bevy_utils::tracing::warn; | use bevy_utils::tracing::warn; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     ClusterConfig, ClusterFarZMode, Clusters, GlobalVisiblePointLights, PointLight, SpotLight, |     ClusterConfig, ClusterFarZMode, Clusters, GlobalVisibleClusterableObjects, PointLight, | ||||||
|     ViewClusterBindings, VisiblePointLights, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, |     SpotLight, ViewClusterBindings, VisibleClusterableObjects, | ||||||
|     MAX_UNIFORM_BUFFER_POINT_LIGHTS, |     CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NDC_MIN: Vec2 = Vec2::NEG_ONE; | const NDC_MIN: Vec2 = Vec2::NEG_ONE; | ||||||
| @ -28,8 +28,8 @@ const VEC2_HALF: Vec2 = Vec2::splat(0.5); | |||||||
| const VEC2_HALF_NEGATIVE_Y: Vec2 = Vec2::new(0.5, -0.5); | const VEC2_HALF_NEGATIVE_Y: Vec2 = Vec2::new(0.5, -0.5); | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| // data required for assigning lights to clusters
 | // data required for assigning objects to clusters
 | ||||||
| pub(crate) struct PointLightAssignmentData { | pub(crate) struct ClusterableObjectAssignmentData { | ||||||
|     entity: Entity, |     entity: Entity, | ||||||
|     transform: GlobalTransform, |     transform: GlobalTransform, | ||||||
|     range: f32, |     range: f32, | ||||||
| @ -38,7 +38,7 @@ pub(crate) struct PointLightAssignmentData { | |||||||
|     render_layers: RenderLayers, |     render_layers: RenderLayers, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PointLightAssignmentData { | impl ClusterableObjectAssignmentData { | ||||||
|     pub fn sphere(&self) -> Sphere { |     pub fn sphere(&self) -> Sphere { | ||||||
|         Sphere { |         Sphere { | ||||||
|             center: self.transform.translation_vec3a(), |             center: self.transform.translation_vec3a(), | ||||||
| @ -49,9 +49,9 @@ impl PointLightAssignmentData { | |||||||
| 
 | 
 | ||||||
| // NOTE: Run this before update_point_light_frusta!
 | // NOTE: Run this before update_point_light_frusta!
 | ||||||
| #[allow(clippy::too_many_arguments)] | #[allow(clippy::too_many_arguments)] | ||||||
| pub(crate) fn assign_lights_to_clusters( | pub(crate) fn assign_objects_to_clusters( | ||||||
|     mut commands: Commands, |     mut commands: Commands, | ||||||
|     mut global_lights: ResMut<GlobalVisiblePointLights>, |     mut global_clusterable_objects: ResMut<GlobalVisibleClusterableObjects>, | ||||||
|     mut views: Query<( |     mut views: Query<( | ||||||
|         Entity, |         Entity, | ||||||
|         &GlobalTransform, |         &GlobalTransform, | ||||||
| @ -60,7 +60,7 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|         &ClusterConfig, |         &ClusterConfig, | ||||||
|         &mut Clusters, |         &mut Clusters, | ||||||
|         Option<&RenderLayers>, |         Option<&RenderLayers>, | ||||||
|         Option<&mut VisiblePointLights>, |         Option<&mut VisibleClusterableObjects>, | ||||||
|     )>, |     )>, | ||||||
|     point_lights_query: Query<( |     point_lights_query: Query<( | ||||||
|         Entity, |         Entity, | ||||||
| @ -76,25 +76,25 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|         Option<&RenderLayers>, |         Option<&RenderLayers>, | ||||||
|         &ViewVisibility, |         &ViewVisibility, | ||||||
|     )>, |     )>, | ||||||
|     mut lights: Local<Vec<PointLightAssignmentData>>, |     mut clusterable_objects: Local<Vec<ClusterableObjectAssignmentData>>, | ||||||
|     mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>, |     mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>, | ||||||
|     mut max_point_lights_warning_emitted: Local<bool>, |     mut max_clusterable_objects_warning_emitted: Local<bool>, | ||||||
|     render_device: Option<Res<RenderDevice>>, |     render_device: Option<Res<RenderDevice>>, | ||||||
| ) { | ) { | ||||||
|     let Some(render_device) = render_device else { |     let Some(render_device) = render_device else { | ||||||
|         return; |         return; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     global_lights.entities.clear(); |     global_clusterable_objects.entities.clear(); | ||||||
|     lights.clear(); |     clusterable_objects.clear(); | ||||||
|     // collect just the relevant light query data into a persisted vec to avoid reallocating each frame
 |     // collect just the relevant query data into a persisted vec to avoid reallocating each frame
 | ||||||
|     lights.extend( |     clusterable_objects.extend( | ||||||
|         point_lights_query |         point_lights_query | ||||||
|             .iter() |             .iter() | ||||||
|             .filter(|(.., visibility)| visibility.get()) |             .filter(|(.., visibility)| visibility.get()) | ||||||
|             .map( |             .map( | ||||||
|                 |(entity, transform, point_light, maybe_layers, _visibility)| { |                 |(entity, transform, point_light, maybe_layers, _visibility)| { | ||||||
|                     PointLightAssignmentData { |                     ClusterableObjectAssignmentData { | ||||||
|                         entity, |                         entity, | ||||||
|                         transform: GlobalTransform::from_translation(transform.translation()), |                         transform: GlobalTransform::from_translation(transform.translation()), | ||||||
|                         shadows_enabled: point_light.shadows_enabled, |                         shadows_enabled: point_light.shadows_enabled, | ||||||
| @ -105,13 +105,13 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|                 }, |                 }, | ||||||
|             ), |             ), | ||||||
|     ); |     ); | ||||||
|     lights.extend( |     clusterable_objects.extend( | ||||||
|         spot_lights_query |         spot_lights_query | ||||||
|             .iter() |             .iter() | ||||||
|             .filter(|(.., visibility)| visibility.get()) |             .filter(|(.., visibility)| visibility.get()) | ||||||
|             .map( |             .map( | ||||||
|                 |(entity, transform, spot_light, maybe_layers, _visibility)| { |                 |(entity, transform, spot_light, maybe_layers, _visibility)| { | ||||||
|                     PointLightAssignmentData { |                     ClusterableObjectAssignmentData { | ||||||
|                         entity, |                         entity, | ||||||
|                         transform: *transform, |                         transform: *transform, | ||||||
|                         shadows_enabled: spot_light.shadows_enabled, |                         shadows_enabled: spot_light.shadows_enabled, | ||||||
| @ -129,55 +129,60 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|         clustered_forward_buffer_binding_type, |         clustered_forward_buffer_binding_type, | ||||||
|         BufferBindingType::Storage { .. } |         BufferBindingType::Storage { .. } | ||||||
|     ); |     ); | ||||||
|     if lights.len() > MAX_UNIFORM_BUFFER_POINT_LIGHTS && !supports_storage_buffers { |     if clusterable_objects.len() > MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS | ||||||
|         lights.sort_by(|light_1, light_2| { |         && !supports_storage_buffers | ||||||
|             crate::point_light_order( |     { | ||||||
|  |         clusterable_objects.sort_by(|clusterable_object_1, clusterable_object_2| { | ||||||
|  |             crate::clusterable_object_order( | ||||||
|                 ( |                 ( | ||||||
|                     &light_1.entity, |                     &clusterable_object_1.entity, | ||||||
|                     &light_1.shadows_enabled, |                     &clusterable_object_1.shadows_enabled, | ||||||
|                     &light_1.spot_light_angle.is_some(), |                     &clusterable_object_1.spot_light_angle.is_some(), | ||||||
|                 ), |                 ), | ||||||
|                 ( |                 ( | ||||||
|                     &light_2.entity, |                     &clusterable_object_2.entity, | ||||||
|                     &light_2.shadows_enabled, |                     &clusterable_object_2.shadows_enabled, | ||||||
|                     &light_2.spot_light_angle.is_some(), |                     &clusterable_object_2.spot_light_angle.is_some(), | ||||||
|                 ), |                 ), | ||||||
|             ) |             ) | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         // check each light against each view's frustum, keep only those that affect at least one of our views
 |         // check each clusterable object against each view's frustum, keep only
 | ||||||
|  |         // those that affect at least one of our views
 | ||||||
|         let frusta: Vec<_> = views |         let frusta: Vec<_> = views | ||||||
|             .iter() |             .iter() | ||||||
|             .map(|(_, _, _, frustum, _, _, _, _)| *frustum) |             .map(|(_, _, _, frustum, _, _, _, _)| *frustum) | ||||||
|             .collect(); |             .collect(); | ||||||
|         let mut lights_in_view_count = 0; |         let mut clusterable_objects_in_view_count = 0; | ||||||
|         lights.retain(|light| { |         clusterable_objects.retain(|clusterable_object| { | ||||||
|             // take one extra light to check if we should emit the warning
 |             // take one extra clusterable object to check if we should emit the warning
 | ||||||
|             if lights_in_view_count == MAX_UNIFORM_BUFFER_POINT_LIGHTS + 1 { |             if clusterable_objects_in_view_count == MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS + 1 { | ||||||
|                 false |                 false | ||||||
|             } else { |             } else { | ||||||
|                 let light_sphere = light.sphere(); |                 let clusterable_object_sphere = clusterable_object.sphere(); | ||||||
|                 let light_in_view = frusta |                 let clusterable_object_in_view = frusta | ||||||
|                     .iter() |                     .iter() | ||||||
|                     .any(|frustum| frustum.intersects_sphere(&light_sphere, true)); |                     .any(|frustum| frustum.intersects_sphere(&clusterable_object_sphere, true)); | ||||||
| 
 | 
 | ||||||
|                 if light_in_view { |                 if clusterable_object_in_view { | ||||||
|                     lights_in_view_count += 1; |                     clusterable_objects_in_view_count += 1; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 light_in_view |                 clusterable_object_in_view | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         if lights.len() > MAX_UNIFORM_BUFFER_POINT_LIGHTS && !*max_point_lights_warning_emitted { |         if clusterable_objects.len() > MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS | ||||||
|  |             && !*max_clusterable_objects_warning_emitted | ||||||
|  |         { | ||||||
|             warn!( |             warn!( | ||||||
|                 "MAX_UNIFORM_BUFFER_POINT_LIGHTS ({}) exceeded", |                 "MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS ({}) exceeded", | ||||||
|                 MAX_UNIFORM_BUFFER_POINT_LIGHTS |                 MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS | ||||||
|             ); |             ); | ||||||
|             *max_point_lights_warning_emitted = true; |             *max_clusterable_objects_warning_emitted = true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         lights.truncate(MAX_UNIFORM_BUFFER_POINT_LIGHTS); |         clusterable_objects.truncate(MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for ( |     for ( | ||||||
| @ -188,15 +193,17 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|         config, |         config, | ||||||
|         clusters, |         clusters, | ||||||
|         maybe_layers, |         maybe_layers, | ||||||
|         mut visible_lights, |         mut visible_clusterable_objects, | ||||||
|     ) in &mut views |     ) in &mut views | ||||||
|     { |     { | ||||||
|         let view_layers = maybe_layers.unwrap_or_default(); |         let view_layers = maybe_layers.unwrap_or_default(); | ||||||
|         let clusters = clusters.into_inner(); |         let clusters = clusters.into_inner(); | ||||||
| 
 | 
 | ||||||
|         if matches!(config, ClusterConfig::None) { |         if matches!(config, ClusterConfig::None) { | ||||||
|             if visible_lights.is_some() { |             if visible_clusterable_objects.is_some() { | ||||||
|                 commands.entity(view_entity).remove::<VisiblePointLights>(); |                 commands | ||||||
|  |                     .entity(view_entity) | ||||||
|  |                     .remove::<VisibleClusterableObjects>(); | ||||||
|             } |             } | ||||||
|             clusters.clear(); |             clusters.clear(); | ||||||
|             continue; |             continue; | ||||||
| @ -216,13 +223,13 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|         let is_orthographic = camera.clip_from_view().w_axis.w == 1.0; |         let is_orthographic = camera.clip_from_view().w_axis.w == 1.0; | ||||||
| 
 | 
 | ||||||
|         let far_z = match config.far_z_mode() { |         let far_z = match config.far_z_mode() { | ||||||
|             ClusterFarZMode::MaxLightRange => { |             ClusterFarZMode::MaxClusterableObjectRange => { | ||||||
|                 let view_from_world_row_2 = view_from_world.row(2); |                 let view_from_world_row_2 = view_from_world.row(2); | ||||||
|                 lights |                 clusterable_objects | ||||||
|                     .iter() |                     .iter() | ||||||
|                     .map(|light| { |                     .map(|object| { | ||||||
|                         -view_from_world_row_2.dot(light.transform.translation().extend(1.0)) |                         -view_from_world_row_2.dot(object.transform.translation().extend(1.0)) | ||||||
|                             + light.range * view_from_world_scale.z |                             + object.range * view_from_world_scale.z | ||||||
|                     }) |                     }) | ||||||
|                     .reduce(f32::max) |                     .reduce(f32::max) | ||||||
|                     .unwrap_or(0.0) |                     .unwrap_or(0.0) | ||||||
| @ -257,43 +264,44 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
| 
 | 
 | ||||||
|         if config.dynamic_resizing() { |         if config.dynamic_resizing() { | ||||||
|             let mut cluster_index_estimate = 0.0; |             let mut cluster_index_estimate = 0.0; | ||||||
|             for light in &lights { |             for clusterable_object in &clusterable_objects { | ||||||
|                 let light_sphere = light.sphere(); |                 let clusterable_object_sphere = clusterable_object.sphere(); | ||||||
| 
 | 
 | ||||||
|                 // Check if the light is within the view frustum
 |                 // Check if the clusterable object is within the view frustum
 | ||||||
|                 if !frustum.intersects_sphere(&light_sphere, true) { |                 if !frustum.intersects_sphere(&clusterable_object_sphere, true) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // calculate a conservative aabb estimate of number of clusters affected by this light
 |                 // calculate a conservative aabb estimate of number of clusters affected by this light
 | ||||||
|                 // this overestimates index counts by at most 50% (and typically much less) when the whole light range is in view
 |                 // this overestimates index counts by at most 50% (and typically much less) when the whole light range is in view
 | ||||||
|                 // it can overestimate more significantly when light ranges are only partially in view
 |                 // it can overestimate more significantly when light ranges are only partially in view
 | ||||||
|                 let (light_aabb_min, light_aabb_max) = cluster_space_light_aabb( |                 let (clusterable_object_aabb_min, clusterable_object_aabb_max) = | ||||||
|                     view_from_world, |                     cluster_space_clusterable_object_aabb( | ||||||
|                     view_from_world_scale, |                         view_from_world, | ||||||
|                     camera.clip_from_view(), |                         view_from_world_scale, | ||||||
|                     &light_sphere, |                         camera.clip_from_view(), | ||||||
|                 ); |                         &clusterable_object_sphere, | ||||||
|  |                     ); | ||||||
| 
 | 
 | ||||||
|                 // since we won't adjust z slices we can calculate exact number of slices required in z dimension
 |                 // since we won't adjust z slices we can calculate exact number of slices required in z dimension
 | ||||||
|                 let z_cluster_min = view_z_to_z_slice( |                 let z_cluster_min = view_z_to_z_slice( | ||||||
|                     cluster_factors, |                     cluster_factors, | ||||||
|                     requested_cluster_dimensions.z, |                     requested_cluster_dimensions.z, | ||||||
|                     light_aabb_min.z, |                     clusterable_object_aabb_min.z, | ||||||
|                     is_orthographic, |                     is_orthographic, | ||||||
|                 ); |                 ); | ||||||
|                 let z_cluster_max = view_z_to_z_slice( |                 let z_cluster_max = view_z_to_z_slice( | ||||||
|                     cluster_factors, |                     cluster_factors, | ||||||
|                     requested_cluster_dimensions.z, |                     requested_cluster_dimensions.z, | ||||||
|                     light_aabb_max.z, |                     clusterable_object_aabb_max.z, | ||||||
|                     is_orthographic, |                     is_orthographic, | ||||||
|                 ); |                 ); | ||||||
|                 let z_count = |                 let z_count = | ||||||
|                     z_cluster_min.max(z_cluster_max) - z_cluster_min.min(z_cluster_max) + 1; |                     z_cluster_min.max(z_cluster_max) - z_cluster_min.min(z_cluster_max) + 1; | ||||||
| 
 | 
 | ||||||
|                 // calculate x/y count using floats to avoid overestimating counts due to large initial tile sizes
 |                 // calculate x/y count using floats to avoid overestimating counts due to large initial tile sizes
 | ||||||
|                 let xy_min = light_aabb_min.xy(); |                 let xy_min = clusterable_object_aabb_min.xy(); | ||||||
|                 let xy_max = light_aabb_max.xy(); |                 let xy_max = clusterable_object_aabb_max.xy(); | ||||||
|                 // multiply by 0.5 to move from [-1,1] to [-0.5, 0.5], max extent of 1 in each dimension
 |                 // multiply by 0.5 to move from [-1,1] to [-0.5, 0.5], max extent of 1 in each dimension
 | ||||||
|                 let xy_count = (xy_max - xy_min) |                 let xy_count = (xy_max - xy_min) | ||||||
|                     * 0.5 |                     * 0.5 | ||||||
| @ -339,16 +347,16 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
| 
 | 
 | ||||||
|         let view_from_clip = camera.clip_from_view().inverse(); |         let view_from_clip = camera.clip_from_view().inverse(); | ||||||
| 
 | 
 | ||||||
|         for lights in &mut clusters.lights { |         for clusterable_objects in &mut clusters.clusterable_objects { | ||||||
|             lights.entities.clear(); |             clusterable_objects.entities.clear(); | ||||||
|             lights.point_light_count = 0; |             clusterable_objects.point_light_count = 0; | ||||||
|             lights.spot_light_count = 0; |             clusterable_objects.spot_light_count = 0; | ||||||
|         } |         } | ||||||
|         let cluster_count = |         let cluster_count = | ||||||
|             (clusters.dimensions.x * clusters.dimensions.y * clusters.dimensions.z) as usize; |             (clusters.dimensions.x * clusters.dimensions.y * clusters.dimensions.z) as usize; | ||||||
|         clusters |         clusters | ||||||
|             .lights |             .clusterable_objects | ||||||
|             .resize_with(cluster_count, VisiblePointLights::default); |             .resize_with(cluster_count, VisibleClusterableObjects::default); | ||||||
| 
 | 
 | ||||||
|         // initialize empty cluster bounding spheres
 |         // initialize empty cluster bounding spheres
 | ||||||
|         cluster_aabb_spheres.clear(); |         cluster_aabb_spheres.clear(); | ||||||
| @ -411,46 +419,53 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|             z_planes.push(HalfSpace::new(normal.extend(d))); |             z_planes.push(HalfSpace::new(normal.extend(d))); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let mut update_from_light_intersections = |visible_lights: &mut Vec<Entity>| { |         let mut update_from_object_intersections = |visible_clusterable_objects: &mut Vec< | ||||||
|             for light in &lights { |             Entity, | ||||||
|                 // check if the light layers overlap the view layers
 |         >| { | ||||||
|                 if !view_layers.intersects(&light.render_layers) { |             for clusterable_object in &clusterable_objects { | ||||||
|  |                 // check if the clusterable light layers overlap the view layers
 | ||||||
|  |                 if !view_layers.intersects(&clusterable_object.render_layers) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 let light_sphere = light.sphere(); |                 let clusterable_object_sphere = clusterable_object.sphere(); | ||||||
| 
 | 
 | ||||||
|                 // Check if the light is within the view frustum
 |                 // Check if the clusterable object is within the view frustum
 | ||||||
|                 if !frustum.intersects_sphere(&light_sphere, true) { |                 if !frustum.intersects_sphere(&clusterable_object_sphere, true) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // NOTE: The light intersects the frustum so it must be visible and part of the global set
 |                 // NOTE: The clusterable object intersects the frustum so it
 | ||||||
|                 global_lights.entities.insert(light.entity); |                 // must be visible and part of the global set
 | ||||||
|                 visible_lights.push(light.entity); |                 global_clusterable_objects | ||||||
|  |                     .entities | ||||||
|  |                     .insert(clusterable_object.entity); | ||||||
|  |                 visible_clusterable_objects.push(clusterable_object.entity); | ||||||
| 
 | 
 | ||||||
|                 // note: caching seems to be slower than calling twice for this aabb calculation
 |                 // note: caching seems to be slower than calling twice for this aabb calculation
 | ||||||
|                 let (light_aabb_xy_ndc_z_view_min, light_aabb_xy_ndc_z_view_max) = |                 let ( | ||||||
|                     cluster_space_light_aabb( |                     clusterable_object_aabb_xy_ndc_z_view_min, | ||||||
|                         view_from_world, |                     clusterable_object_aabb_xy_ndc_z_view_max, | ||||||
|                         view_from_world_scale, |                 ) = cluster_space_clusterable_object_aabb( | ||||||
|                         camera.clip_from_view(), |                     view_from_world, | ||||||
|                         &light_sphere, |                     view_from_world_scale, | ||||||
|                     ); |                     camera.clip_from_view(), | ||||||
|  |                     &clusterable_object_sphere, | ||||||
|  |                 ); | ||||||
| 
 | 
 | ||||||
|                 let min_cluster = ndc_position_to_cluster( |                 let min_cluster = ndc_position_to_cluster( | ||||||
|                     clusters.dimensions, |                     clusters.dimensions, | ||||||
|                     cluster_factors, |                     cluster_factors, | ||||||
|                     is_orthographic, |                     is_orthographic, | ||||||
|                     light_aabb_xy_ndc_z_view_min, |                     clusterable_object_aabb_xy_ndc_z_view_min, | ||||||
|                     light_aabb_xy_ndc_z_view_min.z, |                     clusterable_object_aabb_xy_ndc_z_view_min.z, | ||||||
|                 ); |                 ); | ||||||
|                 let max_cluster = ndc_position_to_cluster( |                 let max_cluster = ndc_position_to_cluster( | ||||||
|                     clusters.dimensions, |                     clusters.dimensions, | ||||||
|                     cluster_factors, |                     cluster_factors, | ||||||
|                     is_orthographic, |                     is_orthographic, | ||||||
|                     light_aabb_xy_ndc_z_view_max, |                     clusterable_object_aabb_xy_ndc_z_view_max, | ||||||
|                     light_aabb_xy_ndc_z_view_max.z, |                     clusterable_object_aabb_xy_ndc_z_view_max.z, | ||||||
|                 ); |                 ); | ||||||
|                 let (min_cluster, max_cluster) = |                 let (min_cluster, max_cluster) = | ||||||
|                     (min_cluster.min(max_cluster), min_cluster.max(max_cluster)); |                     (min_cluster.min(max_cluster), min_cluster.max(max_cluster)); | ||||||
| @ -462,47 +477,51 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|                 // stretched and warped, which prevents simpler algorithms from being correct
 |                 // stretched and warped, which prevents simpler algorithms from being correct
 | ||||||
|                 // as they often assume that the widest part of the sphere under projection is the
 |                 // as they often assume that the widest part of the sphere under projection is the
 | ||||||
|                 // center point on the axis of interest plus the radius, and that is not true!
 |                 // center point on the axis of interest plus the radius, and that is not true!
 | ||||||
|                 let view_light_sphere = Sphere { |                 let view_clusterable_object_sphere = Sphere { | ||||||
|                     center: Vec3A::from(view_from_world * light_sphere.center.extend(1.0)), |                     center: Vec3A::from( | ||||||
|                     radius: light_sphere.radius * view_from_world_scale_max, |                         view_from_world * clusterable_object_sphere.center.extend(1.0), | ||||||
|  |                     ), | ||||||
|  |                     radius: clusterable_object_sphere.radius * view_from_world_scale_max, | ||||||
|                 }; |                 }; | ||||||
|                 let spot_light_dir_sin_cos = light.spot_light_angle.map(|angle| { |                 let spot_light_dir_sin_cos = clusterable_object.spot_light_angle.map(|angle| { | ||||||
|                     let (angle_sin, angle_cos) = angle.sin_cos(); |                     let (angle_sin, angle_cos) = angle.sin_cos(); | ||||||
|                     ( |                     ( | ||||||
|                         (view_from_world * light.transform.back().extend(0.0)) |                         (view_from_world * clusterable_object.transform.back().extend(0.0)) | ||||||
|                             .truncate() |                             .truncate() | ||||||
|                             .normalize(), |                             .normalize(), | ||||||
|                         angle_sin, |                         angle_sin, | ||||||
|                         angle_cos, |                         angle_cos, | ||||||
|                     ) |                     ) | ||||||
|                 }); |                 }); | ||||||
|                 let light_center_clip = |                 let clusterable_object_center_clip = | ||||||
|                     camera.clip_from_view() * view_light_sphere.center.extend(1.0); |                     camera.clip_from_view() * view_clusterable_object_sphere.center.extend(1.0); | ||||||
|                 let light_center_ndc = light_center_clip.xyz() / light_center_clip.w; |                 let object_center_ndc = | ||||||
|  |                     clusterable_object_center_clip.xyz() / clusterable_object_center_clip.w; | ||||||
|                 let cluster_coordinates = ndc_position_to_cluster( |                 let cluster_coordinates = ndc_position_to_cluster( | ||||||
|                     clusters.dimensions, |                     clusters.dimensions, | ||||||
|                     cluster_factors, |                     cluster_factors, | ||||||
|                     is_orthographic, |                     is_orthographic, | ||||||
|                     light_center_ndc, |                     object_center_ndc, | ||||||
|                     view_light_sphere.center.z, |                     view_clusterable_object_sphere.center.z, | ||||||
|                 ); |                 ); | ||||||
|                 let z_center = if light_center_ndc.z <= 1.0 { |                 let z_center = if object_center_ndc.z <= 1.0 { | ||||||
|                     Some(cluster_coordinates.z) |                     Some(cluster_coordinates.z) | ||||||
|                 } else { |                 } else { | ||||||
|                     None |                     None | ||||||
|                 }; |                 }; | ||||||
|                 let y_center = if light_center_ndc.y > 1.0 { |                 let y_center = if object_center_ndc.y > 1.0 { | ||||||
|                     None |                     None | ||||||
|                 } else if light_center_ndc.y < -1.0 { |                 } else if object_center_ndc.y < -1.0 { | ||||||
|                     Some(clusters.dimensions.y + 1) |                     Some(clusters.dimensions.y + 1) | ||||||
|                 } else { |                 } else { | ||||||
|                     Some(cluster_coordinates.y) |                     Some(cluster_coordinates.y) | ||||||
|                 }; |                 }; | ||||||
|                 for z in min_cluster.z..=max_cluster.z { |                 for z in min_cluster.z..=max_cluster.z { | ||||||
|                     let mut z_light = view_light_sphere.clone(); |                     let mut z_object = view_clusterable_object_sphere.clone(); | ||||||
|                     if z_center.is_none() || z != z_center.unwrap() { |                     if z_center.is_none() || z != z_center.unwrap() { | ||||||
|                         // The z plane closer to the light has the larger radius circle where the
 |                         // The z plane closer to the clusterable object has the
 | ||||||
|                         // light sphere intersects the z plane.
 |                         // larger radius circle where the light sphere
 | ||||||
|  |                         // intersects the z plane.
 | ||||||
|                         let z_plane = if z_center.is_some() && z < z_center.unwrap() { |                         let z_plane = if z_center.is_some() && z < z_center.unwrap() { | ||||||
|                             z_planes[(z + 1) as usize] |                             z_planes[(z + 1) as usize] | ||||||
|                         } else { |                         } else { | ||||||
| @ -510,17 +529,18 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|                         }; |                         }; | ||||||
|                         // Project the sphere to this z plane and use its radius as the radius of a
 |                         // Project the sphere to this z plane and use its radius as the radius of a
 | ||||||
|                         // new, refined sphere.
 |                         // new, refined sphere.
 | ||||||
|                         if let Some(projected) = project_to_plane_z(z_light, z_plane) { |                         if let Some(projected) = project_to_plane_z(z_object, z_plane) { | ||||||
|                             z_light = projected; |                             z_object = projected; | ||||||
|                         } else { |                         } else { | ||||||
|                             continue; |                             continue; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     for y in min_cluster.y..=max_cluster.y { |                     for y in min_cluster.y..=max_cluster.y { | ||||||
|                         let mut y_light = z_light.clone(); |                         let mut y_object = z_object.clone(); | ||||||
|                         if y_center.is_none() || y != y_center.unwrap() { |                         if y_center.is_none() || y != y_center.unwrap() { | ||||||
|                             // The y plane closer to the light has the larger radius circle where the
 |                             // The y plane closer to the clusterable object has
 | ||||||
|                             // light sphere intersects the y plane.
 |                             // the larger radius circle where the light sphere
 | ||||||
|  |                             // intersects the y plane.
 | ||||||
|                             let y_plane = if y_center.is_some() && y < y_center.unwrap() { |                             let y_plane = if y_center.is_some() && y < y_center.unwrap() { | ||||||
|                                 y_planes[(y + 1) as usize] |                                 y_planes[(y + 1) as usize] | ||||||
|                             } else { |                             } else { | ||||||
| @ -529,9 +549,9 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|                             // Project the refined sphere to this y plane and use its radius as the
 |                             // Project the refined sphere to this y plane and use its radius as the
 | ||||||
|                             // radius of a new, even more refined sphere.
 |                             // radius of a new, even more refined sphere.
 | ||||||
|                             if let Some(projected) = |                             if let Some(projected) = | ||||||
|                                 project_to_plane_y(y_light, y_plane, is_orthographic) |                                 project_to_plane_y(y_object, y_plane, is_orthographic) | ||||||
|                             { |                             { | ||||||
|                                 y_light = projected; |                                 y_object = projected; | ||||||
|                             } else { |                             } else { | ||||||
|                                 continue; |                                 continue; | ||||||
|                             } |                             } | ||||||
| @ -542,9 +562,9 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|                             if min_x >= max_cluster.x |                             if min_x >= max_cluster.x | ||||||
|                                 || -get_distance_x( |                                 || -get_distance_x( | ||||||
|                                     x_planes[(min_x + 1) as usize], |                                     x_planes[(min_x + 1) as usize], | ||||||
|                                     y_light.center, |                                     y_object.center, | ||||||
|                                     is_orthographic, |                                     is_orthographic, | ||||||
|                                 ) + y_light.radius |                                 ) + y_object.radius | ||||||
|                                     > 0.0 |                                     > 0.0 | ||||||
|                             { |                             { | ||||||
|                                 break; |                                 break; | ||||||
| @ -557,9 +577,9 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|                             if max_x <= min_x |                             if max_x <= min_x | ||||||
|                                 || get_distance_x( |                                 || get_distance_x( | ||||||
|                                     x_planes[max_x as usize], |                                     x_planes[max_x as usize], | ||||||
|                                     y_light.center, |                                     y_object.center, | ||||||
|                                     is_orthographic, |                                     is_orthographic, | ||||||
|                                 ) + y_light.radius |                                 ) + y_object.radius | ||||||
|                                     > 0.0 |                                     > 0.0 | ||||||
|                             { |                             { | ||||||
|                                 break; |                                 break; | ||||||
| @ -601,7 +621,8 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
| 
 | 
 | ||||||
|                                 // test -- based on https://bartwronski.com/2017/04/13/cull-that-cone/
 |                                 // test -- based on https://bartwronski.com/2017/04/13/cull-that-cone/
 | ||||||
|                                 let spot_light_offset = Vec3::from( |                                 let spot_light_offset = Vec3::from( | ||||||
|                                     view_light_sphere.center - cluster_aabb_sphere.center, |                                     view_clusterable_object_sphere.center | ||||||
|  |                                         - cluster_aabb_sphere.center, | ||||||
|                                 ); |                                 ); | ||||||
|                                 let spot_light_dist_sq = spot_light_offset.length_squared(); |                                 let spot_light_dist_sq = spot_light_offset.length_squared(); | ||||||
|                                 let v1_len = spot_light_offset.dot(view_light_direction); |                                 let v1_len = spot_light_offset.dot(view_light_direction); | ||||||
| @ -614,21 +635,26 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
| 
 | 
 | ||||||
|                                 let front_cull = v1_len |                                 let front_cull = v1_len | ||||||
|                                     > cluster_aabb_sphere.radius |                                     > cluster_aabb_sphere.radius | ||||||
|                                         + light.range * view_from_world_scale_max; |                                         + clusterable_object.range * view_from_world_scale_max; | ||||||
|                                 let back_cull = v1_len < -cluster_aabb_sphere.radius; |                                 let back_cull = v1_len < -cluster_aabb_sphere.radius; | ||||||
| 
 | 
 | ||||||
|                                 if !angle_cull && !front_cull && !back_cull { |                                 if !angle_cull && !front_cull && !back_cull { | ||||||
|                                     // this cluster is affected by the spot light
 |                                     // this cluster is affected by the spot light
 | ||||||
|                                     clusters.lights[cluster_index].entities.push(light.entity); |                                     clusters.clusterable_objects[cluster_index] | ||||||
|                                     clusters.lights[cluster_index].spot_light_count += 1; |                                         .entities | ||||||
|  |                                         .push(clusterable_object.entity); | ||||||
|  |                                     clusters.clusterable_objects[cluster_index].spot_light_count += | ||||||
|  |                                         1; | ||||||
|                                 } |                                 } | ||||||
|                                 cluster_index += clusters.dimensions.z as usize; |                                 cluster_index += clusters.dimensions.z as usize; | ||||||
|                             } |                             } | ||||||
|                         } else { |                         } else { | ||||||
|                             for _ in min_x..=max_x { |                             for _ in min_x..=max_x { | ||||||
|                                 // all clusters within range are affected by point lights
 |                                 // all clusters within range are affected by point lights
 | ||||||
|                                 clusters.lights[cluster_index].entities.push(light.entity); |                                 clusters.clusterable_objects[cluster_index] | ||||||
|                                 clusters.lights[cluster_index].point_light_count += 1; |                                     .entities | ||||||
|  |                                     .push(clusterable_object.entity); | ||||||
|  |                                 clusters.clusterable_objects[cluster_index].point_light_count += 1; | ||||||
|                                 cluster_index += clusters.dimensions.z as usize; |                                 cluster_index += clusters.dimensions.z as usize; | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
| @ -637,17 +663,19 @@ pub(crate) fn assign_lights_to_clusters( | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // reuse existing visible lights Vec, if it exists
 |         // reuse existing visible clusterable objects Vec, if it exists
 | ||||||
|         if let Some(visible_lights) = visible_lights.as_mut() { |         if let Some(visible_clusterable_objects) = visible_clusterable_objects.as_mut() { | ||||||
|             visible_lights.entities.clear(); |             visible_clusterable_objects.entities.clear(); | ||||||
|             update_from_light_intersections(&mut visible_lights.entities); |             update_from_object_intersections(&mut visible_clusterable_objects.entities); | ||||||
|         } else { |         } else { | ||||||
|             let mut entities = Vec::new(); |             let mut entities = Vec::new(); | ||||||
|             update_from_light_intersections(&mut entities); |             update_from_object_intersections(&mut entities); | ||||||
|             commands.entity(view_entity).insert(VisiblePointLights { |             commands | ||||||
|                 entities, |                 .entity(view_entity) | ||||||
|                 ..Default::default() |                 .insert(VisibleClusterableObjects { | ||||||
|             }); |                     entities, | ||||||
|  |                     ..Default::default() | ||||||
|  |                 }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -759,22 +787,24 @@ fn ndc_position_to_cluster( | |||||||
|         .clamp(UVec3::ZERO, cluster_dimensions - UVec3::ONE) |         .clamp(UVec3::ZERO, cluster_dimensions - UVec3::ONE) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Calculate bounds for the light using a view space aabb.
 | /// Calculate bounds for the clusterable object using a view space aabb.
 | ||||||
| /// Returns a `(Vec3, Vec3)` containing minimum and maximum with
 | /// Returns a `(Vec3, Vec3)` containing minimum and maximum with
 | ||||||
| ///     `X` and `Y` in normalized device coordinates with range `[-1, 1]`
 | ///     `X` and `Y` in normalized device coordinates with range `[-1, 1]`
 | ||||||
| ///     `Z` in view space, with range `[-inf, -f32::MIN_POSITIVE]`
 | ///     `Z` in view space, with range `[-inf, -f32::MIN_POSITIVE]`
 | ||||||
| fn cluster_space_light_aabb( | fn cluster_space_clusterable_object_aabb( | ||||||
|     view_from_world: Mat4, |     view_from_world: Mat4, | ||||||
|     view_from_world_scale: Vec3, |     view_from_world_scale: Vec3, | ||||||
|     clip_from_view: Mat4, |     clip_from_view: Mat4, | ||||||
|     light_sphere: &Sphere, |     clusterable_object_sphere: &Sphere, | ||||||
| ) -> (Vec3, Vec3) { | ) -> (Vec3, Vec3) { | ||||||
|     let light_aabb_view = Aabb { |     let clusterable_object_aabb_view = Aabb { | ||||||
|         center: Vec3A::from(view_from_world * light_sphere.center.extend(1.0)), |         center: Vec3A::from(view_from_world * clusterable_object_sphere.center.extend(1.0)), | ||||||
|         half_extents: Vec3A::from(light_sphere.radius * view_from_world_scale.abs()), |         half_extents: Vec3A::from(clusterable_object_sphere.radius * view_from_world_scale.abs()), | ||||||
|     }; |     }; | ||||||
|     let (mut light_aabb_view_min, mut light_aabb_view_max) = |     let (mut clusterable_object_aabb_view_min, mut clusterable_object_aabb_view_max) = ( | ||||||
|         (light_aabb_view.min(), light_aabb_view.max()); |         clusterable_object_aabb_view.min(), | ||||||
|  |         clusterable_object_aabb_view.max(), | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     // Constrain view z to be negative - i.e. in front of the camera
 |     // Constrain view z to be negative - i.e. in front of the camera
 | ||||||
|     // When view z is >= 0.0 and we're using a perspective projection, bad things happen.
 |     // When view z is >= 0.0 and we're using a perspective projection, bad things happen.
 | ||||||
| @ -783,67 +813,71 @@ fn cluster_space_light_aabb( | |||||||
|     // use of min/max operations as something that was to the left in view space is now returning a
 |     // use of min/max operations as something that was to the left in view space is now returning a
 | ||||||
|     // coordinate that for view z in front of the camera would be on the right, but at view z behind the
 |     // coordinate that for view z in front of the camera would be on the right, but at view z behind the
 | ||||||
|     // camera is on the left. So, we just constrain view z to be < 0.0 and necessarily in front of the camera.
 |     // camera is on the left. So, we just constrain view z to be < 0.0 and necessarily in front of the camera.
 | ||||||
|     light_aabb_view_min.z = light_aabb_view_min.z.min(-f32::MIN_POSITIVE); |     clusterable_object_aabb_view_min.z = clusterable_object_aabb_view_min.z.min(-f32::MIN_POSITIVE); | ||||||
|     light_aabb_view_max.z = light_aabb_view_max.z.min(-f32::MIN_POSITIVE); |     clusterable_object_aabb_view_max.z = clusterable_object_aabb_view_max.z.min(-f32::MIN_POSITIVE); | ||||||
| 
 | 
 | ||||||
|     // Is there a cheaper way to do this? The problem is that because of perspective
 |     // Is there a cheaper way to do this? The problem is that because of perspective
 | ||||||
|     // the point at max z but min xy may be less xy in screenspace, and similar. As
 |     // the point at max z but min xy may be less xy in screenspace, and similar. As
 | ||||||
|     // such, projecting the min and max xy at both the closer and further z and taking
 |     // such, projecting the min and max xy at both the closer and further z and taking
 | ||||||
|     // the min and max of those projected points addresses this.
 |     // the min and max of those projected points addresses this.
 | ||||||
|     let ( |     let ( | ||||||
|         light_aabb_view_xymin_near, |         clusterable_object_aabb_view_xymin_near, | ||||||
|         light_aabb_view_xymin_far, |         clusterable_object_aabb_view_xymin_far, | ||||||
|         light_aabb_view_xymax_near, |         clusterable_object_aabb_view_xymax_near, | ||||||
|         light_aabb_view_xymax_far, |         clusterable_object_aabb_view_xymax_far, | ||||||
|     ) = ( |     ) = ( | ||||||
|         light_aabb_view_min, |         clusterable_object_aabb_view_min, | ||||||
|         light_aabb_view_min.xy().extend(light_aabb_view_max.z), |         clusterable_object_aabb_view_min | ||||||
|         light_aabb_view_max.xy().extend(light_aabb_view_min.z), |             .xy() | ||||||
|         light_aabb_view_max, |             .extend(clusterable_object_aabb_view_max.z), | ||||||
|  |         clusterable_object_aabb_view_max | ||||||
|  |             .xy() | ||||||
|  |             .extend(clusterable_object_aabb_view_min.z), | ||||||
|  |         clusterable_object_aabb_view_max, | ||||||
|     ); |     ); | ||||||
|     let ( |     let ( | ||||||
|         light_aabb_clip_xymin_near, |         clusterable_object_aabb_clip_xymin_near, | ||||||
|         light_aabb_clip_xymin_far, |         clusterable_object_aabb_clip_xymin_far, | ||||||
|         light_aabb_clip_xymax_near, |         clusterable_object_aabb_clip_xymax_near, | ||||||
|         light_aabb_clip_xymax_far, |         clusterable_object_aabb_clip_xymax_far, | ||||||
|     ) = ( |     ) = ( | ||||||
|         clip_from_view * light_aabb_view_xymin_near.extend(1.0), |         clip_from_view * clusterable_object_aabb_view_xymin_near.extend(1.0), | ||||||
|         clip_from_view * light_aabb_view_xymin_far.extend(1.0), |         clip_from_view * clusterable_object_aabb_view_xymin_far.extend(1.0), | ||||||
|         clip_from_view * light_aabb_view_xymax_near.extend(1.0), |         clip_from_view * clusterable_object_aabb_view_xymax_near.extend(1.0), | ||||||
|         clip_from_view * light_aabb_view_xymax_far.extend(1.0), |         clip_from_view * clusterable_object_aabb_view_xymax_far.extend(1.0), | ||||||
|     ); |     ); | ||||||
|     let ( |     let ( | ||||||
|         light_aabb_ndc_xymin_near, |         clusterable_object_aabb_ndc_xymin_near, | ||||||
|         light_aabb_ndc_xymin_far, |         clusterable_object_aabb_ndc_xymin_far, | ||||||
|         light_aabb_ndc_xymax_near, |         clusterable_object_aabb_ndc_xymax_near, | ||||||
|         light_aabb_ndc_xymax_far, |         clusterable_object_aabb_ndc_xymax_far, | ||||||
|     ) = ( |     ) = ( | ||||||
|         light_aabb_clip_xymin_near.xyz() / light_aabb_clip_xymin_near.w, |         clusterable_object_aabb_clip_xymin_near.xyz() / clusterable_object_aabb_clip_xymin_near.w, | ||||||
|         light_aabb_clip_xymin_far.xyz() / light_aabb_clip_xymin_far.w, |         clusterable_object_aabb_clip_xymin_far.xyz() / clusterable_object_aabb_clip_xymin_far.w, | ||||||
|         light_aabb_clip_xymax_near.xyz() / light_aabb_clip_xymax_near.w, |         clusterable_object_aabb_clip_xymax_near.xyz() / clusterable_object_aabb_clip_xymax_near.w, | ||||||
|         light_aabb_clip_xymax_far.xyz() / light_aabb_clip_xymax_far.w, |         clusterable_object_aabb_clip_xymax_far.xyz() / clusterable_object_aabb_clip_xymax_far.w, | ||||||
|     ); |     ); | ||||||
|     let (light_aabb_ndc_min, light_aabb_ndc_max) = ( |     let (clusterable_object_aabb_ndc_min, clusterable_object_aabb_ndc_max) = ( | ||||||
|         light_aabb_ndc_xymin_near |         clusterable_object_aabb_ndc_xymin_near | ||||||
|             .min(light_aabb_ndc_xymin_far) |             .min(clusterable_object_aabb_ndc_xymin_far) | ||||||
|             .min(light_aabb_ndc_xymax_near) |             .min(clusterable_object_aabb_ndc_xymax_near) | ||||||
|             .min(light_aabb_ndc_xymax_far), |             .min(clusterable_object_aabb_ndc_xymax_far), | ||||||
|         light_aabb_ndc_xymin_near |         clusterable_object_aabb_ndc_xymin_near | ||||||
|             .max(light_aabb_ndc_xymin_far) |             .max(clusterable_object_aabb_ndc_xymin_far) | ||||||
|             .max(light_aabb_ndc_xymax_near) |             .max(clusterable_object_aabb_ndc_xymax_near) | ||||||
|             .max(light_aabb_ndc_xymax_far), |             .max(clusterable_object_aabb_ndc_xymax_far), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     // clamp to ndc coords without depth
 |     // clamp to ndc coords without depth
 | ||||||
|     let (aabb_min_ndc, aabb_max_ndc) = ( |     let (aabb_min_ndc, aabb_max_ndc) = ( | ||||||
|         light_aabb_ndc_min.xy().clamp(NDC_MIN, NDC_MAX), |         clusterable_object_aabb_ndc_min.xy().clamp(NDC_MIN, NDC_MAX), | ||||||
|         light_aabb_ndc_max.xy().clamp(NDC_MIN, NDC_MAX), |         clusterable_object_aabb_ndc_max.xy().clamp(NDC_MIN, NDC_MAX), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     // pack unadjusted z depth into the vecs
 |     // pack unadjusted z depth into the vecs
 | ||||||
|     ( |     ( | ||||||
|         aabb_min_ndc.extend(light_aabb_view_min.z), |         aabb_min_ndc.extend(clusterable_object_aabb_view_min.z), | ||||||
|         aabb_max_ndc.extend(light_aabb_view_max.z), |         aabb_max_ndc.extend(clusterable_object_aabb_view_max.z), | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -903,7 +937,7 @@ fn get_distance_x(plane: HalfSpace, point: Vec3A, is_orthographic: bool) -> f32 | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NOTE: This exploits the fact that a z-plane normal has only a z component
 | // NOTE: This exploits the fact that a z-plane normal has only a z component
 | ||||||
| fn project_to_plane_z(z_light: Sphere, z_plane: HalfSpace) -> Option<Sphere> { | fn project_to_plane_z(z_object: Sphere, z_plane: HalfSpace) -> Option<Sphere> { | ||||||
|     // p = sphere center
 |     // p = sphere center
 | ||||||
|     // n = plane normal
 |     // n = plane normal
 | ||||||
|     // d = n.p if p is in the plane
 |     // d = n.p if p is in the plane
 | ||||||
| @ -912,35 +946,35 @@ fn project_to_plane_z(z_light: Sphere, z_plane: HalfSpace) -> Option<Sphere> { | |||||||
|     //   = pz * nz
 |     //   = pz * nz
 | ||||||
|     // => pz = d / nz
 |     // => pz = d / nz
 | ||||||
|     let z = z_plane.d() / z_plane.normal_d().z; |     let z = z_plane.d() / z_plane.normal_d().z; | ||||||
|     let distance_to_plane = z - z_light.center.z; |     let distance_to_plane = z - z_object.center.z; | ||||||
|     if distance_to_plane.abs() > z_light.radius { |     if distance_to_plane.abs() > z_object.radius { | ||||||
|         return None; |         return None; | ||||||
|     } |     } | ||||||
|     Some(Sphere { |     Some(Sphere { | ||||||
|         center: Vec3A::from(z_light.center.xy().extend(z)), |         center: Vec3A::from(z_object.center.xy().extend(z)), | ||||||
|         // hypotenuse length = radius
 |         // hypotenuse length = radius
 | ||||||
|         // pythagoras = (distance to plane)^2 + b^2 = radius^2
 |         // pythagoras = (distance to plane)^2 + b^2 = radius^2
 | ||||||
|         radius: (z_light.radius * z_light.radius - distance_to_plane * distance_to_plane).sqrt(), |         radius: (z_object.radius * z_object.radius - distance_to_plane * distance_to_plane).sqrt(), | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NOTE: This exploits the fact that a y-plane normal has only y and z components
 | // NOTE: This exploits the fact that a y-plane normal has only y and z components
 | ||||||
| fn project_to_plane_y( | fn project_to_plane_y( | ||||||
|     y_light: Sphere, |     y_object: Sphere, | ||||||
|     y_plane: HalfSpace, |     y_plane: HalfSpace, | ||||||
|     is_orthographic: bool, |     is_orthographic: bool, | ||||||
| ) -> Option<Sphere> { | ) -> Option<Sphere> { | ||||||
|     let distance_to_plane = if is_orthographic { |     let distance_to_plane = if is_orthographic { | ||||||
|         y_plane.d() - y_light.center.y |         y_plane.d() - y_object.center.y | ||||||
|     } else { |     } else { | ||||||
|         -y_light.center.yz().dot(y_plane.normal_d().yz()) |         -y_object.center.yz().dot(y_plane.normal_d().yz()) | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if distance_to_plane.abs() > y_light.radius { |     if distance_to_plane.abs() > y_object.radius { | ||||||
|         return None; |         return None; | ||||||
|     } |     } | ||||||
|     Some(Sphere { |     Some(Sphere { | ||||||
|         center: y_light.center + distance_to_plane * y_plane.normal(), |         center: y_object.center + distance_to_plane * y_plane.normal(), | ||||||
|         radius: (y_light.radius * y_light.radius - distance_to_plane * distance_to_plane).sqrt(), |         radius: (y_object.radius * y_object.radius - distance_to_plane * distance_to_plane).sqrt(), | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ use bevy_render::{ | |||||||
| }; | }; | ||||||
| use bevy_utils::{hashbrown::HashSet, tracing::warn}; | use bevy_utils::{hashbrown::HashSet, tracing::warn}; | ||||||
| 
 | 
 | ||||||
| pub(crate) use crate::cluster::assign::assign_lights_to_clusters; | pub(crate) use crate::cluster::assign::assign_objects_to_clusters; | ||||||
| use crate::MeshPipeline; | use crate::MeshPipeline; | ||||||
| 
 | 
 | ||||||
| mod assign; | mod assign; | ||||||
| @ -32,14 +32,14 @@ mod assign; | |||||||
| mod test; | mod test; | ||||||
| 
 | 
 | ||||||
| // NOTE: this must be kept in sync with the same constants in pbr.frag
 | // NOTE: this must be kept in sync with the same constants in pbr.frag
 | ||||||
| pub const MAX_UNIFORM_BUFFER_POINT_LIGHTS: usize = 256; | pub const MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS: usize = 256; | ||||||
| 
 | 
 | ||||||
| // NOTE: Clustered-forward rendering requires 3 storage buffer bindings so check that
 | // NOTE: Clustered-forward rendering requires 3 storage buffer bindings so check that
 | ||||||
| // at least that many are supported using this constant and SupportedBindingType::from_device()
 | // at least that many are supported using this constant and SupportedBindingType::from_device()
 | ||||||
| pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3; | pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3; | ||||||
| 
 | 
 | ||||||
| // this must match CLUSTER_COUNT_SIZE in pbr.wgsl
 | // this must match CLUSTER_COUNT_SIZE in pbr.wgsl
 | ||||||
| // and must be large enough to contain MAX_UNIFORM_BUFFER_POINT_LIGHTS
 | // and must be large enough to contain MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS
 | ||||||
| const CLUSTER_COUNT_SIZE: u32 = 9; | const CLUSTER_COUNT_SIZE: u32 = 9; | ||||||
| 
 | 
 | ||||||
| const CLUSTER_OFFSET_MASK: u32 = (1 << (32 - (CLUSTER_COUNT_SIZE * 2))) - 1; | const CLUSTER_OFFSET_MASK: u32 = (1 << (32 - (CLUSTER_COUNT_SIZE * 2))) - 1; | ||||||
| @ -58,11 +58,11 @@ const CLUSTER_COUNT_MASK: u32 = (1 << CLUSTER_COUNT_SIZE) - 1; | |||||||
| /// rendering
 | /// rendering
 | ||||||
| #[derive(Debug, Copy, Clone, Reflect)] | #[derive(Debug, Copy, Clone, Reflect)] | ||||||
| pub enum ClusterFarZMode { | pub enum ClusterFarZMode { | ||||||
|     /// Calculate the required maximum z-depth based on currently visible lights.
 |     /// Calculate the required maximum z-depth based on currently visible
 | ||||||
|     /// Makes better use of available clusters, speeding up GPU lighting operations
 |     /// clusterable objects.  Makes better use of available clusters, speeding
 | ||||||
|     /// at the expense of some CPU time and using more indices in the cluster light
 |     /// up GPU lighting operations at the expense of some CPU time and using
 | ||||||
|     /// index lists.
 |     /// more indices in the clusterable object index lists.
 | ||||||
|     MaxLightRange, |     MaxClusterableObjectRange, | ||||||
|     /// Constant max z-depth
 |     /// Constant max z-depth
 | ||||||
|     Constant(f32), |     Constant(f32), | ||||||
| } | } | ||||||
| @ -81,7 +81,7 @@ pub struct ClusterZConfig { | |||||||
| #[derive(Debug, Copy, Clone, Component, Reflect)] | #[derive(Debug, Copy, Clone, Component, Reflect)] | ||||||
| #[reflect(Component)] | #[reflect(Component)] | ||||||
| pub enum ClusterConfig { | pub enum ClusterConfig { | ||||||
|     /// Disable light cluster calculations for this view
 |     /// Disable cluster calculations for this view
 | ||||||
|     None, |     None, | ||||||
|     /// One single cluster. Optimal for low-light complexity scenes or scenes where
 |     /// One single cluster. Optimal for low-light complexity scenes or scenes where
 | ||||||
|     /// most lights affect the entire scene.
 |     /// most lights affect the entire scene.
 | ||||||
| @ -91,7 +91,7 @@ pub enum ClusterConfig { | |||||||
|         dimensions: UVec3, |         dimensions: UVec3, | ||||||
|         z_config: ClusterZConfig, |         z_config: ClusterZConfig, | ||||||
|         /// Specify if clusters should automatically resize in `X/Y` if there is a risk of exceeding
 |         /// Specify if clusters should automatically resize in `X/Y` if there is a risk of exceeding
 | ||||||
|         /// the available cluster-light index limit
 |         /// the available cluster-object index limit
 | ||||||
|         dynamic_resizing: bool, |         dynamic_resizing: bool, | ||||||
|     }, |     }, | ||||||
|     /// Fixed number of `Z` slices, `X` and `Y` calculated to give square clusters
 |     /// Fixed number of `Z` slices, `X` and `Y` calculated to give square clusters
 | ||||||
| @ -104,7 +104,7 @@ pub enum ClusterConfig { | |||||||
|         z_slices: u32, |         z_slices: u32, | ||||||
|         z_config: ClusterZConfig, |         z_config: ClusterZConfig, | ||||||
|         /// Specify if clusters should automatically resize in `X/Y` if there is a risk of exceeding
 |         /// Specify if clusters should automatically resize in `X/Y` if there is a risk of exceeding
 | ||||||
|         /// the available cluster-light index limit
 |         /// the available clusterable object index limit
 | ||||||
|         dynamic_resizing: bool, |         dynamic_resizing: bool, | ||||||
|     }, |     }, | ||||||
| } | } | ||||||
| @ -119,29 +119,29 @@ pub struct Clusters { | |||||||
|     /// and explicitly-configured to avoid having unnecessarily many slices close to the camera.
 |     /// and explicitly-configured to avoid having unnecessarily many slices close to the camera.
 | ||||||
|     pub(crate) near: f32, |     pub(crate) near: f32, | ||||||
|     pub(crate) far: f32, |     pub(crate) far: f32, | ||||||
|     pub(crate) lights: Vec<VisiblePointLights>, |     pub(crate) clusterable_objects: Vec<VisibleClusterableObjects>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Component, Debug, Default)] | #[derive(Clone, Component, Debug, Default)] | ||||||
| pub struct VisiblePointLights { | pub struct VisibleClusterableObjects { | ||||||
|     pub(crate) entities: Vec<Entity>, |     pub(crate) entities: Vec<Entity>, | ||||||
|     pub point_light_count: usize, |     pub point_light_count: usize, | ||||||
|     pub spot_light_count: usize, |     pub spot_light_count: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Resource, Default)] | #[derive(Resource, Default)] | ||||||
| pub struct GlobalVisiblePointLights { | pub struct GlobalVisibleClusterableObjects { | ||||||
|     pub(crate) entities: HashSet<Entity>, |     pub(crate) entities: HashSet<Entity>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Resource)] | #[derive(Resource)] | ||||||
| pub struct GlobalLightMeta { | pub struct GlobalClusterableObjectMeta { | ||||||
|     pub gpu_point_lights: GpuPointLights, |     pub gpu_clusterable_objects: GpuClusterableObjects, | ||||||
|     pub entity_to_index: EntityHashMap<usize>, |     pub entity_to_index: EntityHashMap<usize>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, ShaderType, Default, Debug)] | #[derive(Copy, Clone, ShaderType, Default, Debug)] | ||||||
| pub struct GpuPointLight { | pub struct GpuClusterableObject { | ||||||
|     // For point lights: the lower-right 2x2 values of the projection matrix [2][2] [2][3] [3][2] [3][3]
 |     // For point lights: the lower-right 2x2 values of the projection matrix [2][2] [2][3] [3][2] [3][3]
 | ||||||
|     // For spot lights: 2 components of the direction (x,z), spot_scale and spot_offset
 |     // For spot lights: 2 components of the direction (x,z), spot_scale and spot_offset
 | ||||||
|     pub(crate) light_custom_data: Vec4, |     pub(crate) light_custom_data: Vec4, | ||||||
| @ -153,20 +153,20 @@ pub struct GpuPointLight { | |||||||
|     pub(crate) spot_light_tan_angle: f32, |     pub(crate) spot_light_tan_angle: f32, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub enum GpuPointLights { | pub enum GpuClusterableObjects { | ||||||
|     Uniform(UniformBuffer<GpuPointLightsUniform>), |     Uniform(UniformBuffer<GpuClusterableObjectsUniform>), | ||||||
|     Storage(StorageBuffer<GpuPointLightsStorage>), |     Storage(StorageBuffer<GpuClusterableObjectsStorage>), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(ShaderType)] | #[derive(ShaderType)] | ||||||
| pub struct GpuPointLightsUniform { | pub struct GpuClusterableObjectsUniform { | ||||||
|     data: Box<[GpuPointLight; MAX_UNIFORM_BUFFER_POINT_LIGHTS]>, |     data: Box<[GpuClusterableObject; MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS]>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(ShaderType, Default)] | #[derive(ShaderType, Default)] | ||||||
| pub struct GpuPointLightsStorage { | pub struct GpuClusterableObjectsStorage { | ||||||
|     #[size(runtime)] |     #[size(runtime)] | ||||||
|     data: Vec<GpuPointLight>, |     data: Vec<GpuClusterableObject>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Component)] | #[derive(Component)] | ||||||
| @ -178,14 +178,14 @@ pub struct ExtractedClusterConfig { | |||||||
|     pub(crate) dimensions: UVec3, |     pub(crate) dimensions: UVec3, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| enum ExtractedClustersPointLightsElement { | enum ExtractedClusterableObjectElement { | ||||||
|     ClusterHeader(u32, u32), |     ClusterHeader(u32, u32), | ||||||
|     LightEntity(Entity), |     ClusterableObjectEntity(Entity), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Component)] | #[derive(Component)] | ||||||
| pub struct ExtractedClustersPointLights { | pub struct ExtractedClusterableObjects { | ||||||
|     data: Vec<ExtractedClustersPointLightsElement>, |     data: Vec<ExtractedClusterableObjectElement>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(ShaderType)] | #[derive(ShaderType)] | ||||||
| @ -194,7 +194,7 @@ struct GpuClusterOffsetsAndCountsUniform { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(ShaderType, Default)] | #[derive(ShaderType, Default)] | ||||||
| struct GpuClusterLightIndexListsStorage { | struct GpuClusterableObjectIndexListsStorage { | ||||||
|     #[size(runtime)] |     #[size(runtime)] | ||||||
|     data: Vec<u32>, |     data: Vec<u32>, | ||||||
| } | } | ||||||
| @ -208,12 +208,12 @@ struct GpuClusterOffsetsAndCountsStorage { | |||||||
| enum ViewClusterBuffers { | enum ViewClusterBuffers { | ||||||
|     Uniform { |     Uniform { | ||||||
|         // NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
 |         // NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
 | ||||||
|         cluster_light_index_lists: UniformBuffer<GpuClusterLightIndexListsUniform>, |         clusterable_object_index_lists: UniformBuffer<GpuClusterableObjectIndexListsUniform>, | ||||||
|         // NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
 |         // NOTE: UVec4 is because all arrays in Std140 layout have 16-byte alignment
 | ||||||
|         cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>, |         cluster_offsets_and_counts: UniformBuffer<GpuClusterOffsetsAndCountsUniform>, | ||||||
|     }, |     }, | ||||||
|     Storage { |     Storage { | ||||||
|         cluster_light_index_lists: StorageBuffer<GpuClusterLightIndexListsStorage>, |         clusterable_object_index_lists: StorageBuffer<GpuClusterableObjectIndexListsStorage>, | ||||||
|         cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>, |         cluster_offsets_and_counts: StorageBuffer<GpuClusterOffsetsAndCountsStorage>, | ||||||
|     }, |     }, | ||||||
| } | } | ||||||
| @ -229,7 +229,7 @@ impl Default for ClusterZConfig { | |||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             first_slice_depth: 5.0, |             first_slice_depth: 5.0, | ||||||
|             far_z_mode: ClusterFarZMode::MaxLightRange, |             far_z_mode: ClusterFarZMode::MaxClusterableObjectRange, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -297,7 +297,7 @@ impl ClusterConfig { | |||||||
|     fn far_z_mode(&self) -> ClusterFarZMode { |     fn far_z_mode(&self) -> ClusterFarZMode { | ||||||
|         match self { |         match self { | ||||||
|             ClusterConfig::None => ClusterFarZMode::Constant(0.0), |             ClusterConfig::None => ClusterFarZMode::Constant(0.0), | ||||||
|             ClusterConfig::Single => ClusterFarZMode::MaxLightRange, |             ClusterConfig::Single => ClusterFarZMode::MaxClusterableObjectRange, | ||||||
|             ClusterConfig::XYZ { z_config, .. } | ClusterConfig::FixedZ { z_config, .. } => { |             ClusterConfig::XYZ { z_config, .. } | ClusterConfig::FixedZ { z_config, .. } => { | ||||||
|                 z_config.far_z_mode |                 z_config.far_z_mode | ||||||
|             } |             } | ||||||
| @ -342,7 +342,7 @@ impl Clusters { | |||||||
|         self.dimensions = UVec3::ZERO; |         self.dimensions = UVec3::ZERO; | ||||||
|         self.near = 0.0; |         self.near = 0.0; | ||||||
|         self.far = 0.0; |         self.far = 0.0; | ||||||
|         self.lights.clear(); |         self.clusterable_objects.clear(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -356,14 +356,15 @@ pub fn add_clusters( | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let config = config.copied().unwrap_or_default(); |         let config = config.copied().unwrap_or_default(); | ||||||
|         // actual settings here don't matter - they will be overwritten in assign_lights_to_clusters
 |         // actual settings here don't matter - they will be overwritten in
 | ||||||
|  |         // `assign_objects_to_clusters``
 | ||||||
|         commands |         commands | ||||||
|             .entity(entity) |             .entity(entity) | ||||||
|             .insert((Clusters::default(), config)); |             .insert((Clusters::default(), config)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl VisiblePointLights { | impl VisibleClusterableObjects { | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn iter(&self) -> impl DoubleEndedIterator<Item = &Entity> { |     pub fn iter(&self) -> impl DoubleEndedIterator<Item = &Entity> { | ||||||
|         self.entities.iter() |         self.entities.iter() | ||||||
| @ -380,7 +381,7 @@ impl VisiblePointLights { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl GlobalVisiblePointLights { | impl GlobalVisibleClusterableObjects { | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn iter(&self) -> impl Iterator<Item = &Entity> { |     pub fn iter(&self) -> impl Iterator<Item = &Entity> { | ||||||
|         self.entities.iter() |         self.entities.iter() | ||||||
| @ -392,7 +393,7 @@ impl GlobalVisiblePointLights { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FromWorld for GlobalLightMeta { | impl FromWorld for GlobalClusterableObjectMeta { | ||||||
|     fn from_world(world: &mut World) -> Self { |     fn from_world(world: &mut World) -> Self { | ||||||
|         Self::new( |         Self::new( | ||||||
|             world |             world | ||||||
| @ -402,16 +403,16 @@ impl FromWorld for GlobalLightMeta { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl GlobalLightMeta { | impl GlobalClusterableObjectMeta { | ||||||
|     pub fn new(buffer_binding_type: BufferBindingType) -> Self { |     pub fn new(buffer_binding_type: BufferBindingType) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             gpu_point_lights: GpuPointLights::new(buffer_binding_type), |             gpu_clusterable_objects: GpuClusterableObjects::new(buffer_binding_type), | ||||||
|             entity_to_index: EntityHashMap::default(), |             entity_to_index: EntityHashMap::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl GpuPointLights { | impl GpuClusterableObjects { | ||||||
|     fn new(buffer_binding_type: BufferBindingType) -> Self { |     fn new(buffer_binding_type: BufferBindingType) -> Self { | ||||||
|         match buffer_binding_type { |         match buffer_binding_type { | ||||||
|             BufferBindingType::Storage { .. } => Self::storage(), |             BufferBindingType::Storage { .. } => Self::storage(), | ||||||
| @ -427,17 +428,19 @@ impl GpuPointLights { | |||||||
|         Self::Storage(StorageBuffer::default()) |         Self::Storage(StorageBuffer::default()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub(crate) fn set(&mut self, mut lights: Vec<GpuPointLight>) { |     pub(crate) fn set(&mut self, mut clusterable_objects: Vec<GpuClusterableObject>) { | ||||||
|         match self { |         match self { | ||||||
|             GpuPointLights::Uniform(buffer) => { |             GpuClusterableObjects::Uniform(buffer) => { | ||||||
|                 let len = lights.len().min(MAX_UNIFORM_BUFFER_POINT_LIGHTS); |                 let len = clusterable_objects | ||||||
|                 let src = &lights[..len]; |                     .len() | ||||||
|  |                     .min(MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS); | ||||||
|  |                 let src = &clusterable_objects[..len]; | ||||||
|                 let dst = &mut buffer.get_mut().data[..len]; |                 let dst = &mut buffer.get_mut().data[..len]; | ||||||
|                 dst.copy_from_slice(src); |                 dst.copy_from_slice(src); | ||||||
|             } |             } | ||||||
|             GpuPointLights::Storage(buffer) => { |             GpuClusterableObjects::Storage(buffer) => { | ||||||
|                 buffer.get_mut().data.clear(); |                 buffer.get_mut().data.clear(); | ||||||
|                 buffer.get_mut().data.append(&mut lights); |                 buffer.get_mut().data.append(&mut clusterable_objects); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -448,41 +451,54 @@ impl GpuPointLights { | |||||||
|         render_queue: &RenderQueue, |         render_queue: &RenderQueue, | ||||||
|     ) { |     ) { | ||||||
|         match self { |         match self { | ||||||
|             GpuPointLights::Uniform(buffer) => buffer.write_buffer(render_device, render_queue), |             GpuClusterableObjects::Uniform(buffer) => { | ||||||
|             GpuPointLights::Storage(buffer) => buffer.write_buffer(render_device, render_queue), |                 buffer.write_buffer(render_device, render_queue); | ||||||
|  |             } | ||||||
|  |             GpuClusterableObjects::Storage(buffer) => { | ||||||
|  |                 buffer.write_buffer(render_device, render_queue); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn binding(&self) -> Option<BindingResource> { |     pub fn binding(&self) -> Option<BindingResource> { | ||||||
|         match self { |         match self { | ||||||
|             GpuPointLights::Uniform(buffer) => buffer.binding(), |             GpuClusterableObjects::Uniform(buffer) => buffer.binding(), | ||||||
|             GpuPointLights::Storage(buffer) => buffer.binding(), |             GpuClusterableObjects::Storage(buffer) => buffer.binding(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZeroU64 { |     pub fn min_size(buffer_binding_type: BufferBindingType) -> NonZeroU64 { | ||||||
|         match buffer_binding_type { |         match buffer_binding_type { | ||||||
|             BufferBindingType::Storage { .. } => GpuPointLightsStorage::min_size(), |             BufferBindingType::Storage { .. } => GpuClusterableObjectsStorage::min_size(), | ||||||
|             BufferBindingType::Uniform => GpuPointLightsUniform::min_size(), |             BufferBindingType::Uniform => GpuClusterableObjectsUniform::min_size(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Default for GpuPointLightsUniform { | impl Default for GpuClusterableObjectsUniform { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             data: Box::new([GpuPointLight::default(); MAX_UNIFORM_BUFFER_POINT_LIGHTS]), |             data: Box::new( | ||||||
|  |                 [GpuClusterableObject::default(); MAX_UNIFORM_BUFFER_CLUSTERABLE_OBJECTS], | ||||||
|  |             ), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[allow(clippy::too_many_arguments)] | #[allow(clippy::too_many_arguments)] | ||||||
| // Sort lights by
 | // Sort clusterable objects by:
 | ||||||
| // - point-light vs spot-light, so that we can iterate point lights and spot lights in contiguous blocks in the fragment shader,
 | //
 | ||||||
| // - then those with shadows enabled first, so that the index can be used to render at most `point_light_shadow_maps_count`
 | // * point-light vs spot-light, so that we can iterate point lights and spot
 | ||||||
| //   point light shadows and `spot_light_shadow_maps_count` spot light shadow maps,
 | //   lights in contiguous blocks in the fragment shader,
 | ||||||
| // - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
 | //
 | ||||||
| pub(crate) fn point_light_order( | // * then those with shadows enabled first, so that the index can be used to
 | ||||||
|  | //   render at most `point_light_shadow_maps_count` point light shadows and
 | ||||||
|  | //   `spot_light_shadow_maps_count` spot light shadow maps,
 | ||||||
|  | //
 | ||||||
|  | // * then by entity as a stable key to ensure that a consistent set of
 | ||||||
|  | //   clusterable objects are chosen if the clusterable object count limit is
 | ||||||
|  | //   exceeded.
 | ||||||
|  | pub(crate) fn clusterable_object_order( | ||||||
|     (entity_1, shadows_enabled_1, is_spot_light_1): (&Entity, &bool, &bool), |     (entity_1, shadows_enabled_1, is_spot_light_1): (&Entity, &bool, &bool), | ||||||
|     (entity_2, shadows_enabled_2, is_spot_light_2): (&Entity, &bool, &bool), |     (entity_2, shadows_enabled_2, is_spot_light_2): (&Entity, &bool, &bool), | ||||||
| ) -> std::cmp::Ordering { | ) -> std::cmp::Ordering { | ||||||
| @ -502,20 +518,26 @@ pub fn extract_clusters( | |||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let num_entities: usize = clusters.lights.iter().map(|l| l.entities.len()).sum(); |         let num_entities: usize = clusters | ||||||
|         let mut data = Vec::with_capacity(clusters.lights.len() + num_entities); |             .clusterable_objects | ||||||
|         for cluster_lights in &clusters.lights { |             .iter() | ||||||
|             data.push(ExtractedClustersPointLightsElement::ClusterHeader( |             .map(|l| l.entities.len()) | ||||||
|                 cluster_lights.point_light_count as u32, |             .sum(); | ||||||
|                 cluster_lights.spot_light_count as u32, |         let mut data = Vec::with_capacity(clusters.clusterable_objects.len() + num_entities); | ||||||
|  |         for cluster_objects in &clusters.clusterable_objects { | ||||||
|  |             data.push(ExtractedClusterableObjectElement::ClusterHeader( | ||||||
|  |                 cluster_objects.point_light_count as u32, | ||||||
|  |                 cluster_objects.spot_light_count as u32, | ||||||
|             )); |             )); | ||||||
|             for l in &cluster_lights.entities { |             for clusterable_entity in &cluster_objects.entities { | ||||||
|                 data.push(ExtractedClustersPointLightsElement::LightEntity(*l)); |                 data.push(ExtractedClusterableObjectElement::ClusterableObjectEntity( | ||||||
|  |                     *clusterable_entity, | ||||||
|  |                 )); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         commands.get_or_spawn(entity).insert(( |         commands.get_or_spawn(entity).insert(( | ||||||
|             ExtractedClustersPointLights { data }, |             ExtractedClusterableObjects { data }, | ||||||
|             ExtractedClusterConfig { |             ExtractedClusterConfig { | ||||||
|                 near: clusters.near, |                 near: clusters.near, | ||||||
|                 far: clusters.far, |                 far: clusters.far, | ||||||
| @ -530,8 +552,8 @@ pub fn prepare_clusters( | |||||||
|     render_device: Res<RenderDevice>, |     render_device: Res<RenderDevice>, | ||||||
|     render_queue: Res<RenderQueue>, |     render_queue: Res<RenderQueue>, | ||||||
|     mesh_pipeline: Res<MeshPipeline>, |     mesh_pipeline: Res<MeshPipeline>, | ||||||
|     global_light_meta: Res<GlobalLightMeta>, |     global_clusterable_object_meta: Res<GlobalClusterableObjectMeta>, | ||||||
|     views: Query<(Entity, &ExtractedClustersPointLights)>, |     views: Query<(Entity, &ExtractedClusterableObjects)>, | ||||||
| ) { | ) { | ||||||
|     let render_device = render_device.into_inner(); |     let render_device = render_device.into_inner(); | ||||||
|     let supports_storage_buffers = matches!( |     let supports_storage_buffers = matches!( | ||||||
| @ -545,7 +567,7 @@ pub fn prepare_clusters( | |||||||
| 
 | 
 | ||||||
|         for record in &extracted_clusters.data { |         for record in &extracted_clusters.data { | ||||||
|             match record { |             match record { | ||||||
|                 ExtractedClustersPointLightsElement::ClusterHeader( |                 ExtractedClusterableObjectElement::ClusterHeader( | ||||||
|                     point_light_count, |                     point_light_count, | ||||||
|                     spot_light_count, |                     spot_light_count, | ||||||
|                 ) => { |                 ) => { | ||||||
| @ -556,15 +578,20 @@ pub fn prepare_clusters( | |||||||
|                         *spot_light_count as usize, |                         *spot_light_count as usize, | ||||||
|                     ); |                     ); | ||||||
|                 } |                 } | ||||||
|                 ExtractedClustersPointLightsElement::LightEntity(entity) => { |                 ExtractedClusterableObjectElement::ClusterableObjectEntity(entity) => { | ||||||
|                     if let Some(light_index) = global_light_meta.entity_to_index.get(entity) { |                     if let Some(clusterable_object_index) = | ||||||
|  |                         global_clusterable_object_meta.entity_to_index.get(entity) | ||||||
|  |                     { | ||||||
|                         if view_clusters_bindings.n_indices() >= ViewClusterBindings::MAX_INDICES |                         if view_clusters_bindings.n_indices() >= ViewClusterBindings::MAX_INDICES | ||||||
|                             && !supports_storage_buffers |                             && !supports_storage_buffers | ||||||
|                         { |                         { | ||||||
|                             warn!("Cluster light index lists is full! The PointLights in the view are affecting too many clusters."); |                             warn!( | ||||||
|  |                                 "Clusterable object index lists are full! The clusterable \ | ||||||
|  |                                  objects in the view are present in too many clusters." | ||||||
|  |                             ); | ||||||
|                             break; |                             break; | ||||||
|                         } |                         } | ||||||
|                         view_clusters_bindings.push_index(*light_index); |                         view_clusters_bindings.push_index(*clusterable_object_index); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -592,18 +619,19 @@ impl ViewClusterBindings { | |||||||
|     pub fn clear(&mut self) { |     pub fn clear(&mut self) { | ||||||
|         match &mut self.buffers { |         match &mut self.buffers { | ||||||
|             ViewClusterBuffers::Uniform { |             ViewClusterBuffers::Uniform { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 cluster_offsets_and_counts, |                 cluster_offsets_and_counts, | ||||||
|             } => { |             } => { | ||||||
|                 *cluster_light_index_lists.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS]; |                 *clusterable_object_index_lists.get_mut().data = | ||||||
|  |                     [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS]; | ||||||
|                 *cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS]; |                 *cluster_offsets_and_counts.get_mut().data = [UVec4::ZERO; Self::MAX_UNIFORM_ITEMS]; | ||||||
|             } |             } | ||||||
|             ViewClusterBuffers::Storage { |             ViewClusterBuffers::Storage { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 cluster_offsets_and_counts, |                 cluster_offsets_and_counts, | ||||||
|                 .. |                 .. | ||||||
|             } => { |             } => { | ||||||
|                 cluster_light_index_lists.get_mut().data.clear(); |                 clusterable_object_index_lists.get_mut().data.clear(); | ||||||
|                 cluster_offsets_and_counts.get_mut().data.clear(); |                 cluster_offsets_and_counts.get_mut().data.clear(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -648,7 +676,7 @@ impl ViewClusterBindings { | |||||||
|     pub fn push_index(&mut self, index: usize) { |     pub fn push_index(&mut self, index: usize) { | ||||||
|         match &mut self.buffers { |         match &mut self.buffers { | ||||||
|             ViewClusterBuffers::Uniform { |             ViewClusterBuffers::Uniform { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 .. |                 .. | ||||||
|             } => { |             } => { | ||||||
|                 let array_index = self.n_indices >> 4; // >> 4 is equivalent to / 16
 |                 let array_index = self.n_indices >> 4; // >> 4 is equivalent to / 16
 | ||||||
| @ -656,14 +684,17 @@ impl ViewClusterBindings { | |||||||
|                 let sub_index = self.n_indices & ((1 << 2) - 1); |                 let sub_index = self.n_indices & ((1 << 2) - 1); | ||||||
|                 let index = index as u32; |                 let index = index as u32; | ||||||
| 
 | 
 | ||||||
|                 cluster_light_index_lists.get_mut().data[array_index][component] |= |                 clusterable_object_index_lists.get_mut().data[array_index][component] |= | ||||||
|                     index << (8 * sub_index); |                     index << (8 * sub_index); | ||||||
|             } |             } | ||||||
|             ViewClusterBuffers::Storage { |             ViewClusterBuffers::Storage { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 .. |                 .. | ||||||
|             } => { |             } => { | ||||||
|                 cluster_light_index_lists.get_mut().data.push(index as u32); |                 clusterable_object_index_lists | ||||||
|  |                     .get_mut() | ||||||
|  |                     .data | ||||||
|  |                     .push(index as u32); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -673,32 +704,32 @@ impl ViewClusterBindings { | |||||||
|     pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) { |     pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) { | ||||||
|         match &mut self.buffers { |         match &mut self.buffers { | ||||||
|             ViewClusterBuffers::Uniform { |             ViewClusterBuffers::Uniform { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 cluster_offsets_and_counts, |                 cluster_offsets_and_counts, | ||||||
|             } => { |             } => { | ||||||
|                 cluster_light_index_lists.write_buffer(render_device, render_queue); |                 clusterable_object_index_lists.write_buffer(render_device, render_queue); | ||||||
|                 cluster_offsets_and_counts.write_buffer(render_device, render_queue); |                 cluster_offsets_and_counts.write_buffer(render_device, render_queue); | ||||||
|             } |             } | ||||||
|             ViewClusterBuffers::Storage { |             ViewClusterBuffers::Storage { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 cluster_offsets_and_counts, |                 cluster_offsets_and_counts, | ||||||
|             } => { |             } => { | ||||||
|                 cluster_light_index_lists.write_buffer(render_device, render_queue); |                 clusterable_object_index_lists.write_buffer(render_device, render_queue); | ||||||
|                 cluster_offsets_and_counts.write_buffer(render_device, render_queue); |                 cluster_offsets_and_counts.write_buffer(render_device, render_queue); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn light_index_lists_binding(&self) -> Option<BindingResource> { |     pub fn clusterable_object_index_lists_binding(&self) -> Option<BindingResource> { | ||||||
|         match &self.buffers { |         match &self.buffers { | ||||||
|             ViewClusterBuffers::Uniform { |             ViewClusterBuffers::Uniform { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 .. |                 .. | ||||||
|             } => cluster_light_index_lists.binding(), |             } => clusterable_object_index_lists.binding(), | ||||||
|             ViewClusterBuffers::Storage { |             ViewClusterBuffers::Storage { | ||||||
|                 cluster_light_index_lists, |                 clusterable_object_index_lists, | ||||||
|                 .. |                 .. | ||||||
|             } => cluster_light_index_lists.binding(), |             } => clusterable_object_index_lists.binding(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -715,12 +746,12 @@ impl ViewClusterBindings { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn min_size_cluster_light_index_lists( |     pub fn min_size_clusterable_object_index_lists( | ||||||
|         buffer_binding_type: BufferBindingType, |         buffer_binding_type: BufferBindingType, | ||||||
|     ) -> NonZeroU64 { |     ) -> NonZeroU64 { | ||||||
|         match buffer_binding_type { |         match buffer_binding_type { | ||||||
|             BufferBindingType::Storage { .. } => GpuClusterLightIndexListsStorage::min_size(), |             BufferBindingType::Storage { .. } => GpuClusterableObjectIndexListsStorage::min_size(), | ||||||
|             BufferBindingType::Uniform => GpuClusterLightIndexListsUniform::min_size(), |             BufferBindingType::Uniform => GpuClusterableObjectIndexListsUniform::min_size(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -744,21 +775,21 @@ impl ViewClusterBuffers { | |||||||
| 
 | 
 | ||||||
|     fn uniform() -> Self { |     fn uniform() -> Self { | ||||||
|         ViewClusterBuffers::Uniform { |         ViewClusterBuffers::Uniform { | ||||||
|             cluster_light_index_lists: UniformBuffer::default(), |             clusterable_object_index_lists: UniformBuffer::default(), | ||||||
|             cluster_offsets_and_counts: UniformBuffer::default(), |             cluster_offsets_and_counts: UniformBuffer::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn storage() -> Self { |     fn storage() -> Self { | ||||||
|         ViewClusterBuffers::Storage { |         ViewClusterBuffers::Storage { | ||||||
|             cluster_light_index_lists: StorageBuffer::default(), |             clusterable_object_index_lists: StorageBuffer::default(), | ||||||
|             cluster_offsets_and_counts: StorageBuffer::default(), |             cluster_offsets_and_counts: StorageBuffer::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NOTE: With uniform buffer max binding size as 16384 bytes
 | // NOTE: With uniform buffer max binding size as 16384 bytes
 | ||||||
| // that means we can fit 256 point lights in one uniform
 | // that means we can fit 256 clusterable objects in one uniform
 | ||||||
| // buffer, which means the count can be at most 256 so it
 | // buffer, which means the count can be at most 256 so it
 | ||||||
| // needs 9 bits.
 | // needs 9 bits.
 | ||||||
| // The array of indices can also use u8 and that means the
 | // The array of indices can also use u8 and that means the
 | ||||||
| @ -778,15 +809,15 @@ fn pack_offset_and_counts(offset: usize, point_count: usize, spot_count: usize) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(ShaderType)] | #[derive(ShaderType)] | ||||||
| struct GpuClusterLightIndexListsUniform { | struct GpuClusterableObjectIndexListsUniform { | ||||||
|     data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>, |     data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NOTE: Assert at compile time that GpuClusterLightIndexListsUniform
 | // NOTE: Assert at compile time that GpuClusterableObjectIndexListsUniform
 | ||||||
| // fits within the maximum uniform buffer binding size
 | // fits within the maximum uniform buffer binding size
 | ||||||
| const _: () = assert!(GpuClusterLightIndexListsUniform::SHADER_SIZE.get() <= 16384); | const _: () = assert!(GpuClusterableObjectIndexListsUniform::SHADER_SIZE.get() <= 16384); | ||||||
| 
 | 
 | ||||||
| impl Default for GpuClusterLightIndexListsUniform { | impl Default for GpuClusterableObjectIndexListsUniform { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]), |             data: Box::new([UVec4::ZERO; ViewClusterBindings::MAX_UNIFORM_ITEMS]), | ||||||
|  | |||||||
| @ -298,7 +298,7 @@ impl Plugin for PbrPlugin { | |||||||
|             .register_type::<FogSettings>() |             .register_type::<FogSettings>() | ||||||
|             .register_type::<ShadowFilteringMethod>() |             .register_type::<ShadowFilteringMethod>() | ||||||
|             .init_resource::<AmbientLight>() |             .init_resource::<AmbientLight>() | ||||||
|             .init_resource::<GlobalVisiblePointLights>() |             .init_resource::<GlobalVisibleClusterableObjects>() | ||||||
|             .init_resource::<DirectionalLightShadowMap>() |             .init_resource::<DirectionalLightShadowMap>() | ||||||
|             .init_resource::<PointLightShadowMap>() |             .init_resource::<PointLightShadowMap>() | ||||||
|             .register_type::<DefaultOpaqueRendererMethod>() |             .register_type::<DefaultOpaqueRendererMethod>() | ||||||
| @ -339,7 +339,7 @@ impl Plugin for PbrPlugin { | |||||||
|                 PostUpdate, |                 PostUpdate, | ||||||
|                 ( |                 ( | ||||||
|                     add_clusters.in_set(SimulationLightSystems::AddClusters), |                     add_clusters.in_set(SimulationLightSystems::AddClusters), | ||||||
|                     crate::assign_lights_to_clusters |                     crate::assign_objects_to_clusters | ||||||
|                         .in_set(SimulationLightSystems::AssignLightsToClusters) |                         .in_set(SimulationLightSystems::AssignLightsToClusters) | ||||||
|                         .after(TransformSystem::TransformPropagate) |                         .after(TransformSystem::TransformPropagate) | ||||||
|                         .after(VisibilitySystems::CheckVisibility) |                         .after(VisibilitySystems::CheckVisibility) | ||||||
| @ -427,7 +427,7 @@ impl Plugin for PbrPlugin { | |||||||
|         // Extract the required data from the main world
 |         // Extract the required data from the main world
 | ||||||
|         render_app |         render_app | ||||||
|             .init_resource::<ShadowSamplers>() |             .init_resource::<ShadowSamplers>() | ||||||
|             .init_resource::<GlobalLightMeta>(); |             .init_resource::<GlobalClusterableObjectMeta>(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -561,7 +561,7 @@ pub fn update_directional_light_frusta( | |||||||
| 
 | 
 | ||||||
| // NOTE: Run this after assign_lights_to_clusters!
 | // NOTE: Run this after assign_lights_to_clusters!
 | ||||||
| pub fn update_point_light_frusta( | pub fn update_point_light_frusta( | ||||||
|     global_lights: Res<GlobalVisiblePointLights>, |     global_lights: Res<GlobalVisibleClusterableObjects>, | ||||||
|     mut views: Query< |     mut views: Query< | ||||||
|         (Entity, &GlobalTransform, &PointLight, &mut CubemapFrusta), |         (Entity, &GlobalTransform, &PointLight, &mut CubemapFrusta), | ||||||
|         Or<(Changed<GlobalTransform>, Changed<PointLight>)>, |         Or<(Changed<GlobalTransform>, Changed<PointLight>)>, | ||||||
| @ -605,7 +605,7 @@ pub fn update_point_light_frusta( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn update_spot_light_frusta( | pub fn update_spot_light_frusta( | ||||||
|     global_lights: Res<GlobalVisiblePointLights>, |     global_lights: Res<GlobalVisibleClusterableObjects>, | ||||||
|     mut views: Query< |     mut views: Query< | ||||||
|         (Entity, &GlobalTransform, &SpotLight, &mut Frustum), |         (Entity, &GlobalTransform, &SpotLight, &mut Frustum), | ||||||
|         Or<(Changed<GlobalTransform>, Changed<SpotLight>)>, |         Or<(Changed<GlobalTransform>, Changed<SpotLight>)>, | ||||||
| @ -639,7 +639,7 @@ pub fn update_spot_light_frusta( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn check_light_mesh_visibility( | pub fn check_light_mesh_visibility( | ||||||
|     visible_point_lights: Query<&VisiblePointLights>, |     visible_point_lights: Query<&VisibleClusterableObjects>, | ||||||
|     mut point_lights: Query<( |     mut point_lights: Query<( | ||||||
|         &PointLight, |         &PointLight, | ||||||
|         &GlobalTransform, |         &GlobalTransform, | ||||||
|  | |||||||
| @ -53,13 +53,14 @@ fn unpack_offset_and_counts(cluster_index: u32) -> vec3<u32> { | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn get_light_id(index: u32) -> u32 { | fn get_clusterable_object_id(index: u32) -> u32 { | ||||||
| #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 | #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 | ||||||
|     return bindings::cluster_light_index_lists.data[index]; |     return bindings::clusterable_object_index_lists.data[index]; | ||||||
| #else | #else | ||||||
|     // The index is correct but in cluster_light_index_lists we pack 4 u8s into a u32 |     // The index is correct but in clusterable_object_index_lists we pack 4 u8s into a u32 | ||||||
|     // This means the index into cluster_light_index_lists is index / 4 |     // This means the index into clusterable_object_index_lists is index / 4 | ||||||
|     let indices = bindings::cluster_light_index_lists.data[index >> 4u][(index >> 2u) & ((1u << 2u) - 1u)]; |     let indices = bindings::clusterable_object_index_lists.data[index >> 4u][(index >> 2u) & | ||||||
|  |         ((1u << 2u) - 1u)]; | ||||||
|     // And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index |     // And index % 4 gives the sub-index of the u8 within the u32 so we shift by 8 * sub-index | ||||||
|     return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u); |     return (indices >> (8u * (index & ((1u << 2u) - 1u)))) & ((1u << 8u) - 1u); | ||||||
| #endif | #endif | ||||||
| @ -93,14 +94,23 @@ fn cluster_debug_visualization( | |||||||
|         output_color.a |         output_color.a | ||||||
|     ); |     ); | ||||||
| #endif // CLUSTERED_FORWARD_DEBUG_Z_SLICES | #endif // CLUSTERED_FORWARD_DEBUG_Z_SLICES | ||||||
| #ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_LIGHT_COMPLEXITY | #ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_COMPLEXITY | ||||||
|     // NOTE: This debug mode visualises the number of lights within the cluster that contains |     // NOTE: This debug mode visualises the number of clusterable objects within | ||||||
|     // the fragment. It shows a sort of lighting complexity measure. |     // the cluster that contains the fragment. It shows a sort of cluster | ||||||
|  |     // complexity measure. | ||||||
|     let cluster_overlay_alpha = 0.1; |     let cluster_overlay_alpha = 0.1; | ||||||
|     let max_light_complexity_per_cluster = 64.0; |     let max_complexity_per_cluster = 64.0; | ||||||
|     output_color.r = (1.0 - cluster_overlay_alpha) * output_color.r + cluster_overlay_alpha * smoothStep(0.0, max_light_complexity_per_cluster, f32(offset_and_counts[1] + offset_and_counts[2])); |     output_color.r = (1.0 - cluster_overlay_alpha) * output_color.r + cluster_overlay_alpha * | ||||||
|     output_color.g = (1.0 - cluster_overlay_alpha) * output_color.g + cluster_overlay_alpha * (1.0 - smoothStep(0.0, max_light_complexity_per_cluster, f32(offset_and_counts[1] + offset_and_counts[2]))); |         smoothStep( | ||||||
| #endif // CLUSTERED_FORWARD_DEBUG_CLUSTER_LIGHT_COMPLEXITY |             0.0, | ||||||
|  |             max_complexity_per_cluster, | ||||||
|  |             f32(offset_and_counts[1] + offset_and_counts[2])); | ||||||
|  |     output_color.g = (1.0 - cluster_overlay_alpha) * output_color.g + cluster_overlay_alpha * | ||||||
|  |         (1.0 - smoothStep( | ||||||
|  |             0.0, | ||||||
|  |             max_complexity_per_cluster, | ||||||
|  |             f32(offset_and_counts[1] + offset_and_counts[2]))); | ||||||
|  | #endif // CLUSTERED_FORWARD_DEBUG_CLUSTER_COMPLEXITY | ||||||
| #ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY | #ifdef CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY | ||||||
|     // NOTE: Visualizes the cluster to which the fragment belongs |     // NOTE: Visualizes the cluster to which the fragment belongs | ||||||
|     let cluster_overlay_alpha = 0.1; |     let cluster_overlay_alpha = 0.1; | ||||||
|  | |||||||
| @ -173,7 +173,7 @@ pub fn extract_lights( | |||||||
|     mut commands: Commands, |     mut commands: Commands, | ||||||
|     point_light_shadow_map: Extract<Res<PointLightShadowMap>>, |     point_light_shadow_map: Extract<Res<PointLightShadowMap>>, | ||||||
|     directional_light_shadow_map: Extract<Res<DirectionalLightShadowMap>>, |     directional_light_shadow_map: Extract<Res<DirectionalLightShadowMap>>, | ||||||
|     global_point_lights: Extract<Res<GlobalVisiblePointLights>>, |     global_point_lights: Extract<Res<GlobalVisibleClusterableObjects>>, | ||||||
|     point_lights: Extract< |     point_lights: Extract< | ||||||
|         Query<( |         Query<( | ||||||
|             &PointLight, |             &PointLight, | ||||||
| @ -513,7 +513,7 @@ pub fn prepare_lights( | |||||||
|     mut texture_cache: ResMut<TextureCache>, |     mut texture_cache: ResMut<TextureCache>, | ||||||
|     render_device: Res<RenderDevice>, |     render_device: Res<RenderDevice>, | ||||||
|     render_queue: Res<RenderQueue>, |     render_queue: Res<RenderQueue>, | ||||||
|     mut global_light_meta: ResMut<GlobalLightMeta>, |     mut global_light_meta: ResMut<GlobalClusterableObjectMeta>, | ||||||
|     mut light_meta: ResMut<LightMeta>, |     mut light_meta: ResMut<LightMeta>, | ||||||
|     views: Query<( |     views: Query<( | ||||||
|         Entity, |         Entity, | ||||||
| @ -634,7 +634,7 @@ pub fn prepare_lights( | |||||||
|     //   point light shadows and `spot_light_shadow_maps_count` spot light shadow maps,
 |     //   point light shadows and `spot_light_shadow_maps_count` spot light shadow maps,
 | ||||||
|     // - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
 |     // - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
 | ||||||
|     point_lights.sort_by(|(entity_1, light_1, _), (entity_2, light_2, _)| { |     point_lights.sort_by(|(entity_1, light_1, _), (entity_2, light_2, _)| { | ||||||
|         crate::cluster::point_light_order( |         crate::cluster::clusterable_object_order( | ||||||
|             ( |             ( | ||||||
|                 entity_1, |                 entity_1, | ||||||
|                 &light_1.shadows_enabled, |                 &light_1.shadows_enabled, | ||||||
| @ -714,7 +714,7 @@ pub fn prepare_lights( | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         gpu_point_lights.push(GpuPointLight { |         gpu_point_lights.push(GpuClusterableObject { | ||||||
|             light_custom_data, |             light_custom_data, | ||||||
|             // premultiply color by intensity
 |             // premultiply color by intensity
 | ||||||
|             // we don't use the alpha at all, so no reason to multiply only [0..3]
 |             // we don't use the alpha at all, so no reason to multiply only [0..3]
 | ||||||
| @ -779,9 +779,11 @@ pub fn prepare_lights( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     global_light_meta.gpu_point_lights.set(gpu_point_lights); |  | ||||||
|     global_light_meta |     global_light_meta | ||||||
|         .gpu_point_lights |         .gpu_clusterable_objects | ||||||
|  |         .set(gpu_point_lights); | ||||||
|  |     global_light_meta | ||||||
|  |         .gpu_clusterable_objects | ||||||
|         .write_buffer(&render_device, &render_queue); |         .write_buffer(&render_device, &render_queue); | ||||||
| 
 | 
 | ||||||
|     live_shadow_mapping_lights.clear(); |     live_shadow_mapping_lights.clear(); | ||||||
|  | |||||||
| @ -41,9 +41,9 @@ use crate::{ | |||||||
|         self, IrradianceVolume, RenderViewIrradianceVolumeBindGroupEntries, |         self, IrradianceVolume, RenderViewIrradianceVolumeBindGroupEntries, | ||||||
|         IRRADIANCE_VOLUMES_ARE_USABLE, |         IRRADIANCE_VOLUMES_ARE_USABLE, | ||||||
|     }, |     }, | ||||||
|     prepass, FogMeta, GlobalLightMeta, GpuFog, GpuLights, GpuPointLights, LightMeta, |     prepass, FogMeta, GlobalClusterableObjectMeta, GpuClusterableObjects, GpuFog, GpuLights, | ||||||
|     LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, RenderViewLightProbes, |     LightMeta, LightProbesBuffer, LightProbesUniform, MeshPipeline, MeshPipelineKey, | ||||||
|     ScreenSpaceAmbientOcclusionTextures, ScreenSpaceReflectionsBuffer, |     RenderViewLightProbes, ScreenSpaceAmbientOcclusionTextures, ScreenSpaceReflectionsBuffer, | ||||||
|     ScreenSpaceReflectionsUniform, ShadowSamplers, ViewClusterBindings, ViewShadowBindings, |     ScreenSpaceReflectionsUniform, ShadowSamplers, ViewClusterBindings, ViewShadowBindings, | ||||||
|     CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, |     CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, | ||||||
| }; | }; | ||||||
| @ -229,13 +229,13 @@ fn layout_entries( | |||||||
|             ), |             ), | ||||||
|             // Directional Shadow Texture Array Sampler
 |             // Directional Shadow Texture Array Sampler
 | ||||||
|             (5, sampler(SamplerBindingType::Comparison)), |             (5, sampler(SamplerBindingType::Comparison)), | ||||||
|             // PointLights
 |             // ClusterableObjects
 | ||||||
|             ( |             ( | ||||||
|                 6, |                 6, | ||||||
|                 buffer_layout( |                 buffer_layout( | ||||||
|                     clustered_forward_buffer_binding_type, |                     clustered_forward_buffer_binding_type, | ||||||
|                     false, |                     false, | ||||||
|                     Some(GpuPointLights::min_size( |                     Some(GpuClusterableObjects::min_size( | ||||||
|                         clustered_forward_buffer_binding_type, |                         clustered_forward_buffer_binding_type, | ||||||
|                     )), |                     )), | ||||||
|                 ), |                 ), | ||||||
| @ -246,9 +246,11 @@ fn layout_entries( | |||||||
|                 buffer_layout( |                 buffer_layout( | ||||||
|                     clustered_forward_buffer_binding_type, |                     clustered_forward_buffer_binding_type, | ||||||
|                     false, |                     false, | ||||||
|                     Some(ViewClusterBindings::min_size_cluster_light_index_lists( |                     Some( | ||||||
|                         clustered_forward_buffer_binding_type, |                         ViewClusterBindings::min_size_clusterable_object_index_lists( | ||||||
|                     )), |                             clustered_forward_buffer_binding_type, | ||||||
|  |                         ), | ||||||
|  |                     ), | ||||||
|                 ), |                 ), | ||||||
|             ), |             ), | ||||||
|             // ClusterOffsetsAndCounts
 |             // ClusterOffsetsAndCounts
 | ||||||
| @ -446,7 +448,7 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|     mesh_pipeline: Res<MeshPipeline>, |     mesh_pipeline: Res<MeshPipeline>, | ||||||
|     shadow_samplers: Res<ShadowSamplers>, |     shadow_samplers: Res<ShadowSamplers>, | ||||||
|     light_meta: Res<LightMeta>, |     light_meta: Res<LightMeta>, | ||||||
|     global_light_meta: Res<GlobalLightMeta>, |     global_light_meta: Res<GlobalClusterableObjectMeta>, | ||||||
|     fog_meta: Res<FogMeta>, |     fog_meta: Res<FogMeta>, | ||||||
|     view_uniforms: Res<ViewUniforms>, |     view_uniforms: Res<ViewUniforms>, | ||||||
|     views: Query<( |     views: Query<( | ||||||
| @ -476,7 +478,7 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|     if let ( |     if let ( | ||||||
|         Some(view_binding), |         Some(view_binding), | ||||||
|         Some(light_binding), |         Some(light_binding), | ||||||
|         Some(point_light_binding), |         Some(clusterable_objects_binding), | ||||||
|         Some(globals), |         Some(globals), | ||||||
|         Some(fog_binding), |         Some(fog_binding), | ||||||
|         Some(light_probes_binding), |         Some(light_probes_binding), | ||||||
| @ -485,7 +487,7 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|     ) = ( |     ) = ( | ||||||
|         view_uniforms.uniforms.binding(), |         view_uniforms.uniforms.binding(), | ||||||
|         light_meta.view_gpu_lights.binding(), |         light_meta.view_gpu_lights.binding(), | ||||||
|         global_light_meta.gpu_point_lights.binding(), |         global_light_meta.gpu_clusterable_objects.binding(), | ||||||
|         globals_buffer.buffer.binding(), |         globals_buffer.buffer.binding(), | ||||||
|         fog_meta.gpu_fogs.binding(), |         fog_meta.gpu_fogs.binding(), | ||||||
|         light_probes_buffer.binding(), |         light_probes_buffer.binding(), | ||||||
| @ -524,8 +526,13 @@ pub fn prepare_mesh_view_bind_groups( | |||||||
|                 (3, &shadow_samplers.point_light_sampler), |                 (3, &shadow_samplers.point_light_sampler), | ||||||
|                 (4, &shadow_bindings.directional_light_depth_texture_view), |                 (4, &shadow_bindings.directional_light_depth_texture_view), | ||||||
|                 (5, &shadow_samplers.directional_light_sampler), |                 (5, &shadow_samplers.directional_light_sampler), | ||||||
|                 (6, point_light_binding.clone()), |                 (6, clusterable_objects_binding.clone()), | ||||||
|                 (7, cluster_bindings.light_index_lists_binding().unwrap()), |                 ( | ||||||
|  |                     7, | ||||||
|  |                     cluster_bindings | ||||||
|  |                         .clusterable_object_index_lists_binding() | ||||||
|  |                         .unwrap(), | ||||||
|  |                 ), | ||||||
|                 (8, cluster_bindings.offsets_and_counts_binding().unwrap()), |                 (8, cluster_bindings.offsets_and_counts_binding().unwrap()), | ||||||
|                 (9, globals.clone()), |                 (9, globals.clone()), | ||||||
|                 (10, fog_binding.clone()), |                 (10, fog_binding.clone()), | ||||||
|  | |||||||
| @ -22,12 +22,12 @@ | |||||||
| @group(0) @binding(5) var directional_shadow_textures_sampler: sampler_comparison; | @group(0) @binding(5) var directional_shadow_textures_sampler: sampler_comparison; | ||||||
| 
 | 
 | ||||||
| #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 | #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 | ||||||
| @group(0) @binding(6) var<storage> point_lights: types::PointLights; | @group(0) @binding(6) var<storage> clusterable_objects: types::ClusterableObjects; | ||||||
| @group(0) @binding(7) var<storage> cluster_light_index_lists: types::ClusterLightIndexLists; | @group(0) @binding(7) var<storage> clusterable_object_index_lists: types::ClusterLightIndexLists; | ||||||
| @group(0) @binding(8) var<storage> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts; | @group(0) @binding(8) var<storage> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts; | ||||||
| #else | #else | ||||||
| @group(0) @binding(6) var<uniform> point_lights: types::PointLights; | @group(0) @binding(6) var<uniform> clusterable_objects: types::ClusterableObjects; | ||||||
| @group(0) @binding(7) var<uniform> cluster_light_index_lists: types::ClusterLightIndexLists; | @group(0) @binding(7) var<uniform> clusterable_object_index_lists: types::ClusterLightIndexLists; | ||||||
| @group(0) @binding(8) var<uniform> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts; | @group(0) @binding(8) var<uniform> cluster_offsets_and_counts: types::ClusterOffsetsAndCounts; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| #define_import_path bevy_pbr::mesh_view_types | #define_import_path bevy_pbr::mesh_view_types | ||||||
| 
 | 
 | ||||||
| struct PointLight { | struct ClusterableObject { | ||||||
|     // For point lights: the lower-right 2x2 values of the projection matrix [2][2] [2][3] [3][2] [3][3] |     // For point lights: the lower-right 2x2 values of the projection matrix [2][2] [2][3] [3][2] [3][3] | ||||||
|     // For spot lights: the direction (x,z), spot_scale and spot_offset |     // For spot lights: the direction (x,z), spot_scale and spot_offset | ||||||
|     light_custom_data: vec4<f32>, |     light_custom_data: vec4<f32>, | ||||||
| @ -88,8 +88,8 @@ const FOG_MODE_EXPONENTIAL_SQUARED: u32   = 3u; | |||||||
| const FOG_MODE_ATMOSPHERIC: u32           = 4u; | const FOG_MODE_ATMOSPHERIC: u32           = 4u; | ||||||
| 
 | 
 | ||||||
| #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 | #if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3 | ||||||
| struct PointLights { | struct ClusterableObjects { | ||||||
|     data: array<PointLight>, |     data: array<ClusterableObject>, | ||||||
| }; | }; | ||||||
| struct ClusterLightIndexLists { | struct ClusterLightIndexLists { | ||||||
|     data: array<u32>, |     data: array<u32>, | ||||||
| @ -98,11 +98,11 @@ struct ClusterOffsetsAndCounts { | |||||||
|     data: array<vec4<u32>>, |     data: array<vec4<u32>>, | ||||||
| }; | }; | ||||||
| #else | #else | ||||||
| struct PointLights { | struct ClusterableObjects { | ||||||
|     data: array<PointLight, 256u>, |     data: array<ClusterableObject, 256u>, | ||||||
| }; | }; | ||||||
| struct ClusterLightIndexLists { | struct ClusterLightIndexLists { | ||||||
|     // each u32 contains 4 u8 indices into the PointLights array |     // each u32 contains 4 u8 indices into the ClusterableObjects array | ||||||
|     data: array<vec4<u32>, 1024u>, |     data: array<vec4<u32>, 1024u>, | ||||||
| }; | }; | ||||||
| struct ClusterOffsetsAndCounts { | struct ClusterOffsetsAndCounts { | ||||||
|  | |||||||
| @ -410,10 +410,10 @@ fn apply_pbr_lighting( | |||||||
| 
 | 
 | ||||||
|     // Point lights (direct) |     // Point lights (direct) | ||||||
|     for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) { |     for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) { | ||||||
|         let light_id = clustering::get_light_id(i); |         let light_id = clustering::get_clusterable_object_id(i); | ||||||
|         var shadow: f32 = 1.0; |         var shadow: f32 = 1.0; | ||||||
|         if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u |         if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u | ||||||
|                 && (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { |                 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { | ||||||
|             shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal); |             shadow = shadows::fetch_point_shadow(light_id, in.world_position, in.world_normal); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -432,7 +432,7 @@ fn apply_pbr_lighting( | |||||||
|         // F0 = vec3<f32>(0.0) |         // F0 = vec3<f32>(0.0) | ||||||
|         var transmitted_shadow: f32 = 1.0; |         var transmitted_shadow: f32 = 1.0; | ||||||
|         if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) |         if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) | ||||||
|                 && (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { |                 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { | ||||||
|             transmitted_shadow = shadows::fetch_point_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); |             transmitted_shadow = shadows::fetch_point_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -444,11 +444,11 @@ fn apply_pbr_lighting( | |||||||
| 
 | 
 | ||||||
|     // Spot lights (direct) |     // Spot lights (direct) | ||||||
|     for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) { |     for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) { | ||||||
|         let light_id = clustering::get_light_id(i); |         let light_id = clustering::get_clusterable_object_id(i); | ||||||
| 
 | 
 | ||||||
|         var shadow: f32 = 1.0; |         var shadow: f32 = 1.0; | ||||||
|         if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u |         if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u | ||||||
|                 && (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { |                 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { | ||||||
|             shadow = shadows::fetch_spot_shadow(light_id, in.world_position, in.world_normal); |             shadow = shadows::fetch_spot_shadow(light_id, in.world_position, in.world_normal); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -467,7 +467,7 @@ fn apply_pbr_lighting( | |||||||
|         // F0 = vec3<f32>(0.0) |         // F0 = vec3<f32>(0.0) | ||||||
|         var transmitted_shadow: f32 = 1.0; |         var transmitted_shadow: f32 = 1.0; | ||||||
|         if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) |         if ((in.flags & (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT)) == (MESH_FLAGS_SHADOW_RECEIVER_BIT | MESH_FLAGS_TRANSMITTED_SHADOW_RECEIVER_BIT) | ||||||
|                 && (view_bindings::point_lights.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { |                 && (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { | ||||||
|             transmitted_shadow = shadows::fetch_spot_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); |             transmitted_shadow = shadows::fetch_spot_shadow(light_id, diffuse_transmissive_lobe_world_position, -in.world_normal); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -447,7 +447,7 @@ fn point_light(light_id: u32, input: ptr<function, LightingInput>) -> vec3<f32> | |||||||
|     let N = (*input).layers[LAYER_BASE].N; |     let N = (*input).layers[LAYER_BASE].N; | ||||||
|     let V = (*input).V; |     let V = (*input).V; | ||||||
| 
 | 
 | ||||||
|     let light = &view_bindings::point_lights.data[light_id]; |     let light = &view_bindings::clusterable_objects.data[light_id]; | ||||||
|     let light_to_frag = (*light).position_radius.xyz - P; |     let light_to_frag = (*light).position_radius.xyz - P; | ||||||
|     let L = normalize(light_to_frag); |     let L = normalize(light_to_frag); | ||||||
|     let distance_square = dot(light_to_frag, light_to_frag); |     let distance_square = dot(light_to_frag, light_to_frag); | ||||||
| @ -540,7 +540,7 @@ fn spot_light(light_id: u32, input: ptr<function, LightingInput>) -> vec3<f32> { | |||||||
|     // reuse the point light calculations |     // reuse the point light calculations | ||||||
|     let point_light = point_light(light_id, input); |     let point_light = point_light(light_id, input); | ||||||
| 
 | 
 | ||||||
|     let light = &view_bindings::point_lights.data[light_id]; |     let light = &view_bindings::clusterable_objects.data[light_id]; | ||||||
| 
 | 
 | ||||||
|     // reconstruct spot dir from x/z and y-direction flag |     // reconstruct spot dir from x/z and y-direction flag | ||||||
|     var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y); |     var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y); | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0); | const flip_z: vec3<f32> = vec3<f32>(1.0, 1.0, -1.0); | ||||||
| 
 | 
 | ||||||
| fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { | fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { | ||||||
|     let light = &view_bindings::point_lights.data[light_id]; |     let light = &view_bindings::clusterable_objects.data[light_id]; | ||||||
| 
 | 
 | ||||||
|     // because the shadow maps align with the axes and the frustum planes are at 45 degrees |     // because the shadow maps align with the axes and the frustum planes are at 45 degrees | ||||||
|     // we can get the worldspace depth by taking the largest absolute axis |     // we can get the worldspace depth by taking the largest absolute axis | ||||||
| @ -47,7 +47,7 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: v | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { | fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 { | ||||||
|     let light = &view_bindings::point_lights.data[light_id]; |     let light = &view_bindings::clusterable_objects.data[light_id]; | ||||||
| 
 | 
 | ||||||
|     let surface_to_light = (*light).position_radius.xyz - frag_position.xyz; |     let surface_to_light = (*light).position_radius.xyz - frag_position.xyz; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ use bevy::{ | |||||||
|     color::palettes::css::DEEP_PINK, |     color::palettes::css::DEEP_PINK, | ||||||
|     diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, |     diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, | ||||||
|     math::{DVec2, DVec3}, |     math::{DVec2, DVec3}, | ||||||
|     pbr::{ExtractedPointLight, GlobalLightMeta}, |     pbr::{ExtractedPointLight, GlobalClusterableObjectMeta}, | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     render::{camera::ScalingMode, Render, RenderApp, RenderSet}, |     render::{camera::ScalingMode, Render, RenderApp, RenderSet}, | ||||||
|     window::{PresentMode, WindowResolution}, |     window::{PresentMode, WindowResolution}, | ||||||
| @ -170,7 +170,7 @@ fn print_visible_light_count( | |||||||
|     time: Res<Time>, |     time: Res<Time>, | ||||||
|     mut timer: Local<PrintingTimer>, |     mut timer: Local<PrintingTimer>, | ||||||
|     visible: Query<&ExtractedPointLight>, |     visible: Query<&ExtractedPointLight>, | ||||||
|     global_light_meta: Res<GlobalLightMeta>, |     global_light_meta: Res<GlobalClusterableObjectMeta>, | ||||||
| ) { | ) { | ||||||
|     timer.0.tick(time.delta()); |     timer.0.tick(time.delta()); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Patrick Walton
						Patrick Walton