1import package::light_probe::{light_probe_iterator_new, light_probe_iterator_next}; 2import package::render::mesh_view_bindings as bindings; 3import package::render::mesh_view_bindings::light_probes; 4import package::render::mesh_view_types::{ 5 LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE, LIGHT_PROBE_FLAG_PARALLAX_CORRECT 7import package::render::pbr_lighting::{F_Schlick_vec, LightingInput, LayerLightingInput, LAYER_BASE, LAYER_CLEARCOAT}; 8import package::render::clustered_forward::ClusterableObjectIndexRanges; 9import bevy_render::maths::quat_rotate; 12const FLOAT_MAX: f32 = 3.40282347e+38; 14struct EnvironmentMapLight { 19struct EnvironmentMapRadiances { 20 irradiance: vec3<f32>, 24struct MultiscatterResult { 41fn compute_cubemap_sample_dir( 42 world_ray_origin: vec3<f32>, 43 world_ray_direction: vec3<f32>, 44 light_from_world: mat4x4<f32>, 45 parallax_correction_bounds: vec3<f32>, 46 parallax_correct: bool, 47 view_rotation: vec4<f32>, 49 var sample_dir: vec3<f32>; 52 if (parallax_correct) { 56 let ray_origin = (light_from_world * vec4(world_ray_origin, 1.0)).xyz; 57 let ray_direction = (light_from_world * vec4(world_ray_direction, 0.0)).xyz; 63 var t0 = (-parallax_correction_bounds - ray_origin) / ray_direction; 64 var t1 = (parallax_correction_bounds - ray_origin) / ray_direction; 71 t0 = select(vec3(FLOAT_MAX), t0, t0 >= vec3(0.0)); 72 t1 = select(vec3(FLOAT_MAX), t1, t1 >= vec3(0.0)); 76 let t_min = min(t0, t1); 77 let t = min(min(t_min.x, t_min.y), t_min.z); 80 sample_dir = ray_origin + ray_direction * t; 84 sample_dir = (light_from_world * vec4(world_ray_direction, 0.0)).xyz; 89 sample_dir = quat_rotate(view_rotation, sample_dir); 92 sample_dir.z = -sample_dir.z; 100@if(MULTIPLE_LIGHT_PROBES_IN_ARRAY) 103 input: LayerLightingInput, 104 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 105 world_position: vec3<f32>, 106 found_diffuse_indirect: bool, 107) -> EnvironmentMapRadiances { 111 let perceptual_roughness = input.perceptual_roughness; 112 let roughness = input.roughness; 114 var radiances: EnvironmentMapRadiances; 118 var iterator = light_probe_iterator_new( 121 clusterable_object_index_ranges, 124 var total_weight = 0.0; 125 radiances.irradiance = vec3(0.0); 126 radiances.radiance = vec3(0.0); 129 var query_result = light_probe_iterator_next(&iterator); 130 var view_rotation = vec4<f32>(0.0, 0.0, 0.0, 1.0); 136 if (query_result.texture_index < 0 && total_weight < 0.9999) { 137 query_result.texture_index = light_probes.view_cubemap_index; 138 query_result.intensity = light_probes.intensity_for_view; 139 view_rotation = light_probes.view_rotation; 140 query_result.light_from_world = mat4x4( 141 vec4(1.0, 0.0, 0.0, 0.0), 142 vec4(0.0, 1.0, 0.0, 0.0), 143 vec4(0.0, 0.0, 1.0, 0.0), 144 vec4(0.0, 0.0, 0.0, 1.0) 146 query_result.parallax_correction_bounds = vec3(0.0); 147 if light_probes.view_environment_map_affects_lightmapped_mesh_diffuse != 0u { 148 query_result.flags = LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE; 150 query_result.flags = 0u; 152 query_result.weight = 1.0 - total_weight; 156 if (query_result.texture_index < 0) { 161 let radiance_level = perceptual_roughness * f32(textureNumLevels( 162 bindings::specular_environment_maps[query_result.texture_index]) - 1u); 166 var enable_diffuse = !found_diffuse_indirect; 168 enable_diffuse = enable_diffuse && 169 (query_result.flags & LIGHT_PROBE_FLAG_AFFECTS_LIGHTMAPPED_MESH_DIFFUSE) != 0u; 171 let parallax_correct = (query_result.flags & LIGHT_PROBE_FLAG_PARALLAX_CORRECT) != 0u; 173 if (enable_diffuse) { 174 let irradiance_sample_dir = compute_cubemap_sample_dir( 177 query_result.light_from_world, 178 query_result.parallax_correction_bounds, 182 radiances.irradiance += textureSampleLevel( 183 bindings::diffuse_environment_maps[query_result.texture_index], 184 bindings::environment_map_sampler, 185 irradiance_sample_dir, 186 0.0).rgb * query_result.intensity * query_result.weight; 189 var radiance_sample_dir = radiance_sample_direction(N, R, roughness); 190 radiance_sample_dir = compute_cubemap_sample_dir( 193 query_result.light_from_world, 194 query_result.parallax_correction_bounds, 198 radiances.radiance += 200 bindings::specular_environment_maps[query_result.texture_index], 201 bindings::environment_map_sampler, 203 radiance_level).rgb * query_result.intensity * query_result.weight; 205 total_weight += query_result.weight; 208 if (total_weight != 0.0) { 209 radiances.irradiance /= total_weight; 210 radiances.radiance /= total_weight; 219 input: LayerLightingInput, 220 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 221 world_position: vec3<f32>, 222 found_diffuse_indirect: bool, 223) -> EnvironmentMapRadiances { 227 let perceptual_roughness = input.perceptual_roughness; 228 let roughness = input.roughness; 230 var radiances: EnvironmentMapRadiances; 233 if (light_probes.view_cubemap_index < 0) { 234 radiances.irradiance = vec3(0.0); 235 radiances.radiance = vec3(0.0); 242 let radiance_level = perceptual_roughness * f32(light_probes.smallest_specular_mip_level_for_view); 244 let intensity = light_probes.intensity_for_view; 248 var enable_diffuse = !found_diffuse_indirect; 250 enable_diffuse = enable_diffuse && 251 light_probes.view_environment_map_affects_lightmapped_mesh_diffuse; 253 if (enable_diffuse) { 254 var irradiance_sample_dir = N; 257 irradiance_sample_dir = quat_rotate(light_probes.view_rotation, irradiance_sample_dir); 259 irradiance_sample_dir.z = -irradiance_sample_dir.z; 260 radiances.irradiance = textureSampleLevel( 261 bindings::diffuse_environment_map, 262 bindings::environment_map_sampler, 263 irradiance_sample_dir, 264 0.0).rgb * intensity; 267 var radiance_sample_dir = radiance_sample_direction(N, R, roughness); 270 radiance_sample_dir = quat_rotate(light_probes.view_rotation, radiance_sample_dir); 272 radiance_sample_dir.z = -radiance_sample_dir.z; 273 radiances.radiance = textureSampleLevel( 274 bindings::specular_environment_map, 275 bindings::environment_map_sampler, 277 radiance_level).rgb * intensity; 282@if(STANDARD_MATERIAL_CLEARCOAT) 286fn environment_map_light_clearcoat( 287 out: ptr<function, EnvironmentMapLight>, 288 input: ptr<function, LightingInput>, 289 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 290 found_diffuse_indirect: bool, 293 let world_position = (*input).P; 294 let clearcoat_NdotV = (*input).layers[LAYER_CLEARCOAT].NdotV; 295 let clearcoat_strength = (*input).clearcoat_strength; 299 let clearcoat_F0 = vec3<f32>(0.04); 300 let Fc = F_Schlick_vec(clearcoat_F0, 1.0, clearcoat_NdotV) * clearcoat_strength; 301 let inv_Fc = 1.0 - Fc; 303 let clearcoat_radiances = compute_radiances( 304 (*input).layers[LAYER_CLEARCOAT], 305 clusterable_object_index_ranges, 307 found_diffuse_indirect, 313 (*out).diffuse *= inv_Fc; 314 (*out).specular = (*out).specular * inv_Fc * inv_Fc + clearcoat_radiances.radiance * Fc; 321fn compute_multiscatter( 325 specular_occlusion: f32, 326) -> MultiscatterResult { 327 let FssEss = (F0 * F_ab.x + F_ab.y) * specular_occlusion; 328 let Favg = F0 + (1.0 - F0) / 21.0; 329 let FmsEms = FssEss * Favg / (1.0 - Ems * Favg) * Ems; 330 let Edss = 1.0 - (FssEss + FmsEms); 332 return MultiscatterResult(FssEss, FmsEms, Edss); 335fn environment_map_light( 336 input: ptr<function, LightingInput>, 337 clusterable_object_index_ranges: ptr<function, ClusterableObjectIndexRanges>, 338 found_diffuse_indirect: bool, 339) -> EnvironmentMapLight { 341 let roughness = (*input).layers[LAYER_BASE].roughness; 342 let diffuse_color = (*input).diffuse_color; 343 let metallic = (*input).metallic; 344 let NdotV = (*input).layers[LAYER_BASE].NdotV; 345 let F_ab = (*input).F_ab; 346 let F0_dielectric = (*input).F0_dielectric; 347 let F0_metallic = (*input).F0_metallic; 348 let world_position = (*input).P; 350 var out: EnvironmentMapLight; 352 let radiances = compute_radiances( 353 (*input).layers[LAYER_BASE], 354 clusterable_object_index_ranges, 356 found_diffuse_indirect, 359 if (all(radiances.irradiance == vec3(0.0)) && all(radiances.radiance == vec3(0.0))) { 360 out.diffuse = vec3(0.0); 361 out.specular = vec3(0.0); 368 let F0_surface = mix(F0_dielectric, F0_metallic, metallic); 369 let specular_occlusion = saturate(dot(F0_surface, vec3(50.0 * 0.33))); 373 let Ems = 1.0 - (F_ab.x + F_ab.y); 375 let ms_dielectric = compute_multiscatter(F0_dielectric, F_ab, Ems, specular_occlusion); 376 let ms_metallic = compute_multiscatter(F0_metallic, F_ab, Ems, specular_occlusion); 378 let FssEss = mix(ms_dielectric.FssEss, ms_metallic.FssEss, metallic); 379 let FmsEms = mix(ms_dielectric.FmsEms, ms_metallic.FmsEms, metallic); 380 let kD = diffuse_color * ms_dielectric.Edss; 382 if (!found_diffuse_indirect) { 383 out.diffuse = (FmsEms + kD) * radiances.irradiance; 385 out.diffuse = vec3(0.0); 388 out.specular = FssEss * radiances.radiance; 390 @if(STANDARD_MATERIAL_CLEARCOAT) 391 environment_map_light_clearcoat( 394 clusterable_object_index_ranges, 395 found_diffuse_indirect, 403fn radiance_sample_direction(N: vec3<f32>, R: vec3<f32>, roughness: f32) -> vec3<f32> { 404 let smoothness = saturate(1.0 - roughness); 405 let lerp_factor = smoothness * (sqrt(smoothness) + roughness); 406 return mix(N, R, lerp_factor);