11#import bevy_pbr :: meshlet_bindings :: {
22 meshlet_cluster_meshlet_ids ,
33 meshlet_bounding_spheres ,
4+ meshlet_simplification_errors ,
45 meshlet_cluster_instance_ids ,
56 meshlet_instance_uniforms ,
67 meshlet_second_pass_candidates ,
1314 meshlet_hardware_raster_indirect_args ,
1415 meshlet_raster_clusters ,
1516 meshlet_raster_cluster_rightmost_slot ,
17+ MeshletBoundingSphere ,
1618}
1719#import bevy_render :: maths :: affine3_to_square
1820
@@ -48,8 +50,8 @@ fn cull_clusters(
4850 let world_from_local = affine3_to_square (instance_uniform . world_from_local );
4951 let world_scale = max (length (world_from_local [0 ]), max (length (world_from_local [1 ]), length (world_from_local [2 ])));
5052 let bounding_spheres = meshlet_bounding_spheres [meshlet_id ];
51- let culling_bounding_sphere_center = world_from_local * vec4 (bounding_spheres . self_culling . center , 1 .0 );
52- let culling_bounding_sphere_radius = world_scale * bounding_spheres . self_culling . radius ;
53+ let culling_bounding_sphere_center = world_from_local * vec4 (bounding_spheres . culling_sphere . center , 1 .0 );
54+ let culling_bounding_sphere_radius = world_scale * bounding_spheres . culling_sphere . radius ;
5355
5456#ifdef MESHLET_FIRST_CULLING_PASS
5557 // Frustum culling
@@ -60,28 +62,19 @@ fn cull_clusters(
6062 }
6163 }
6264
63- // Calculate view-space LOD bounding sphere for the cluster
64- let lod_bounding_sphere_center = world_from_local * vec4 (bounding_spheres . self_lod . center , 1 .0 );
65- let lod_bounding_sphere_radius = world_scale * bounding_spheres . self_lod . radius ;
66- let lod_bounding_sphere_center_view_space = (view . view_from_world * vec4 (lod_bounding_sphere_center . xyz , 1 .0 )). xyz ;
67-
68- // Calculate view-space LOD bounding sphere for the cluster's parent
69- let parent_lod_bounding_sphere_center = world_from_local * vec4 (bounding_spheres . parent_lod . center , 1 .0 );
70- let parent_lod_bounding_sphere_radius = world_scale * bounding_spheres . parent_lod . radius ;
71- let parent_lod_bounding_sphere_center_view_space = (view . view_from_world * vec4 (parent_lod_bounding_sphere_center . xyz , 1 .0 )). xyz ;
72-
73- // Check LOD cut (cluster error imperceptible, and parent error not imperceptible)
74- let lod_is_ok = lod_error_is_imperceptible (lod_bounding_sphere_center_view_space , lod_bounding_sphere_radius );
75- let parent_lod_is_ok = lod_error_is_imperceptible (parent_lod_bounding_sphere_center_view_space , parent_lod_bounding_sphere_radius );
65+ // Check LOD cut (cluster group error imperceptible, and parent group error not imperceptible)
66+ let simplification_errors = unpack2x16float (meshlet_simplification_errors [meshlet_id ]);
67+ let lod_is_ok = lod_error_is_imperceptible (bounding_spheres . lod_group_sphere , simplification_errors . x , world_from_local , world_scale );
68+ let parent_lod_is_ok = lod_error_is_imperceptible (bounding_spheres . lod_parent_group_sphere , simplification_errors . y , world_from_local , world_scale );
7669 if ! lod_is_ok || parent_lod_is_ok { return ; }
7770#endif
7871
7972 // Project the culling bounding sphere to view-space for occlusion culling
8073#ifdef MESHLET_FIRST_CULLING_PASS
8174 let previous_world_from_local = affine3_to_square (instance_uniform . previous_world_from_local );
8275 let previous_world_from_local_scale = max (length (previous_world_from_local [0 ]), max (length (previous_world_from_local [1 ]), length (previous_world_from_local [2 ])));
83- let occlusion_culling_bounding_sphere_center = previous_world_from_local * vec4 (bounding_spheres . self_culling . center , 1 .0 );
84- let occlusion_culling_bounding_sphere_radius = previous_world_from_local_scale * bounding_spheres . self_culling . radius ;
76+ let occlusion_culling_bounding_sphere_center = previous_world_from_local * vec4 (bounding_spheres . culling_sphere . center , 1 .0 );
77+ let occlusion_culling_bounding_sphere_radius = previous_world_from_local_scale * bounding_spheres . culling_sphere . radius ;
8578 let occlusion_culling_bounding_sphere_center_view_space = (previous_view . view_from_world * vec4 (occlusion_culling_bounding_sphere_center . xyz , 1 .0 )). xyz ;
8679#else
8780 let occlusion_culling_bounding_sphere_center = culling_bounding_sphere_center ;
@@ -148,14 +141,23 @@ fn cull_clusters(
148141 meshlet_raster_clusters [buffer_slot ] = cluster_id ;
149142}
150143
151- // https://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space/21649403#21649403
152- fn lod_error_is_imperceptible (cp : vec3 <f32 >, r : f32 ) -> bool {
153- let d2 = dot (cp , cp );
154- let r2 = r * r ;
155- let sphere_diameter_uv = view . clip_from_view [0 ][0 ] * r / sqrt (d2 - r2 );
156- let view_size = f32 (max (view . viewport . z , view . viewport . w ));
157- let sphere_diameter_pixels = sphere_diameter_uv * view_size ;
158- return sphere_diameter_pixels < 1 .0 ;
144+ // https://github.com/zeux/meshoptimizer/blob/1e48e96c7e8059321de492865165e9ef071bffba/demo/nanite.cpp#L115
145+ fn lod_error_is_imperceptible (lod_sphere : MeshletBoundingSphere , simplification_error : f32 , world_from_local : mat4x4 <f32 >, world_scale : f32 ) -> bool {
146+ let sphere_world_space = (world_from_local * vec4 (lod_sphere . center , 1 .0 )). xyz ;
147+ let radius_world_space = world_scale * lod_sphere . radius ;
148+ let error_world_space = world_scale * simplification_error ;
149+
150+ var projected_error = error_world_space ;
151+ if view . clip_from_view [3 ][3 ] != 1 .0 {
152+ // Perspective
153+ let distance_to_closest_point_on_sphere = distance (sphere_world_space , view . world_position ) - radius_world_space ;
154+ let distance_to_closest_point_on_sphere_clamped_to_znear = max (distance_to_closest_point_on_sphere , view . clip_from_view [3 ][2 ]);
155+ projected_error /= distance_to_closest_point_on_sphere_clamped_to_znear ;
156+ }
157+ projected_error *= view . clip_from_view [1 ][1 ] * 0 .5 ;
158+ projected_error *= view . viewport . w ;
159+
160+ return projected_error < 1 .0 ;
159161}
160162
161163// https://zeux.io/2023/01/12/approximate-projected-bounds
0 commit comments