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.
1#define_import_path bevy_pbr::light_probe
3#import bevy_pbr::clustered_forward
4#import bevy_pbr::clustered_forward::ClusterableObjectIndexRanges
5#import bevy_pbr::mesh_view_bindings::light_probes
6#import bevy_pbr::mesh_view_types::LightProbe
8// The result of searching for a light probe.
9struct LightProbeQueryResult {
10 // The index of the light probe texture or textures in the binding array or
11 // arrays.
12 texture_index: i32,
13 // A scale factor that's applied to the diffuse and specular light from the
14 // light probe. This is in units of cd/m² (candela per square meter).
15 intensity: f32,
16 // Transform from world space to the light probe model space. In light probe
17 // model space, the light probe is a 1×1×1 cube centered on the origin.
18 light_from_world: mat4x4<f32>,
19 // Whether this light probe contributes diffuse light to lightmapped meshes.
20 affects_lightmapped_mesh_diffuse: bool,
21};
23fn transpose_affine_matrix(matrix: mat3x4<f32>) -> mat4x4<f32> {
24 let matrix4x4 = mat4x4<f32>(
25 matrix[0],
26 matrix[1],
27 matrix[2],
28 vec4<f32>(0.0, 0.0, 0.0, 1.0));
29 return transpose(matrix4x4);
32#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
34// Searches for a light probe that contains the fragment.
35//
36// This is the version that's used when storage buffers are available and
37// light probes are clustered.
38//
39// TODO: Interpolate between multiple light probes.
40fn query_light_probe(
41 world_position: vec3<f32>,
42 is_irradiance_volume: bool,
43 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>,
44) -> LightProbeQueryResult {
45 var result: LightProbeQueryResult;
46 result.texture_index = -1;
48 // Reflection probe indices are followed by irradiance volume indices in the
49 // cluster index list. Use this fact to create our bracketing range of
50 // indices.
51 var start_offset: u32;
52 var end_offset: u32;
53 if is_irradiance_volume {
54 start_offset = (*clusterable_object_index_ranges).first_irradiance_volume_index_offset;
55 end_offset = (*clusterable_object_index_ranges).first_decal_offset;
56 } else {
57 start_offset = (*clusterable_object_index_ranges).first_reflection_probe_index_offset;
58 end_offset = (*clusterable_object_index_ranges).first_irradiance_volume_index_offset;
59 }
61 for (var light_probe_index_offset: u32 = start_offset;
62 light_probe_index_offset < end_offset && result.texture_index < 0;
63 light_probe_index_offset += 1u) {
64 let light_probe_index = i32(clustered_forward::get_clusterable_object_id(
65 light_probe_index_offset));
67 var light_probe: LightProbe;
68 if is_irradiance_volume {
69 light_probe = light_probes.irradiance_volumes[light_probe_index];
70 } else {
71 light_probe = light_probes.reflection_probes[light_probe_index];
72 }
74 // Unpack the inverse transform.
75 let light_from_world =
76 transpose_affine_matrix(light_probe.light_from_world_transposed);
78 // Check to see if the transformed point is inside the unit cube
79 // centered at the origin.
80 let probe_space_pos = (light_from_world * vec4<f32>(world_position, 1.0f)).xyz;
81 if (all(abs(probe_space_pos) <= vec3(0.5f))) {
82 result.texture_index = light_probe.cubemap_index;
83 result.intensity = light_probe.intensity;
84 result.light_from_world = light_from_world;
85 result.affects_lightmapped_mesh_diffuse =
86 light_probe.affects_lightmapped_mesh_diffuse != 0u;
87 break;
88 }
89 }
91 return result;
94#else // AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
96// Searches for a light probe that contains the fragment.
97//
98// This is the version that's used when storage buffers aren't available and
99// light probes aren't clustered. It simply does a brute force search of all
100// light probes. Because platforms without sufficient SSBO bindings typically
101// lack bindless shaders, there will usually only be one of each type of light
102// probe present anyway.
103fn query_light_probe(
104 world_position: vec3<f32>,
105 is_irradiance_volume: bool,
106 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>,
107) -> LightProbeQueryResult {
108 var result: LightProbeQueryResult;
109 result.texture_index = -1;
111 var light_probe_count: i32;
112 if is_irradiance_volume {
113 light_probe_count = light_probes.irradiance_volume_count;
114 } else {
115 light_probe_count = light_probes.reflection_probe_count;
116 }
118 for (var light_probe_index: i32 = 0;
119 light_probe_index < light_probe_count && result.texture_index < 0;
120 light_probe_index += 1) {
121 var light_probe: LightProbe;
122 if is_irradiance_volume {
123 light_probe = light_probes.irradiance_volumes[light_probe_index];
124 } else {
125 light_probe = light_probes.reflection_probes[light_probe_index];
126 }
128 // Unpack the inverse transform.
129 let light_from_world =
130 transpose_affine_matrix(light_probe.light_from_world_transposed);
132 // Check to see if the transformed point is inside the unit cube
133 // centered at the origin.
134 let probe_space_pos = (light_from_world * vec4<f32>(world_position, 1.0f)).xyz;
135 if (all(abs(probe_space_pos) <= vec3(0.5f))) {
136 result.texture_index = light_probe.cubemap_index;
137 result.intensity = light_probe.intensity;
138 result.light_from_world = light_from_world;
139 result.affects_lightmapped_mesh_diffuse =
140 light_probe.affects_lightmapped_mesh_diffuse != 0u;
142 // TODO: Workaround for ICE in DXC https://github.com/microsoft/DirectXShaderCompiler/issues/6183
143 // We can't use `break` here because of the ICE.
144 // So instead we rely on the fact that we set `result.texture_index`
145 // above and check its value in the `for` loop header before
146 // looping.
147 // break;
148 }
149 }
151 return result;
154#endif // AVAILABLE_STORAGE_BUFFER_BINDINGS >= 3
Contents