1#define_import_path bevy_pbr::environment_map 3#import bevy_pbr::light_probe::{light_probe_iterator_new, light_probe_iterator_next} 4#import bevy_pbr::mesh_view_bindings as bindings 5#import bevy_pbr::mesh_view_bindings::light_probes 6#import bevy_pbr::mesh_view_types::{ 7 LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE, LIGHT_PROBE_FLAG_PARALLAX_CORRECT 9#import bevy_pbr::lighting::{F_Schlick_vec, LightingInput, LayerLightingInput, LAYER_BASE, LAYER_CLEARCOAT} 10#import bevy_pbr::clustered_forward::ClusterableObjectIndexRanges 13const FLOAT_MAX: f32 = 3.40282347e+38; 15struct EnvironmentMapLight { 20struct EnvironmentMapRadiances { 21 irradiance: vec3<f32>, 25struct MultiscatterResult { 42fn compute_cubemap_sample_dir( 43 world_ray_origin: vec3<f32>, 44 world_ray_direction: vec3<f32>, 45 light_from_world: mat4x4<f32>, 46 parallax_correction_bounds: vec3<f32>, 47 parallax_correct: bool, 48 view_rotation: vec4<f32>, 50 var sample_dir: vec3<f32>; 53 if (parallax_correct) { 57 let ray_origin = (light_from_world * vec4(world_ray_origin, 1.0)).xyz; 58 let ray_direction = (light_from_world * vec4(world_ray_direction, 0.0)).xyz; 64 var t0 = (-parallax_correction_bounds - ray_origin) / ray_direction; 65 var t1 = (parallax_correction_bounds - ray_origin) / ray_direction; 72 t0 = select(vec3(FLOAT_MAX), t0, t0 >= vec3(0.0)); 73 t1 = select(vec3(FLOAT_MAX), t1, t1 >= vec3(0.0)); 77 let t_min = min(t0, t1); 78 let t = min(min(t_min.x, t_min.y), t_min.z); 81 sample_dir = ray_origin + ray_direction * t; 85 sample_dir = (light_from_world * vec4(world_ray_direction, 0.0)).xyz; 90 sample_dir = quat_rotate(view_rotation, sample_dir); 93 sample_dir.z = -sample_dir.z; 101#ifdef MULTIPLE_LIGHT_PROBES_IN_ARRAY 104 input: LayerLightingInput, 105 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 106 world_position: vec3<f32>, 107 found_diffuse_indirect: bool, 108) -> EnvironmentMapRadiances { 112 let perceptual_roughness = input.perceptual_roughness; 113 let roughness = input.roughness; 115 var radiances: EnvironmentMapRadiances; 119 var iterator = light_probe_iterator_new( 122 clusterable_object_index_ranges, 125 var total_weight = 0.0; 126 radiances.irradiance = vec3(0.0); 127 radiances.radiance = vec3(0.0); 130 var query_result = light_probe_iterator_next(&iterator); 131 var view_rotation = vec4<f32>(0.0, 0.0, 0.0, 1.0); 137 if (query_result.texture_index < 0 && total_weight < 0.9999) { 138 query_result.texture_index = light_probes.view_cubemap_index; 139 query_result.intensity = light_probes.intensity_for_view; 140 view_rotation = light_probes.view_rotation; 141 query_result.light_from_world = mat4x4( 142 vec4(1.0, 0.0, 0.0, 0.0), 143 vec4(0.0, 1.0, 0.0, 0.0), 144 vec4(0.0, 0.0, 1.0, 0.0), 145 vec4(0.0, 0.0, 0.0, 1.0) 147 query_result.parallax_correction_bounds = vec3(0.0); 148 if light_probes.view_environment_map_affects_lightmapped_mesh_diffuse != 0u { 149 query_result.flags = LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE; 151 query_result.flags = 0u; 153 query_result.weight = 1.0 - total_weight; 157 if (query_result.texture_index < 0) { 162 let radiance_level = perceptual_roughness * f32(textureNumLevels( 163 bindings::specular_environment_maps[query_result.texture_index]) - 1u); 167 var enable_diffuse = !found_diffuse_indirect; 169 enable_diffuse = enable_diffuse && 170 (query_result.flags & LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE) != 0u; 173 let parallax_correct = (query_result.flags & LIGHT_PROBE_FLAG_PARALLAX_CORRECT) != 0u; 175 if (enable_diffuse) { 176 let irradiance_sample_dir = compute_cubemap_sample_dir( 179 query_result.light_from_world, 180 query_result.parallax_correction_bounds, 184 radiances.irradiance = textureSampleLevel( 185 bindings::diffuse_environment_maps[query_result.texture_index], 186 bindings::environment_map_sampler, 187 irradiance_sample_dir, 188 0.0).rgb * query_result.intensity * query_result.weight; 191 var radiance_sample_dir = radiance_sample_direction(N, R, roughness); 192 radiance_sample_dir = compute_cubemap_sample_dir( 195 query_result.light_from_world, 196 query_result.parallax_correction_bounds, 200 radiances.radiance += 202 bindings::specular_environment_maps[query_result.texture_index], 203 bindings::environment_map_sampler, 205 radiance_level).rgb * query_result.intensity * query_result.weight; 207 total_weight += query_result.weight; 210 if (total_weight != 0.0) { 211 radiances.irradiance /= total_weight; 212 radiances.radiance /= total_weight; 218#else // MULTIPLE_LIGHT_PROBES_IN_ARRAY 221 input: LayerLightingInput, 222 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 223 world_position: vec3<f32>, 224 found_diffuse_indirect: bool, 225) -> EnvironmentMapRadiances { 229 let perceptual_roughness = input.perceptual_roughness; 230 let roughness = input.roughness; 232 var radiances: EnvironmentMapRadiances; 235 if (light_probes.view_cubemap_index < 0) { 236 radiances.irradiance = vec3(0.0); 237 radiances.radiance = vec3(0.0); 244 let radiance_level = perceptual_roughness * f32(light_probes.smallest_specular_mip_level_for_view); 246 let intensity = light_probes.intensity_for_view; 250 var enable_diffuse = !found_diffuse_indirect; 252 enable_diffuse = enable_diffuse && 253 light_probes.view_environment_map_affects_lightmapped_mesh_diffuse; 256 if (enable_diffuse) { 257 var irradiance_sample_dir = N; 260 irradiance_sample_dir = quat_rotate(light_probes.view_rotation, irradiance_sample_dir); 262 irradiance_sample_dir.z = -irradiance_sample_dir.z; 263 radiances.irradiance = textureSampleLevel( 264 bindings::diffuse_environment_map, 265 bindings::environment_map_sampler, 266 irradiance_sample_dir, 267 0.0).rgb * intensity; 270 var radiance_sample_dir = radiance_sample_direction(N, R, roughness); 273 radiance_sample_dir = quat_rotate(light_probes.view_rotation, radiance_sample_dir); 275 radiance_sample_dir.z = -radiance_sample_dir.z; 276 radiances.radiance = textureSampleLevel( 277 bindings::specular_environment_map, 278 bindings::environment_map_sampler, 280 radiance_level).rgb * intensity; 285#endif // MULTIPLE_LIGHT_PROBES_IN_ARRAY 287#ifdef STANDARD_MATERIAL_CLEARCOAT 291fn environment_map_light_clearcoat( 292 out: ptr<function, EnvironmentMapLight>, 293 input: ptr<function, LightingInput>, 294 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 295 found_diffuse_indirect: bool, 298 let world_position = (*input).P; 299 let clearcoat_NdotV = (*input).layers[LAYER_CLEARCOAT].NdotV; 300 let clearcoat_strength = (*input).clearcoat_strength; 304 let clearcoat_F0 = vec3<f32>(0.04); 305 let Fc = F_Schlick_vec(clearcoat_F0, 1.0, clearcoat_NdotV) * clearcoat_strength; 306 let inv_Fc = 1.0 - Fc; 308 let clearcoat_radiances = compute_radiances( 309 (*input).layers[LAYER_CLEARCOAT], 310 clusterable_object_index_ranges, 312 found_diffuse_indirect, 318 (*out).diffuse *= inv_Fc; 319 (*out).specular = (*out).specular * inv_Fc * inv_Fc + clearcoat_radiances.radiance * Fc; 322#endif // STANDARD_MATERIAL_CLEARCOAT 328fn compute_multiscatter( 332 specular_occlusion: f32, 333) -> MultiscatterResult { 334 let FssEss = (F0 * F_ab.x + F_ab.y) * specular_occlusion; 335 let Favg = F0 + (1.0 - F0) / 21.0; 336 let FmsEms = FssEss * Favg / (1.0 - Ems * Favg) * Ems; 337 let Edss = 1.0 - (FssEss + FmsEms); 339 return MultiscatterResult(FssEss, FmsEms, Edss); 342fn environment_map_light( 343 input: ptr<function, LightingInput>, 344 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 345 found_diffuse_indirect: bool, 346) -> EnvironmentMapLight { 348 let roughness = (*input).layers[LAYER_BASE].roughness; 349 let diffuse_color = (*input).diffuse_color; 350 let metallic = (*input).metallic; 351 let NdotV = (*input).layers[LAYER_BASE].NdotV; 352 let F_ab = (*input).F_ab; 353 let F0_dielectric = (*input).F0_dielectric; 354 let F0_metallic = (*input).F0_metallic; 355 let world_position = (*input).P; 357 var out: EnvironmentMapLight; 359 let radiances = compute_radiances( 360 (*input).layers[LAYER_BASE], 361 clusterable_object_index_ranges, 363 found_diffuse_indirect, 366 if (all(radiances.irradiance == vec3(0.0)) && all(radiances.radiance == vec3(0.0))) { 367 out.diffuse = vec3(0.0); 368 out.specular = vec3(0.0); 375 let F0_surface = mix(F0_dielectric, F0_metallic, metallic); 376 let specular_occlusion = saturate(dot(F0_surface, vec3(50.0 * 0.33))); 380 let Ems = 1.0 - (F_ab.x + F_ab.y); 382 let ms_dielectric = compute_multiscatter(F0_dielectric, F_ab, Ems, specular_occlusion); 383 let ms_metallic = compute_multiscatter(F0_metallic, F_ab, Ems, specular_occlusion); 385 let FssEss = mix(ms_dielectric.FssEss, ms_metallic.FssEss, metallic); 386 let FmsEms = mix(ms_dielectric.FmsEms, ms_metallic.FmsEms, metallic); 387 let kD = diffuse_color * ms_dielectric.Edss; 389 if (!found_diffuse_indirect) { 390 out.diffuse = (FmsEms + kD) * radiances.irradiance; 392 out.diffuse = vec3(0.0); 395 out.specular = FssEss * radiances.radiance; 397#ifdef STANDARD_MATERIAL_CLEARCOAT 398 environment_map_light_clearcoat( 401 clusterable_object_index_ranges, 402 found_diffuse_indirect, 404#endif // STANDARD_MATERIAL_CLEARCOAT 411fn radiance_sample_direction(N: vec3<f32>, R: vec3<f32>, roughness: f32) -> vec3<f32> { 412 let smoothness = saturate(1.0 - roughness); 413 let lerp_factor = smoothness * (sqrt(smoothness) + roughness); 414 return mix(N, R, lerp_factor); 417fn quat_rotate(q: vec4<f32>, dir: vec3<f32>) -> vec3<f32> { 418 let t = 2.0 * cross(q.xyz, dir); 419 return dir + q.w * t + cross(q.xyz, t);