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, orthonormalize} 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>, 85 F0_dielectric: vec3<f32>, 86 F0_metallic: vec3<f32>, 95#ifdef STANDARD_MATERIAL_CLEARCOAT 97 clearcoat_strength: f32, 98#endif // STANDARD_MATERIAL_CLEARCOAT 100#ifdef STANDARD_MATERIAL_ANISOTROPY 110#endif // STANDARD_MATERIAL_ANISOTROPY 114struct DerivedLightingInput { 131fn getDistanceAttenuation(distanceSquare: f32, inverseRangeSquared: f32) -> f32 { 132 return getRangeFalloff(distanceSquare, inverseRangeSquared) * 1.0 / max(distanceSquare, 0.0001); 136fn getRangeFalloff(distanceSquare: f32, inverseRangeSquared: f32) -> f32 { 137 let factor = distanceSquare * inverseRangeSquared; 138 let smoothFactor = saturate(1.0 - factor * factor); 139 return smoothFactor * smoothFactor; 149fn D_GGX(roughness: f32, NdotH: f32) -> f32 { 150 let oneMinusNdotHSquared = 1.0 - NdotH * NdotH; 151 let a = NdotH * roughness; 152 let k = roughness / (oneMinusNdotHSquared + a * a); 153 let d = k * k * (1.0 / PI); 173fn D_GGX_anisotropic(at: f32, ab: f32, NdotH: f32, TdotH: f32, BdotH: f32) -> f32 { 175 let f = vec3(ab * TdotH, at * BdotH, a2 * NdotH); 176 let w2 = a2 / dot(f, f); 177 let d = a2 * w2 * w2 * (1.0 / PI); 188fn V_SmithGGXCorrelated(roughness: f32, NdotV: f32, NdotL: f32) -> f32 { 189 let a2 = roughness * roughness; 190 let lambdaV = NdotL * sqrt((NdotV - a2 * NdotV) * NdotV + a2); 191 let lambdaL = NdotV * sqrt((NdotL - a2 * NdotL) * NdotL + a2); 192 let v = 0.5 / (lambdaV + lambdaL); 207 let GGX_V = NdotL * length(vec3(at * TdotV, ab * BdotV, NdotV)); 208 let GGX_L = NdotV * length(vec3(at * TdotL, ab * BdotL, NdotL)); 209 let v = 0.5 / (GGX_V + GGX_L); 215fn ggx_vndf_pdf(i: vec3<f32>, NdotH: f32, roughness: f32) -> f32 { 216 let ndf = D_GGX(roughness, NdotH); 219 let ai = roughness * i.xy; 220 let len2 = dot(ai, ai); 221 let t = sqrt(len2 + i.z * i.z); 224 let s = 1.0 + length(i.xy); 227 let k = (1.0 - a2) * s2 / (s2 + a2 * i.z * i.z); 228 return ndf / (2.0 * (k * i.z + t)); 232 return ndf * (t - i.z) / (2.0 * len2); 236fn sample_visible_ggx( 243 let alpha = roughness; 246 let wi_n = dot(view, n); 247 let wi_z = -n * wi_n; 248 let wi_xy = view + wi_z; 251 let wi_std = -normalize(alpha * wi_xy + wi_z); 254 let wi_std_z = dot(wi_std, n); 258 let s = 1.0 + length(wi_xy); 259 let a = clamp(alpha, 0.0, 1.0); 262 let k = (1.0 - a2) * s2 / (s2 + a2 * wi_n * wi_n); 263 let b = select(wi_std_z, k * wi_std_z, wi_n > 0.0); 266 let z = 1.0 - xi.y * (1.0 + b); 267 let sin_theta = sqrt(max(0.0, 1.0 - z * z)); 268 let phi = 2.0 * PI * xi.x - PI; 269 let x = sin_theta * cos(phi); 270 let y = sin_theta * sin(phi); 271 let c_std = vec3f(x, y, z); 274 let TBN = orthonormalize(n); 278 let wm_std = c + wi_std; 279 let wm_std_z = n * dot(n, wm_std); 280 let wm_std_xy = wm_std_z - wm_std; 283 let H = normalize(alpha * wm_std_xy + wm_std_z); 286 return reflect(-view, H); 290fn G_Smith(NdotV: f32, NdotL: f32, roughness: f32) -> f32 { 291 let k = roughness / 2.0; 292 let GGXL = NdotL / (NdotL * (1.0 - k) + k); 293 let GGXV = NdotV / (NdotV * (1.0 - k) + k); 301fn V_Kelemen(LdotH: f32) -> f32 { 302 return 0.25 / (LdotH * LdotH); 308fn F_Schlick_vec(f0: vec3<f32>, f90: f32, VdotH: f32) -> vec3<f32> { 310 return f0 + (f90 - f0) * pow(1.0 - VdotH, 5.0); 313fn F_Schlick(f0: f32, f90: f32, VdotH: f32) -> f32 { 315 return f0 + (f90 - f0) * pow(1.0 - VdotH, 5.0); 318fn fresnel(f0: vec3<f32>, LdotH: f32) -> vec3<f32> { 321 let f90 = saturate(dot(f0, vec3<f32>(50.0 * 0.33))); 322 return F_Schlick_vec(f0, f90, LdotH); 330fn specular_multiscatter( 336 specular_intensity: f32, 338 var Fr = (specular_intensity * D * V) * F; 341 Fr *= 1.0 + F0 * (1.0 / (F_ab.x + F_ab.y) - 1.0); 349fn derive_lighting_input(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>) -> DerivedLightingInput { 350 var input: DerivedLightingInput; 351 var H: vec3<f32> = normalize(L + V); 353 input.NdotL = saturate(dot(N, L)); 354 input.NdotH = saturate(dot(N, H)); 355 input.LdotH = saturate(dot(L, H)); 360fn compute_specular_layer_values_for_point_light( 361 input: ptr<function, LightingInput>, 364 light_to_frag: vec3<f32>, 369 let R = (*input).layers[layer].R; 370 let a = (*input).layers[layer].roughness; 374 var LtFdotR = dot(light_to_frag, R); 387 LtFdotR = max(0.0001, LtFdotR); 389 let centerToRay = LtFdotR * R - light_to_frag; 390 let closestPoint = light_to_frag + centerToRay * saturate( 391 light_radius * inverseSqrt(dot(centerToRay, centerToRay))); 392 let LspecLengthInverse = inverseSqrt(dot(closestPoint, closestPoint)); 396 let a_prime = saturate(a + light_radius / (2.0 * distance)); 398 let L: vec3<f32> = closestPoint * LspecLengthInverse; 399 return vec4(L, a_prime); 405 input: ptr<function, LightingInput>, 406 derived_input: ptr<function, DerivedLightingInput>, 408 specular_intensity: f32, 411 let NdotV = (*input).layers[LAYER_BASE].NdotV; 412 let F0 = mix((*input).F0_dielectric, (*input).F0_metallic, (*input).metallic); 413 let NdotL = (*derived_input).NdotL; 414 let NdotH = (*derived_input).NdotH; 415 let LdotH = (*derived_input).LdotH; 418 let D = D_GGX(roughness, NdotH); 420 let V = V_SmithGGXCorrelated(roughness, NdotV, NdotL); 422 let F = fresnel(F0, LdotH); 425 let Fr = specular_multiscatter(D, V, F, F0, (*input).F_ab, specular_intensity); 434fn specular_clearcoat( 435 input: ptr<function, LightingInput>, 436 derived_input: ptr<function, DerivedLightingInput>, 437 clearcoat_strength: f32, 439 specular_intensity: f32, 442 let NdotH = (*derived_input).NdotH; 443 let LdotH = (*derived_input).LdotH; 446 let Dc = D_GGX(roughness, NdotH); 448 let Vc = V_Kelemen(LdotH); 450 let Fc = F_Schlick(0.04, 1.0, LdotH) * clearcoat_strength; 452 let Frc = (specular_intensity * Dc * Vc) * Fc; 453 return vec2(Fc, Frc); 456#ifdef STANDARD_MATERIAL_ANISOTROPY 458fn specular_anisotropy( 459 input: ptr<function, LightingInput>, 460 derived_input: ptr<function, DerivedLightingInput>, 463 specular_intensity: f32, 466 let NdotV = (*input).layers[LAYER_BASE].NdotV; 468 let F0 = mix((*input).F0_dielectric, (*input).F0_metallic, (*input).metallic); 469 let anisotropy = (*input).anisotropy; 470 let Ta = (*input).Ta; 471 let Ba = (*input).Ba; 472 let H = (*derived_input).H; 473 let NdotL = (*derived_input).NdotL; 474 let NdotH = (*derived_input).NdotH; 475 let LdotH = (*derived_input).LdotH; 477 let TdotL = dot(Ta, L); 478 let BdotL = dot(Ba, L); 479 let TdotH = dot(Ta, H); 480 let BdotH = dot(Ba, H); 481 let TdotV = dot(Ta, V); 482 let BdotV = dot(Ba, V); 484 let ab = roughness * roughness; 485 let at = mix(ab, 1.0, anisotropy * anisotropy); 487 let Da = D_GGX_anisotropic(at, ab, NdotH, TdotH, BdotH); 488 let Va = V_GGX_anisotropic(at, ab, NdotL, NdotV, BdotV, TdotV, TdotL, BdotL); 489 let Fa = fresnel(F0, LdotH); 492 let Fr = specular_multiscatter(Da, Va, Fa, F0, (*input).F_ab, specular_intensity); 496#endif // STANDARD_MATERIAL_ANISOTROPY 513 input: ptr<function, LightingInput>, 514 derived_input: ptr<function, DerivedLightingInput>, 517 let roughness = (*input).layers[LAYER_BASE].roughness; 518 let NdotV = (*input).layers[LAYER_BASE].NdotV; 519 let NdotL = (*derived_input).NdotL; 520 let LdotH = (*derived_input).LdotH; 522 let f90 = 0.5 + 2.0 * roughness * LdotH * LdotH; 523 let lightScatter = F_Schlick(1.0, f90, NdotL); 524 let viewScatter = F_Schlick(1.0, f90, NdotV); 525 return lightScatter * viewScatter * (1.0 / PI); 529fn F_AB(perceptual_roughness: f32, NdotV: f32) -> vec2<f32> { 531 return textureSampleLevel(view_bindings::dfg_lut, view_bindings::dfg_lut_sampler, vec2<f32>(NdotV, perceptual_roughness), 0.0).rg; 534 let c0 = vec4<f32>(-1.0, -0.0275, -0.572, 0.022); 535 let c1 = vec4<f32>(1.0, 0.0425, 1.04, -0.04); 536 let r = perceptual_roughness * c0 + c1; 537 let a004 = min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x + r.y; 539 let f_ab_epsilon = 0.00005; 540 return max(vec2<f32>(-1.04, 1.04) * a004 + r.zw, vec2<f32>(f_ab_epsilon)); 544fn EnvBRDFApprox(F0: vec3<f32>, F_ab: vec2<f32>) -> vec3<f32> { 545 return F0 * F_ab.x + F_ab.y; 548fn perceptualRoughnessToRoughness(perceptualRoughness: f32) -> f32 { 552 let clampedPerceptualRoughness = clamp(perceptualRoughness, 0.089, 1.0); 553 return clampedPerceptualRoughness * clampedPerceptualRoughness; 557const CUBEMAP_TYPE_CROSS_VERTICAL: u32 = 0; 558const CUBEMAP_TYPE_CROSS_HORIZONTAL: u32 = 1; 559const CUBEMAP_TYPE_SEQUENCE_VERTICAL: u32 = 2; 560const CUBEMAP_TYPE_SEQUENCE_HORIZONTAL: u32 = 3; 562const X_PLUS: u32 = 0; 563const X_MINUS: u32 = 1; 564const Y_PLUS: u32 = 2; 565const Y_MINUS: u32 = 3; 566const Z_MINUS: u32 = 4; 567const Z_PLUS: u32 = 5; 569fn cubemap_uv(direction: vec3<f32>, cubemap_type: u32) -> vec2<f32> { 570 let abs_direction = abs(direction); 571 let max_axis = max(abs_direction.x, max(abs_direction.y, abs_direction.z)); 573 let face_index = select( 574 select(X_PLUS, X_MINUS, direction.x < 0.0), 576 select(Y_PLUS, Y_MINUS, direction.y < 0.0), 577 select(Z_PLUS, Z_MINUS, direction.z < 0.0), 578 max_axis != abs_direction.y 580 max_axis != abs_direction.x 583 var face_uv: vec2<f32>; 585 var corner_uv: vec2<u32> = vec2(0, 0); 586 var face_size: vec2<f32>; 589 case X_PLUS: { face_uv = vec2<f32>(direction.z, -direction.y); divisor = direction.x; } 590 case X_MINUS: { face_uv = vec2<f32>(-direction.z, -direction.y); divisor = -direction.x; } 591 case Y_PLUS: { face_uv = vec2<f32>(direction.x, -direction.z); divisor = direction.y; } 592 case Y_MINUS: { face_uv = vec2<f32>(direction.x, direction.z); divisor = -direction.y; } 593 case Z_PLUS: { face_uv = vec2<f32>(direction.x, direction.y); divisor = direction.z; } 594 case Z_MINUS: { face_uv = vec2<f32>(direction.x, -direction.y); divisor = -direction.z; } 597 face_uv = (face_uv / divisor) * 0.5 + 0.5; 599 switch cubemap_type { 600 case CUBEMAP_TYPE_CROSS_VERTICAL: { 601 face_size = vec2(1.0/3.0, 1.0/4.0); 602 corner_uv = vec2<u32>((0x111102u >> (4 * face_index)) & 0xFu, (0x132011u >> (4 * face_index)) & 0xFu); 604 case CUBEMAP_TYPE_CROSS_HORIZONTAL: { 605 face_size = vec2(1.0/4.0, 1.0/3.0); 606 corner_uv = vec2<u32>((0x131102u >> (4 * face_index)) & 0xFu, (0x112011u >> (4 * face_index)) & 0xFu); 608 case CUBEMAP_TYPE_SEQUENCE_HORIZONTAL: { 609 face_size = vec2(1.0/6.0, 1.0); 610 corner_uv.x = face_index; 612 case CUBEMAP_TYPE_SEQUENCE_VERTICAL: { 613 face_size = vec2(1.0, 1.0/6.0); 614 corner_uv.y = face_index; 619 return (vec2<f32>(corner_uv) + face_uv) * face_size; 630fn specular_fix_remap(a: f32) -> f32 { 631 let inv_a_sq = (1.0 - a) * (1.0 - a); 632 return 1.0 - inv_a_sq * inv_a_sq; 637 input: ptr<function, LightingInput>, 638 enable_diffuse: bool, 639 enable_texture: bool, 642 let diffuse_color = (*input).diffuse_color; 644 let N = (*input).layers[LAYER_BASE].N; 647 let light = &view_bindings::clustered_lights.data[light_id]; 648 let light_to_frag = (*light).position_radius.xyz - P; 649 let L = normalize(light_to_frag); 650 let distance_square = dot(light_to_frag, light_to_frag); 651 let distance = sqrt(distance_square); 652 let rangeAttenuation = getDistanceAttenuation(distance_square, (*light).color_inverse_square_range.w); 656 let a = (*input).layers[LAYER_BASE].roughness; 657 let specular_L_a_prime = compute_specular_layer_values_for_point_light( 662 (*light).position_radius.w, 665 let L_spec = specular_L_a_prime.xyz; 666 let a_prime = specular_L_a_prime.w; 667 var specular_derived_input = derive_lighting_input(N, V, L_spec); 669 let normalizationFactor = a / a_prime; 670 let specular_intensity = normalizationFactor * normalizationFactor; 672 let brdf_roughness = mix(a, a_prime, specular_fix_remap(a)); 674#ifdef STANDARD_MATERIAL_ANISOTROPY 675 var specular_light = specular_anisotropy(input, &specular_derived_input, L, brdf_roughness, specular_intensity); 676#else // STANDARD_MATERIAL_ANISOTROPY 677 var specular_light = specular(input, &specular_derived_input, brdf_roughness, specular_intensity); 678#endif // STANDARD_MATERIAL_ANISOTROPY 681 let light_radius = (*light).position_radius.w; 682 if light_radius > 0.0 { 683 let solid_angle = light_radius * light_radius / (distance * distance); 684 specular_light *= saturate(specular_derived_input.NdotL / max(specular_derived_input.NdotL + solid_angle, 1e-4)); 689#ifdef STANDARD_MATERIAL_CLEARCOAT 691 let clearcoat_N = (*input).layers[LAYER_CLEARCOAT].N; 692 let clearcoat_strength = (*input).clearcoat_strength; 693 let clearcoat_a = (*input).layers[LAYER_CLEARCOAT].roughness; 698 let clearcoat_specular_L_a_prime = compute_specular_layer_values_for_point_light( 703 (*light).position_radius.w, 706 let L_clearcoat_spec = clearcoat_specular_L_a_prime.xyz; 707 let clearcoat_a_prime = clearcoat_specular_L_a_prime.w; 708 var clearcoat_specular_derived_input = 709 derive_lighting_input(clearcoat_N, V, L_clearcoat_spec); 712 let clearcoat_normalizationFactor = clearcoat_a / clearcoat_a_prime; 714 let clearcoat_specular_intensity = clearcoat_normalizationFactor * clearcoat_normalizationFactor; 716 let clearcoat_brdf_roughness = mix(clearcoat_a, clearcoat_a_prime, specular_fix_remap(clearcoat_a)); 718 let Fc_Frc = specular_clearcoat( 720 &clearcoat_specular_derived_input, 722 clearcoat_brdf_roughness, 723 clearcoat_specular_intensity 725 let inv_Fc = 1.0 - Fc_Frc.r; 729 if light_radius > 0.0 { 730 let solid_angle = light_radius * light_radius / (distance * distance); 731 Frc *= saturate(clearcoat_specular_derived_input.NdotL / max(clearcoat_specular_derived_input.NdotL + solid_angle, 1e-4)); 733#endif // STANDARD_MATERIAL_CLEARCOAT 737 var derived_input = derive_lighting_input(N, V, L); 738 var diffuse = vec3(0.0); 739 if (enable_diffuse) { 740 diffuse = diffuse_color * Fd_Burley(input, &derived_input); 756 var color_times_NdotL: vec3<f32>; 757#ifdef STANDARD_MATERIAL_CLEARCOAT 761 color_times_NdotL = (diffuse * derived_input.NdotL + specular_light * specular_derived_input.NdotL * inv_Fc) * inv_Fc + Frc * clearcoat_specular_derived_input.NdotL; 762#else // STANDARD_MATERIAL_CLEARCOAT 763 color_times_NdotL = diffuse * derived_input.NdotL + specular_light * specular_derived_input.NdotL; 764#endif // STANDARD_MATERIAL_CLEARCOAT 766 var texture_sample = 1f; 769 if enable_texture && (*light).decal_index != 0xFFFFFFFFu { 770 let relative_position = (view_bindings::clustered_decals.decals[(*light).decal_index].local_from_world * vec4(P, 1.0)).xyz; 771 let cubemap_type = view_bindings::clustered_decals.decals[(*light).decal_index].tag; 772 let decal_uv = cubemap_uv(relative_position, cubemap_type); 773 let image_index = view_bindings::clustered_decals.decals[(*light).decal_index].base_color_texture_index; 775 texture_sample = textureSampleLevel( 776 view_bindings::clustered_decal_textures[image_index], 777 view_bindings::clustered_decal_sampler, 784 return color_times_NdotL * (*light).color_inverse_square_range.rgb * 785 rangeAttenuation * texture_sample; 790 input: ptr<function, LightingInput>, 794 let point_light = point_light(light_id, input, enable_diffuse, false); 796 let light = &view_bindings::clustered_lights.data[light_id]; 799 var spot_dir = vec3<f32>((*light).light_custom_data.x, 0.0, (*light).light_custom_data.y); 800 spot_dir.y = sqrt(max(0.0, 1.0 - spot_dir.x * spot_dir.x - spot_dir.z * spot_dir.z)); 801 if ((*light).flags & POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE) != 0u { 802 spot_dir.y = -spot_dir.y; 804 let light_to_frag = (*light).position_radius.xyz - (*input).P.xyz; 809 let cd = dot(-spot_dir, normalize(light_to_frag)); 810 let attenuation = saturate(cd * (*light).light_custom_data.z + (*light).light_custom_data.w); 811 let spot_attenuation = attenuation * attenuation; 813 var texture_sample = 1f; 816 if (*light).decal_index != 0xFFFFFFFFu { 817 let local_position = (view_bindings::clustered_decals.decals[(*light).decal_index].local_from_world * 818 vec4((*input).P, 1.0)).xyz; 819 if local_position.z < 0.0 { 820 let decal_uv = (local_position.xy / (local_position.z * (*light).spot_light_tan_angle)) * vec2(-0.5, 0.5) + 0.5; 821 let image_index = view_bindings::clustered_decals.decals[(*light).decal_index].base_color_texture_index; 823 texture_sample = textureSampleLevel( 824 view_bindings::clustered_decal_textures[image_index], 825 view_bindings::clustered_decal_sampler, 833 return point_light * spot_attenuation * texture_sample; 838 input: ptr<function, LightingInput>, 842 let diffuse_color = (*input).diffuse_color; 843 let NdotV = (*input).layers[LAYER_BASE].NdotV; 844 let N = (*input).layers[LAYER_BASE].N; 846 let roughness = (*input).layers[LAYER_BASE].roughness; 848 let light = &view_bindings::lights.directional_lights[light_id]; 850 let L = (*light).direction_to_light.xyz; 851 var derived_input = derive_lighting_input(N, V, L); 853 var diffuse = vec3(0.0); 854 if (enable_diffuse) { 855 diffuse = diffuse_color * Fd_Burley(input, &derived_input); 858#ifdef STANDARD_MATERIAL_ANISOTROPY 859 let specular_light = specular_anisotropy(input, &derived_input, L, roughness, 1.0); 860#else // STANDARD_MATERIAL_ANISOTROPY 861 let specular_light = specular(input, &derived_input, roughness, 1.0); 862#endif // STANDARD_MATERIAL_ANISOTROPY 864#ifdef STANDARD_MATERIAL_CLEARCOAT 865 let clearcoat_N = (*input).layers[LAYER_CLEARCOAT].N; 866 let clearcoat_strength = (*input).clearcoat_strength; 867 let clearcoat_roughness = (*input).layers[LAYER_CLEARCOAT].roughness; 872 var derived_clearcoat_input = derive_lighting_input(clearcoat_N, V, L); 875 specular_clearcoat(input, &derived_clearcoat_input, clearcoat_strength, clearcoat_roughness, 1.0); 876 let inv_Fc = 1.0 - Fc_Frc.r; 878#endif // STANDARD_MATERIAL_CLEARCOAT 880 var color: vec3<f32>; 881#ifdef STANDARD_MATERIAL_CLEARCOAT 885 color = (diffuse + specular_light * inv_Fc) * inv_Fc * derived_input.NdotL + 886 Frc * derived_clearcoat_input.NdotL; 887#else // STANDARD_MATERIAL_CLEARCOAT 888 color = (diffuse + specular_light) * derived_input.NdotL; 889#endif // STANDARD_MATERIAL_CLEARCOAT 891 var texture_sample = 1f; 894 if (*light).decal_index != 0xFFFFFFFFu { 895 let local_position = (view_bindings::clustered_decals.decals[(*light).decal_index].local_from_world * 896 vec4((*input).P, 1.0)).xyz; 897 let decal_uv = local_position.xy * vec2(-0.5, 0.5) + 0.5; 900 if (view_bindings::clustered_decals.decals[(*light).decal_index].tag != 0u) 901 || all(clamp(decal_uv, vec2(0.0), vec2(1.0)) == decal_uv) 903 let image_index = view_bindings::clustered_decals.decals[(*light).decal_index].base_color_texture_index; 905 texture_sample = textureSampleLevel( 906 view_bindings::clustered_decal_textures[image_index], 907 view_bindings::clustered_decal_sampler, 908 decal_uv - floor(decal_uv), 917color *= (*light).color.rgb * texture_sample; 921 let atmosphere = view_bindings::atmosphere; 923 view_bindings::atmosphere.world_to_atmosphere * vec4(P, 1.0) 925 let P_clamped = clamp_to_surface(atmosphere, P_as); 926 let r = length(P_clamped); 927 let local_up = normalize(P_clamped); 928 let mu_light = dot(L, local_up); 931 let transmittance = sample_transmittance_lut(r, mu_light); 932 let sun_visibility = calculate_visible_sun_ratio(atmosphere, r, mu_light, (*light).sun_disk_angular_size); 935 color *= transmittance * sun_visibility; 947fn ltc_integrate_edge(v1: vec3<f32>, v2: vec3<f32>) -> f32 { 950 let a = 0.8543985 + (0.4965155 + 0.0145206 * y) * y; 951 let b = 3.4175940 + (4.1616724 + y) * y; 953 let theta_sintheta = select(0.5 * inverseSqrt(max(1.0 - x * x, 1e-7)) - v, v, x > 0.0); 954 return cross(v1, v2).z * theta_sintheta; 957fn ltc_integrate_quad( 962 points: array<vec3<f32>, 4> 964 let T1 = normalize(V - N * dot(V, N)); 965 let T2 = -cross(N, T1); 967 let Minv_local = Minv * transpose(mat3x3<f32>(T1, T2, N)); 970 var L: array<vec3<f32>, 4>; 971 L[0] = Minv_local * (points[0] - P); 972 L[1] = Minv_local * (points[1] - P); 973 L[2] = Minv_local * (points[2] - P); 974 L[3] = Minv_local * (points[3] - P); 979 var clipped: array<vec3<f32>, 5>; 980 var n_clipped: i32 = 0; 982 for (var i = 0i; i < 4i; i++) { 984 let b = L[(i + 1) % 4]; 986 clipped[n_clipped] = a; 989 if ((a.z >= 0.0) != (b.z >= 0.0)) { 990 let t = a.z / (a.z - b.z); 991 clipped[n_clipped] = mix(a, b, t); 996 if (n_clipped == 0) { 1000 for (var i = 0i; i < n_clipped; i++) { 1001 clipped[i] = normalize(clipped[i]); 1006 for (var i = 0i; i < n_clipped; i++) { 1007 sum += ltc_integrate_edge(clipped[i], clipped[(i + 1) % n_clipped]); 1013#ifdef AREA_LIGHT_LUTS 1016 input: ptr<function, LightingInput>, 1017 enable_diffuse: bool, 1020 let diffuse_color = (*input).diffuse_color; 1022 let N = (*input).layers[LAYER_BASE].N; 1024 let NdotV = (*input).layers[LAYER_BASE].NdotV; 1025 let perceptual_roughness = (*input).layers[LAYER_BASE].perceptual_roughness; 1027 let light = &view_bindings::lights.rect_lights[light_id]; 1028 let light_to_frag = (*light).position - P; 1029 let distance_square = dot(light_to_frag, light_to_frag); 1030 let inverse_range_squared = 1.0 / max((*light).range * (*light).range, 0.0001); 1031 let range_falloff = getRangeFalloff(distance_square, inverse_range_squared); 1033 let light_normal = cross((*light).up, (*light).right); 1034 let hw = (*light).right * (*light).width * 0.5; 1035 let hh = (*light).up * (*light).height * 0.5; 1036 var corners: array<vec3<f32>, 4>; 1037 corners[0] = (*light).position + hw - hh; 1038 corners[1] = (*light).position - hw - hh; 1039 corners[2] = (*light).position - hw + hh; 1040 corners[3] = (*light).position + hw + hh; 1043 if dot(light_normal, P - corners[0]) <= 0.0 { 1044 return vec3<f32>(0.0); 1047 let LUT_SCALE = 63.0 / 64.0; 1048 let LUT_BIAS = 0.5 / 64.0; 1049 let uv = vec2<f32>(perceptual_roughness, sqrt(1.0 - NdotV)) * LUT_SCALE + LUT_BIAS; 1050 let t1 = textureSampleLevel(view_bindings::area_light_luts, view_bindings::area_light_luts_sampler, uv, 0, 0.0); 1051 let t2 = textureSampleLevel(view_bindings::area_light_luts, view_bindings::area_light_luts_sampler, uv, 1, 0.0); 1054 let Minv = mat3x3<f32>( 1055 vec3<f32>(t1.x, 0.0, t1.y), 1056 vec3<f32>(0.0, 1.0, 0.0), 1057 vec3<f32>(t1.z, 0.0, t1.w), 1059 let spec = ltc_integrate_quad(N, V, P, Minv, corners); 1062 let identity = mat3x3<f32>( 1063 vec3<f32>(1.0, 0.0, 0.0), 1064 vec3<f32>(0.0, 1.0, 0.0), 1065 vec3<f32>(0.0, 0.0, 1.0), 1067 let diff = select(0.0, ltc_integrate_quad(N, V, P, identity, corners), enable_diffuse); 1070 let F0 = mix((*input).F0_dielectric, (*input).F0_metallic, (*input).metallic); 1071 let spec_weight = F0 * t2.x + (1.0 - F0) * t2.y; 1073#ifdef STANDARD_MATERIAL_CLEARCOAT 1074 let clearcoat_N = (*input).layers[LAYER_CLEARCOAT].N; 1075 let clearcoat_strength = (*input).clearcoat_strength; 1076 let clearcoat_perceptual_roughness = (*input).layers[LAYER_CLEARCOAT].perceptual_roughness; 1077 let clearcoat_NdotV = (*input).layers[LAYER_CLEARCOAT].NdotV; 1080 let cc_uv = vec2<f32>(clearcoat_perceptual_roughness, sqrt(1.0 - clearcoat_NdotV)) * LUT_SCALE + LUT_BIAS; 1081 let tc1 = textureSampleLevel(view_bindings::area_light_luts, view_bindings::area_light_luts_sampler, cc_uv, 0, 0.0); 1082 let tc2 = textureSampleLevel(view_bindings::area_light_luts, view_bindings::area_light_luts_sampler, cc_uv, 1, 0.0); 1083 let Minv_cc = mat3x3<f32>( 1084 vec3<f32>(tc1.x, 0.0, tc1.y), 1085 vec3<f32>(0.0, 1.0, 0.0), 1086 vec3<f32>(tc1.z, 0.0, tc1.w), 1088 let spec_cc = ltc_integrate_quad(clearcoat_N, V, P, Minv_cc, corners); 1091 let spec_weight_cc = 0.04 * tc2.x + (1.0 - 0.04) * tc2.y; 1092 let Fc = clearcoat_strength * spec_weight_cc; 1093 let inv_Fc = 1.0 - Fc; 1095 return ((spec_weight * spec * inv_Fc + diffuse_color * diff) * inv_Fc 1096 + spec_weight_cc * spec_cc * clearcoat_strength) * (*light).color.rgb * range_falloff; 1098 return (spec_weight * spec + diffuse_color * diff) * (*light).color.rgb * range_falloff; 1105fn sample_transmittance_lut(r: f32, mu: f32) -> vec3<f32> { 1106 let uv = transmittance_lut_r_mu_to_uv(view_bindings::atmosphere, r, mu); 1107 return textureSampleLevel( 1108 view_bindings::atmosphere_transmittance_texture, 1109 view_bindings::atmosphere_transmittance_sampler, uv, 0.0).rgb;