Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1import package::render::clustered_forward::view_z_to_z_slice;
3// Valid values for the `object_type` field.
4const CLUSTERABLE_OBJECT_TYPE_POINT_LIGHT: u32 = 0u;
5const CLUSTERABLE_OBJECT_TYPE_SPOT_LIGHT: u32 = 1u;
6const CLUSTERABLE_OBJECT_TYPE_REFLECTION_PROBE: u32 = 2u;
7const CLUSTERABLE_OBJECT_TYPE_IRRADIANCE_VOLUME: u32 = 3u;
8const CLUSTERABLE_OBJECT_TYPE_DECAL: u32 = 4u;
9const CLUSTERABLE_OBJECT_TYPE_RECT_LIGHT: u32 = 5u;
11const NDC_MIN: vec2<f32> = vec2<f32>(-1.0);
12const NDC_MAX: vec2<f32> = vec2<f32>(1.0);
14/// Metadata stored on GPU that's global to all clusters for a view.
15///
16/// See the comments in `bevy_pbr/src/cluster/gpu.rs` for information on the
17/// fields.
18struct ClusterMetadata {
19 indirect_draw_params: ClusterRasterIndirectDrawParams,
21 clustered_light_count: u32,
22 reflection_probe_count: u32,
23 irradiance_volume_count: u32,
24 decal_count: u32,
26 z_slice_list_capacity: u32,
27 index_list_size: u32,
29 farthest_z: atomic<u32>,
30};
32/// Indirect draw parameters in the format required by the WebGPU specification.
33struct ClusterRasterIndirectDrawParams {
34 index_count: u32,
35 instance_count: atomic<u32>,
36 first_index: u32,
37 base_vertex: u32,
38 first_instance: u32,
41/// The GPU representation of a single Z-slice of a clusterable object.
42///
43/// See the comments in `bevy_pbr/src/cluster/gpu.rs` for information on the
44/// fields.
45struct ClusterableObjectZSlice {
46 object_index: u32,
47 object_type: u32,
48 z_slice: u32,
49};
51/// An axis-aligned bounding box.
52struct Aabb {
53 /// The minimum extents of the box.
54 min: vec3<f32>,
55 /// The maximum extents of the box.
56 max: vec3<f32>,
57};
59/// An axis-aligned bounding box using unsigned integer coordinates.
60///
61/// This is used for cluster bounds.
62struct AabbU {
63 /// The minimum extents of the box.
64 min: vec3<u32>,
65 /// The maximum extents of the box, plus one.
66 ///
67 /// We add 1 here so that 0-size AABBs can be expressed.
68 max: vec3<u32>,
71/// Returns the AABB of an object suitable for conversion into an AABB of
72/// clusters.
73///
74/// See `bevy_light::cluster::assign::cluster_space_clusterable_object_aabb`.
75fn cluster_space_object_aabb(
76 position: vec3<f32>,
77 radius: f32,
78 view_from_world: mat4x4<f32>,
79 clip_from_view: mat4x4<f32>,
80 view_from_world_scale: vec3<f32>
81) -> Aabb {
82 let position_view = (view_from_world * vec4(position, 1.0)).xyz;
83 let half_extents = radius * abs(view_from_world_scale);
85 var view_min = position_view - half_extents;
86 var view_max = position_view + half_extents;
88 // Constrain view z to be negative - i.e. in front of the camera
89 // When view z is >= 0.0 and we're using a perspective projection, bad
90 // things happen. At view z == 0.0, ndc x,y are mathematically undefined.
91 // At view z > 0.0, i.e. behind the camera, the perspective projection flips
92 // the directions of the axes. This breaks assumptions about use of min/max
93 // operations as something that was to the left in view space is now
94 // returning a coordinate that for view z in front of the camera would be on
95 // the right, but at view z behind the camera is on the left. So, we just
96 // constrain view z to be < 0.0 and necessarily in front of the camera.
97 view_min.z = min(view_min.z, -0.00001);
98 view_max.z = min(view_max.z, -0.00001);
100 // Is there a cheaper way to do this? The problem is that because of
101 // perspective the point at max z but min xy may be less xy in screenspace,
102 // and similar. As such, projecting the min and max xy at both the closer
103 // and further z and taking the min and max of those projected points
104 // addresses this.
105 let view_xymin_near = view_min;
106 let view_xymin_far = vec3(view_min.xy, view_max.z);
107 let view_xymax_near = vec3(view_max.xy, view_min.z);
108 let view_xymax_far = view_max;
110 let clip_xymin_near = clip_from_view * vec4(view_xymin_near, 1.0);
111 let clip_xymin_far = clip_from_view * vec4(view_xymin_far, 1.0);
112 let clip_xymax_near = clip_from_view * vec4(view_xymax_near, 1.0);
113 let clip_xymax_far = clip_from_view * vec4(view_xymax_far, 1.0);
115 let ndc_xymin_near = clip_xymin_near.xyz / clip_xymin_near.w;
116 let ndc_xymin_far = clip_xymin_far.xyz / clip_xymin_far.w;
117 let ndc_xymax_near = clip_xymax_near.xyz / clip_xymax_near.w;
118 let ndc_xymax_far = clip_xymax_far.xyz / clip_xymax_far.w;
120 var ndc_min = min(min(ndc_xymin_near, ndc_xymin_far), min(ndc_xymax_near, ndc_xymax_far));
121 var ndc_max = max(max(ndc_xymin_near, ndc_xymin_far), max(ndc_xymax_near, ndc_xymax_far));
123 // clamp to ndc coords without depth
124 ndc_min = vec3(clamp(ndc_min.xy, NDC_MIN, NDC_MAX), ndc_min.z);
125 ndc_max = vec3(clamp(ndc_max.xy, NDC_MIN, NDC_MAX), ndc_max.z);
127 // pack unadjusted z depth into the vecs
128 return Aabb(vec3(ndc_min.xy, view_min.z), vec3(ndc_max.xy, view_max.z));
131/// Computes the scale of the camera from the view matrix.
132fn compute_view_from_world_scale(world_from_view: mat4x4<f32>) -> vec3<f32> {
133 let world_from_view_3x3 = mat3x3<f32>(
134 world_from_view[0].xyz,
135 world_from_view[1].xyz,
136 world_from_view[2].xyz
137 );
138 let det = determinant(world_from_view_3x3);
139 let scale = vec3<f32>(
140 length(world_from_view_3x3[0]) * sign(det),
141 length(world_from_view_3x3[1]),
142 length(world_from_view_3x3[2])
143 );
144 return vec3<f32>(1.0) / scale;
147/// Returns the cluster coordinates corresponding to a position in normalized
148/// device coordinates.
149/// See `bevy_light::cluster::assign::ndc_position_to_cluster`.
150fn ndc_position_to_cluster(
151 cluster_dimensions: vec3<u32>,
152 cluster_factors: vec2<f32>,
153 is_orthographic: bool,
154 ndc_p: vec3<f32>,
155 view_z: f32
156) -> vec3<u32> {
157 let frag_coord = clamp(ndc_p.xy * vec2(0.5, -0.5) + vec2(0.5), vec2(0.0), vec2(1.0));
158 let xy = vec2<u32>(floor(frag_coord * vec2<f32>(cluster_dimensions.xy)));
159 let z_slice = view_z_to_z_slice(cluster_factors, cluster_dimensions.z, view_z, is_orthographic);
160 return clamp(vec3<u32>(xy, z_slice), vec3(0u), cluster_dimensions - vec3(1u));
163/// Returns the AABB encompassing all clusters that intersect a sphere.
164fn calculate_sphere_cluster_bounds(
165 position: vec3<f32>,
166 radius: f32,
167 view_from_world: mat4x4<f32>,
168 clip_from_view: mat4x4<f32>,
169 view_from_world_scale: vec3<f32>,
170 cluster_dimensions: vec3<u32>,
171 cluster_factors: vec2<f32>,
172 is_orthographic: bool,
173) -> AabbU {
174 let aabb_ndc = cluster_space_object_aabb(
175 position,
176 radius,
177 view_from_world,
178 clip_from_view,
179 view_from_world_scale
180 );
182 let temp_min_cluster = ndc_position_to_cluster(
183 cluster_dimensions,
184 cluster_factors,
185 is_orthographic,
186 aabb_ndc.min,
187 aabb_ndc.min.z
188 );
189 let temp_max_cluster = ndc_position_to_cluster(
190 cluster_dimensions,
191 cluster_factors,
192 is_orthographic,
193 aabb_ndc.max,
194 aabb_ndc.max.z
195 );
197 let min_cluster = min(temp_min_cluster, temp_max_cluster);
198 let max_cluster = max(temp_min_cluster, temp_max_cluster);
200 return AabbU(min_cluster, max_cluster);
Contents