1#define_import_path bevy_pbr::lighting 4 mesh_view_types::POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE, 5 mesh_view_bindings as view_bindings, 6 atmosphere::functions::{calculate_visible_sun_ratio, clamp_to_surface}, 7 atmosphere::bruneton_functions::transmittance_lut_r_mu_to_uv, 9#import bevy_render::maths::PI 11const LAYER_BASE: u32 = 0; 12const LAYER_CLEARCOAT: u32 = 1; 50struct LayerLightingInput { 59 perceptual_roughness: f32, 67#ifdef STANDARD_MATERIAL_CLEARCOAT 68 layers: array<LayerLightingInput, 2>, 69#else // STANDARD_MATERIAL_CLEARCOAT 70 layers: array<LayerLightingInput, 1>, 71#endif // STANDARD_MATERIAL_CLEARCOAT 79 diffuse_color: vec3<f32>, 92#ifdef STANDARD_MATERIAL_CLEARCOAT 94 clearcoat_strength: f32, 95#endif // STANDARD_MATERIAL_CLEARCOAT 97#ifdef STANDARD_MATERIAL_ANISOTROPY 107#endif // STANDARD_MATERIAL_ANISOTROPY 111struct DerivedLightingInput { 128fn getDistanceAttenuation(distanceSquare: f32, inverseRangeSquared: f32) -> f32 { 129 let factor = distanceSquare * inverseRangeSquared; 130 let smoothFactor = saturate(1.0 - factor * factor); 131 let attenuation = smoothFactor * smoothFactor; 132 return attenuation * 1.0 / max(distanceSquare, 0.0001); 142fn D_GGX(roughness: f32, NdotH: f32) -> f32 { 143 let oneMinusNdotHSquared = 1.0 - NdotH * NdotH; 144 let a = NdotH * roughness; 145 let k = roughness / (oneMinusNdotHSquared + a * a); 146 let d = k * k * (1.0 / PI); 166fn D_GGX_anisotropic(at: f32, ab: f32, NdotH: f32, TdotH: f32, BdotH: f32) -> f32 { 168 let f = vec3(ab * TdotH, at * BdotH, a2 * NdotH); 169 let w2 = a2 / dot(f, f); 170 let d = a2 * w2 * w2 * (1.0 / PI); 181fn V_SmithGGXCorrelated(roughness: f32, NdotV: f32, NdotL: f32) -> f32 { 182 let a2 = roughness * roughness; 183 let lambdaV = NdotL * sqrt((NdotV - a2 * NdotV) * NdotV + a2); 184 let lambdaL = NdotV * sqrt((NdotL - a2 * NdotL) * NdotL + a2); 185 let v = 0.5 / (lambdaV + lambdaL); 200 let GGX_V = NdotL * length(vec3(at * TdotV, ab * BdotV, NdotV)); 201 let GGX_L = NdotV * length(vec3(at * TdotL, ab * BdotL, NdotL)); 202 let v = 0.5 / (GGX_V + GGX_L); 208fn ggx_vndf_pdf(i: vec3<f32>, NdotH: f32, roughness: f32) -> f32 { 209 let ndf = D_GGX(roughness, NdotH); 212 let ai = roughness * i.xy; 213 let len2 = dot(ai, ai); 214 let t = sqrt(len2 + i.z * i.z); 217 let s = 1.0 + length(i.xy); 220 let k = (1.0 - a2) * s2 / (s2 + a2 * i.z * i.z); 221 return ndf / (2.0 * (k * i.z + t)); 225 return ndf * (t - i.z) / (2.0 * len2); 229fn sample_visible_ggx( 236 let alpha = roughness; 239 let wi_n = dot(view, n); 240 let wi_z = -n * wi_n; 241 let wi_xy = view + wi_z; 244 let wi_std = -normalize(alpha * wi_xy + wi_z); 247 let wi_std_z = dot(wi_std, n); 251 let s = 1.0 + length(wi_xy); 252 let a = clamp(alpha, 0.0, 1.0); 255 let k = (1.0 - a2) * s2 / (s2 + a2 * wi_n * wi_n); 256 let b = select(wi_std_z, k * wi_std_z, wi_n > 0.0); 259 let z = 1.0 - xi.y * (1.0 + b); 260 let sin_theta = sqrt(max(0.0, 1.0 - z * z)); 261 let phi = 2.0 * PI * xi.x - PI; 262 let x = sin_theta * cos(phi); 263 let y = sin_theta * sin(phi); 264 let c_std = vec3f(x, y, z); 267 let up = vec3f(0.0, 0.0, 1.0); 269 let c = dot(wr, c_std) * wr / wr.z - c_std; 272 let wm_std = c + wi_std; 273 let wm_std_z = n * dot(n, wm_std); 274 let wm_std_xy = wm_std_z - wm_std; 277 let H = normalize(alpha * wm_std_xy + wm_std_z); 280 return reflect(-view, H); 284fn G_Smith(NdotV: f32, NdotL: f32, roughness: f32) -> f32 { 285 let k = roughness / 2.0; 286 let GGXL = NdotL / (NdotL * (1.0 - k) + k); 287 let GGXV = NdotV / (NdotV * (1.0 - k) + k); 295fn V_Kelemen(LdotH: f32) -> f32 { 296 return 0.25 / (LdotH * LdotH); 302fn F_Schlick_vec(f0: vec3<f32>, f90: f32, VdotH: f32) -> vec3<f32> { 304 return f0 + (f90 - f0) * pow(1.0 - VdotH, 5.0); 307fn F_Schlick(f0: f32, f90: f32, VdotH: f32) -> f32 { 309 return f0 + (f90 - f0) * pow(1.0 - VdotH, 5.0); 312fn fresnel(f0: vec3<f32>, LdotH: f32) -> vec3<f32> { 315 let f90 = saturate(dot(f0, vec3<f32>(50.0 * 0.33))); 316 return F_Schlick_vec(f0, f90, LdotH); 324fn specular_multiscatter( 330 specular_intensity: f32, 332 var Fr = (specular_intensity * D * V) * F; 333 Fr *= 1.0 + F0 * (1.0 / F_ab.x - 1.0); 341fn derive_lighting_input(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>) -> DerivedLightingInput { 342 var input: DerivedLightingInput; 343 var H: vec3<f32> = normalize(L + V); 345 input.NdotL = saturate(dot(N, L)); 346 input.NdotH = saturate(dot(N, H)); 347 input.LdotH = saturate(dot(L, H)); 352fn compute_specular_layer_values_for_point_light( 353 input: ptr<function, LightingInput>, 356 light_to_frag: vec3<f32>, 361 let R = (*input).layers[layer].R; 362 let a = (*input).layers[layer].roughness; 366 var LtFdotR = dot(light_to_frag, R); 379 LtFdotR = max(0.0001, LtFdotR); 381 let centerToRay = LtFdotR * R - light_to_frag; 382 let closestPoint = light_to_frag + centerToRay * saturate( 383 light_radius * inverseSqrt(dot(centerToRay, centerToRay))); 384 let LspecLengthInverse = inverseSqrt(dot(closestPoint, closestPoint)); 388 let a_prime = saturate(a + light_radius / (2.0 * distance)); 390 let L: vec3<f32> = closestPoint * LspecLengthInverse; 391 return vec4(L, a_prime); 397 input: ptr<function, LightingInput>, 398 derived_input: ptr<function, DerivedLightingInput>, 400 specular_intensity: f32, 403 let NdotV = (*input).layers[LAYER_BASE].NdotV; 404 let F0 = (*input).F0_; 405 let NdotL = (*derived_input).NdotL; 406 let NdotH = (*derived_input).NdotH; 407 let LdotH = (*derived_input).LdotH; 410 let D = D_GGX(roughness, NdotH); 412 let V = V_SmithGGXCorrelated(roughness, NdotV, NdotL); 414 let F = fresnel(F0, LdotH); 417 let Fr = specular_multiscatter(D, V, F, F0, (*input).F_ab, specular_intensity); 426fn specular_clearcoat( 427 input: ptr<function, LightingInput>, 428 derived_input: ptr<function, DerivedLightingInput>, 429 clearcoat_strength: f32, 431 specular_intensity: f32, 434 let NdotH = (*derived_input).NdotH; 435 let LdotH = (*derived_input).LdotH; 438 let Dc = D_GGX(roughness, NdotH); 440 let Vc = V_Kelemen(LdotH); 442 let Fc = F_Schlick(0.04, 1.0, LdotH) * clearcoat_strength; 444 let Frc = (specular_intensity * Dc * Vc) * Fc; 445 return vec2(Fc, Frc); 448#ifdef STANDARD_MATERIAL_ANISOTROPY 450fn specular_anisotropy( 451 input: ptr<function, LightingInput>, 452 derived_input: ptr<function, DerivedLightingInput>, 455 specular_intensity: f32, 458 let NdotV = (*input).layers[LAYER_BASE].NdotV; 460 let F0 = (*input).F0_; 461 let anisotropy = (*input).anisotropy; 462 let Ta = (*input).Ta; 463 let Ba = (*input).Ba; 464 let H = (*derived_input).H; 465 let NdotL = (*derived_input).NdotL; 466 let NdotH = (*derived_input).NdotH; 467 let LdotH = (*derived_input).LdotH; 469 let TdotL = dot(Ta, L); 470 let BdotL = dot(Ba, L); 471 let TdotH = dot(Ta, H); 472 let BdotH = dot(Ba, H); 473 let TdotV = dot(Ta, V); 474 let BdotV = dot(Ba, V); 476 let ab = roughness * roughness; 477 let at = mix(ab, 1.0, anisotropy * anisotropy); 479 let Da = D_GGX_anisotropic(at, ab, NdotH, TdotH, BdotH); 480 let Va = V_GGX_anisotropic(at, ab, NdotL, NdotV, BdotV, TdotV, TdotL, BdotL); 481 let Fa = fresnel(F0, LdotH); 484 let Fr = specular_multiscatter(Da, Va, Fa, F0, (*input).F_ab, specular_intensity); 488#endif // STANDARD_MATERIAL_ANISOTROPY 505 input: ptr<function, LightingInput>, 506 derived_input: ptr<function, DerivedLightingInput>, 509 let roughness = (*input).layers[LAYER_BASE].roughness; 510 let NdotV = (*input).layers[LAYER_BASE].NdotV; 511 let NdotL = (*derived_input).NdotL; 512 let LdotH = (*derived_input).LdotH; 514 let f90 = 0.5 + 2.0 * roughness * LdotH * LdotH; 515 let lightScatter = F_Schlick(1.0, f90, NdotL); 516 let viewScatter = F_Schlick(1.0, f90, NdotV); 517 return lightScatter * viewScatter * (1.0 / PI); 523fn F_AB(perceptual_roughness: f32, NdotV: f32) -> vec2<f32> { 524 let c0 = vec4<f32>(-1.0, -0.0275, -0.572, 0.022); 525 let c1 = vec4<f32>(1.0, 0.0425, 1.04, -0.04); 526 let r = perceptual_roughness * c0 + c1; 527 let a004 = min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x + r.y; 528 return vec2<f32>(-1.04, 1.04) * a004 + r.zw; 531fn EnvBRDFApprox(F0: vec3<f32>, F_ab: vec2<f32>) -> vec3<f32> { 532 return F0 * F_ab.x + F_ab.y; 535fn perceptualRoughnessToRoughness(perceptualRoughness: f32) -> f32 { 539 let clampedPerceptualRoughness = clamp(perceptualRoughness, 0.089, 1.0); 540 return clampedPerceptualRoughness * clampedPerceptualRoughness; 544const CUBEMAP_TYPE_CROSS_VERTICAL: u32 = 0; 545const CUBEMAP_TYPE_CROSS_HORIZONTAL: u32 = 1; 546const CUBEMAP_TYPE_SEQUENCE_VERTICAL: u32 = 2; 547const CUBEMAP_TYPE_SEQUENCE_HORIZONTAL: u32 = 3; 549const X_PLUS: u32 = 0; 550const X_MINUS: u32 = 1; 551const Y_PLUS: u32 = 2; 552const Y_MINUS: u32 = 3; 553const Z_MINUS: u32 = 4; 554const Z_PLUS: u32 = 5; 556fn cubemap_uv(direction: vec3<f32>, cubemap_type: u32) -> vec2<f32> { 557 let abs_direction = abs(direction); 558 let max_axis = max(abs_direction.x, max(abs_direction.y, abs_direction.z)); 560 let face_index = select( 561 select(X_PLUS, X_MINUS, direction.x < 0.0), 563 select(Y_PLUS, Y_MINUS, direction.y < 0.0), 564 select(Z_PLUS, Z_MINUS, direction.z < 0.0), 565 max_axis != abs_direction.y 567 max_axis != abs_direction.x 570 var face_uv: vec2<f32>; 572 var corner_uv: vec2<u32> = vec2(0, 0); 573 var face_size: vec2<f32>; 576 case X_PLUS: { face_uv = vec2<f32>(direction.z, -direction.y); divisor = direction.x; } 577 case X_MINUS: { face_uv = vec2<f32>(-direction.z, -direction.y); divisor = -direction.x; } 578 case Y_PLUS: { face_uv = vec2<f32>(direction.x, -direction.z); divisor = direction.y; } 579 case Y_MINUS: { face_uv = vec2<f32>(direction.x, direction.z); divisor = -direction.y; } 580 case Z_PLUS: { face_uv = vec2<f32>(direction.x, direction.y); divisor = direction.z; } 581 case Z_MINUS: { face_uv = vec2<f32>(direction.x, -direction.y); divisor = -direction.z; } 584 face_uv = (face_uv / divisor) * 0.5 + 0.5; 586 switch cubemap_type { 587 case CUBEMAP_TYPE_CROSS_VERTICAL: { 588 face_size = vec2(1.0/3.0, 1.0/4.0); 589 corner_uv = vec2<u32>((0x111102u >> (4 * face_index)) & 0xFu, (0x132011u >> (4 * face_index)) & 0xFu); 591 case CUBEMAP_TYPE_CROSS_HORIZONTAL: { 592 face_size = vec2(1.0/4.0, 1.0/3.0); 593 corner_uv = vec2<u32>((0x131102u >> (4 * face_index)) & 0xFu, (0x112011u >> (4 * face_index)) & 0xFu); 595 case CUBEMAP_TYPE_SEQUENCE_HORIZONTAL: { 596 face_size = vec2(1.0/6.0, 1.0); 597 corner_uv.x = face_index; 599 case CUBEMAP_TYPE_SEQUENCE_VERTICAL: { 600 face_size = vec2(1.0, 1.0/6.0); 601 corner_uv.y = face_index; 606 return (vec2<f32>(corner_uv) + face_uv) * face_size; 617fn specular_fix_remap(a: f32) -> f32 { 618 let inv_a_sq = (1.0 - a) * (1.0 - a); 619 return 1.0 - inv_a_sq * inv_a_sq; 624 input: ptr<function, LightingInput>, 625 enable_diffuse: bool, 626 enable_texture: bool, 629 let diffuse_color = (*input).diffuse_color; 631 let N = (*input).layers[LAYER_BASE].N; 634 let light = &view_bindings::clusterable_objects.data[light_id]; 635 let light_to_frag = (*light).position_radius.xyz - P; 636 let L = normalize(light_to_frag); 637 let distance_square = dot(light_to_frag, light_to_frag); 638 let distance = sqrt(distance_square); 639 let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w); 643 let a = (*input).layers[LAYER_BASE].roughness; 644 let specular_L_a_prime = compute_specular_layer_values_for_point_light( 649 (*light).position_radius.w, 652 let L_spec = specular_L_a_prime.xyz; 653 let a_prime = specular_L_a_prime.w; 654 var specular_derived_input = derive_lighting_input(N, V, L_spec); 656 let normalizationFactor = a / a_prime; 657 let specular_intensity = normalizationFactor * normalizationFactor; 659 let brdf_roughness = mix(a, a_prime, specular_fix_remap(a)); 661#ifdef STANDARD_MATERIAL_ANISOTROPY 662 var specular_light = specular_anisotropy(input, &specular_derived_input, L, brdf_roughness, specular_intensity); 663#else // STANDARD_MATERIAL_ANISOTROPY 664 var specular_light = specular(input, &specular_derived_input, brdf_roughness, specular_intensity); 665#endif // STANDARD_MATERIAL_ANISOTROPY 668 let light_radius = (*light).position_radius.w; 669 if light_radius > 0.0 { 670 let solid_angle = light_radius * light_radius / (distance * distance); 671 specular_light *= saturate(specular_derived_input.NdotL / max(specular_derived_input.NdotL + solid_angle, 1e-4)); 676#ifdef STANDARD_MATERIAL_CLEARCOAT 678 let clearcoat_N = (*input).layers[LAYER_CLEARCOAT].N; 679 let clearcoat_strength = (*input).clearcoat_strength; 680 let clearcoat_a = (*input).layers[LAYER_CLEARCOAT].roughness; 685 let clearcoat_specular_L_a_prime = compute_specular_layer_values_for_point_light( 690 (*light).position_radius.w, 693 let L_clearcoat_spec = clearcoat_specular_L_a_prime.xyz; 694 let clearcoat_a_prime = clearcoat_specular_L_a_prime.w; 695 var clearcoat_specular_derived_input = 696 derive_lighting_input(clearcoat_N, V, L_clearcoat_spec); 699 let clearcoat_normalizationFactor = clearcoat_a / clearcoat_a_prime; 701 let clearcoat_specular_intensity = clearcoat_normalizationFactor * clearcoat_normalizationFactor; 703 let clearcoat_brdf_roughness = mix(clearcoat_a, clearcoat_a_prime, specular_fix_remap(clearcoat_a)); 705 let Fc_Frc = specular_clearcoat( 707 &clearcoat_specular_derived_input, 709 clearcoat_brdf_roughness, 710 clearcoat_specular_intensity 712 let inv_Fc = 1.0 - Fc_Frc.r; 716 if light_radius > 0.0 { 717 let solid_angle = light_radius * light_radius / (distance * distance); 718 Frc *= saturate(clearcoat_specular_derived_input.NdotL / max(clearcoat_specular_derived_input.NdotL + solid_angle, 1e-4)); 720#endif // STANDARD_MATERIAL_CLEARCOAT 724 var derived_input = derive_lighting_input(N, V, L); 725 var diffuse = vec3(0.0); 726 if (enable_diffuse) { 727 diffuse = diffuse_color * Fd_Burley(input, &derived_input); 743 var color_times_NdotL: vec3<f32>; 744#ifdef STANDARD_MATERIAL_CLEARCOAT 748 color_times_NdotL = (diffuse * derived_input.NdotL + specular_light * specular_derived_input.NdotL * inv_Fc) * inv_Fc + Frc * clearcoat_specular_derived_input.NdotL; 749#else // STANDARD_MATERIAL_CLEARCOAT 750 color_times_NdotL = diffuse * derived_input.NdotL + specular_light * specular_derived_input.NdotL; 751#endif // STANDARD_MATERIAL_CLEARCOAT 753 var texture_sample = 1f; 756 if enable_texture && (*light).decal_index != 0xFFFFFFFFu { 757 let relative_position = (view_bindings::clustered_decals.decals[(*light).decal_index].local_from_world * vec4(P, 1.0)).xyz; 758 let cubemap_type = view_bindings::clustered_decals.decals[(*light).decal_index].tag; 759 let decal_uv = cubemap_uv(relative_position, cubemap_type); 760 let image_index = view_bindings::clustered_decals.decals[(*light).decal_index].base_color_texture_index; 762 texture_sample = textureSampleLevel( 763 view_bindings::clustered_decal_textures[image_index], 764 view_bindings::clustered_decal_sampler, 771 return color_times_NdotL * (*light).color_inverse_square_range.rgb * 772 rangeAttenuation * texture_sample; 777 input: ptr<function, LightingInput>, 781 let point_light = point_light(light_id, input, enable_diffuse, false); 783 let light = &view_bindings::clusterable_objects.data[light_id]; 786 var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y); 787 spot_dir.y = sqrt(max(0.0, 1.0 - spot_dir.x * spot_dir.x - spot_dir.z * spot_dir.z)); 788 if ((*light).flags & POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u { 789 spot_dir.y = -spot_dir.y; 791 let light_to_frag = (*light).position_radius.xyz - (*input).P.xyz; 796 let cd = dot(-spot_dir, normalize(light_to_frag)); 797 let attenuation = saturate(cd * (*light).light_custom_data.z + (*light).light_custom_data.w); 798 let spot_attenuation = attenuation * attenuation; 800 var texture_sample = 1f; 803 if (*light).decal_index != 0xFFFFFFFFu { 804 let local_position = (view_bindings::clustered_decals.decals[(*light).decal_index].local_from_world * 805 vec4((*input).P, 1.0)).xyz; 806 if local_position.z < 0.0 { 807 let decal_uv = (local_position.xy / (local_position.z * (*light).spot_light_tan_angle)) * vec2(-0.5, 0.5) + 0.5; 808 let image_index = view_bindings::clustered_decals.decals[(*light).decal_index].base_color_texture_index; 810 texture_sample = textureSampleLevel( 811 view_bindings::clustered_decal_textures[image_index], 812 view_bindings::clustered_decal_sampler, 820 return point_light * spot_attenuation * texture_sample; 825 input: ptr<function, LightingInput>, 829 let diffuse_color = (*input).diffuse_color; 830 let NdotV = (*input).layers[LAYER_BASE].NdotV; 831 let N = (*input).layers[LAYER_BASE].N; 833 let roughness = (*input).layers[LAYER_BASE].roughness; 835 let light = &view_bindings::lights.directional_lights[light_id]; 837 let L = (*light).direction_to_light.xyz; 838 var derived_input = derive_lighting_input(N, V, L); 840 var diffuse = vec3(0.0); 841 if (enable_diffuse) { 842 diffuse = diffuse_color * Fd_Burley(input, &derived_input); 845#ifdef STANDARD_MATERIAL_ANISOTROPY 846 let specular_light = specular_anisotropy(input, &derived_input, L, roughness, 1.0); 847#else // STANDARD_MATERIAL_ANISOTROPY 848 let specular_light = specular(input, &derived_input, roughness, 1.0); 849#endif // STANDARD_MATERIAL_ANISOTROPY 851#ifdef STANDARD_MATERIAL_CLEARCOAT 852 let clearcoat_N = (*input).layers[LAYER_CLEARCOAT].N; 853 let clearcoat_strength = (*input).clearcoat_strength; 854 let clearcoat_roughness = (*input).layers[LAYER_CLEARCOAT].roughness; 859 var derived_clearcoat_input = derive_lighting_input(clearcoat_N, V, L); 862 specular_clearcoat(input, &derived_clearcoat_input, clearcoat_strength, clearcoat_roughness, 1.0); 863 let inv_Fc = 1.0 - Fc_Frc.r; 865#endif // STANDARD_MATERIAL_CLEARCOAT 867 var color: vec3<f32>; 868#ifdef STANDARD_MATERIAL_CLEARCOAT 872 color = (diffuse + specular_light * inv_Fc) * inv_Fc * derived_input.NdotL + 873 Frc * derived_clearcoat_input.NdotL; 874#else // STANDARD_MATERIAL_CLEARCOAT 875 color = (diffuse + specular_light) * derived_input.NdotL; 876#endif // STANDARD_MATERIAL_CLEARCOAT 878 var texture_sample = 1f; 881 if (*light).decal_index != 0xFFFFFFFFu { 882 let local_position = (view_bindings::clustered_decals.decals[(*light).decal_index].local_from_world * 883 vec4((*input).P, 1.0)).xyz; 884 let decal_uv = local_position.xy * vec2(-0.5, 0.5) + 0.5; 887 if (view_bindings::clustered_decals.decals[(*light).decal_index].tag != 0u) 888 || all(clamp(decal_uv, vec2(0.0), vec2(1.0)) == decal_uv) 890 let image_index = view_bindings::clustered_decals.decals[(*light).decal_index].base_color_texture_index; 892 texture_sample = textureSampleLevel( 893 view_bindings::clustered_decal_textures[image_index], 894 view_bindings::clustered_decal_sampler, 895 decal_uv - floor(decal_uv), 904color *= (*light).color.rgb * texture_sample; 908 let atmosphere = view_bindings::atmosphere_data.atmosphere; 909 let O = vec3(0.0, atmosphere.bottom_radius, 0.0); 910 let P_scaled = P * vec3(view_bindings::atmosphere_data.settings.scene_units_to_m); 911 let P_as = P_scaled + O; 912 let P_clamped = clamp_to_surface(atmosphere, P_as); 913 let r = length(P_clamped); 914 let local_up = normalize(P_clamped); 915 let mu_light = dot(L, local_up); 918 let transmittance = sample_transmittance_lut(r, mu_light); 919 let sun_visibility = calculate_visible_sun_ratio(atmosphere, r, mu_light, (*light).sun_disk_angular_size); 922 color *= transmittance * sun_visibility; 929fn sample_transmittance_lut(r: f32, mu: f32) -> vec3<f32> { 930 let uv = transmittance_lut_r_mu_to_uv(view_bindings::atmosphere_data.atmosphere, r, mu); 931 return textureSampleLevel( 932 view_bindings::atmosphere_transmittance_texture, 933 view_bindings::atmosphere_transmittance_sampler, uv, 0.0).rgb;