diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 974721f89138..488bab8302d4 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1493,6 +1493,11 @@ float SchlickFresnel(float u) { return m2 * m2 * m; // pow(m,5) } +float V_Kelemen(float LdotH) { + // Kelemen 2001, "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling" + return 0.25 / (LdotH * LdotH + 1e-4); +} + void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, vec2 screen_uv, #ifdef LIGHT_BACKLIGHT_USED vec3 backlight, @@ -1531,6 +1536,8 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di float NdotV = dot(N, V); float cNdotV = max(NdotV, 1e-4); + float cc_attenuation = 1.0; + #if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) vec3 H = normalize(V + L); #endif @@ -1543,6 +1550,20 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di float cLdotH = clamp(A + dot(L, H), 0.0, 1.0); #endif +#if defined(LIGHT_CLEARCOAT_USED) + // Clearcoat ignores normal_map, use vertex normal instead + float ccNdotH = clamp(A + dot(vertex_normal, H), 0.0, 1.0); + float cLdotH5 = SchlickFresnel(cLdotH); + + float Dr = D_GGX(ccNdotH, mix(0.001, 0.1, clearcoat_roughness)); + float Gr = V_Kelemen(cLdotH); + float Fr = mix(0.04, 1.0, cLdotH5) * clearcoat; + cc_attenuation = 1.0 - Fr; + float clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL; + + specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount; +#endif // LIGHT_CLEARCOAT_USED + if (metallic < 1.0) { float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance @@ -1564,10 +1585,10 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di diffuse_brdf_NL = cNdotL * (1.0 / M_PI); #endif - diffuse_light += light_color * diffuse_brdf_NL * attenuation; + diffuse_light += light_color * diffuse_brdf_NL * attenuation * cc_attenuation; #if defined(LIGHT_BACKLIGHT_USED) - diffuse_light += light_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation; + diffuse_light += light_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation * cc_attenuation; #endif #if defined(LIGHT_RIM_USED) @@ -1609,7 +1630,9 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di float G = V_GGX(cNdotL, cNdotV, alpha_ggx); #endif // LIGHT_ANISOTROPY_USED // F +#if !defined(LIGHT_CLEARCOAT_USED) float cLdotH5 = SchlickFresnel(cLdotH); +#endif // Calculate Fresnel using cheap approximate specular occlusion term from Filament: // https://google.github.io/filament/Filament.html#lighting/occlusion/specularocclusion float f90 = clamp(50.0 * f0.g, 0.0, 1.0); @@ -1617,27 +1640,8 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di vec3 specular_brdf_NL = cNdotL * D * F * G; - specular_light += specular_brdf_NL * light_color * attenuation * specular_amount; -#endif - -#if defined(LIGHT_CLEARCOAT_USED) - // Clearcoat ignores normal_map, use vertex normal instead - float ccNdotL = max(min(A + dot(vertex_normal, L), 1.0), 0.0); - float ccNdotH = clamp(A + dot(vertex_normal, H), 0.0, 1.0); - float ccNdotV = max(dot(vertex_normal, V), 1e-4); - -#if !defined(SPECULAR_SCHLICK_GGX) - float cLdotH5 = SchlickFresnel(cLdotH); + specular_light += specular_brdf_NL * light_color * attenuation * cc_attenuation * specular_amount; #endif - float Dr = D_GGX(ccNdotH, mix(0.001, 0.1, clearcoat_roughness)); - float Gr = 0.25 / (cLdotH * cLdotH + 1e-4); - float Fr = mix(.04, 1.0, cLdotH5); - float clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL; - - specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount; - // TODO: Clearcoat adds light to the scene right now (it is non-energy conserving), both diffuse and specular need to be scaled by (1.0 - FR) - // but to do so we need to rearrange this entire function -#endif // LIGHT_CLEARCOAT_USED } #ifdef USE_SHADOW_TO_OPACITY diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index b4a1f6347e91..e9b2bc71e5d9 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1687,32 +1687,27 @@ void fragment_shader(in SceneData scene_data) { #endif #ifdef LIGHT_CLEARCOAT_USED + vec3 cc_specular_light = vec3(0.0); + vec3 cc_ref_vec = vec3(0.0); if (bool(scene_data.flags & SCENE_DATA_FLAGS_USE_REFLECTION_CUBEMAP)) { - float NoV = max(dot(geo_normal, view), 0.0001); // We want to use geometric normal, not normal_map - vec3 ref_vec = reflect(-view, geo_normal); - ref_vec = mix(ref_vec, geo_normal, clearcoat_roughness * clearcoat_roughness); - // The clear coat layer assumes an IOR of 1.5 (4% reflectance) - float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV)); - float attenuation = 1.0 - Fc; - ambient_light *= attenuation; - indirect_specular_light *= attenuation; - - float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0); - ref_vec = scene_data.radiance_inverse_xform * ref_vec; - float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD; + cc_ref_vec = reflect(-view, geo_normal); + cc_ref_vec = mix(cc_ref_vec, geo_normal, mix(0.001, 0.1, clearcoat_roughness)); + + vec3 cc_radiance_ref_vec = scene_data.radiance_inverse_xform * cc_ref_vec; + float roughness_lod = sqrt(mix(0.001, 0.1, clearcoat_roughness)) * MAX_ROUGHNESS_LOD; #ifdef USE_RADIANCE_CUBEMAP_ARRAY float lod, blend; blend = modf(roughness_lod, lod); - vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb; - clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend); + vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod)).rgb; + clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod + 1)).rgb, blend); #else - vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb; + vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cc_radiance_ref_vec, roughness_lod).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY - indirect_specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a; + cc_specular_light += clearcoat_light * scene_data.IBL_exposure_normalization * scene_data.ambient_light_color_energy.a; } #endif // LIGHT_CLEARCOAT_USED #endif // !AMBIENT_LIGHT_DISABLED @@ -1964,6 +1959,9 @@ void fragment_shader(in SceneData scene_data) { vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0); vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0); +#ifdef LIGHT_CLEARCOAT_USED + vec3 cc_reflection_accum = vec3(0.0, 0.0, 0.0); +#endif uint cluster_reflection_offset = cluster_offset + implementation_data.cluster_type_size * 3; @@ -2013,7 +2011,11 @@ void fragment_shader(in SceneData scene_data) { break; } - reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, ambient_accum, reflection_accum); + reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, +#ifdef LIGHT_CLEARCOAT_USED + cc_specular_light, cc_ref_vec, mix(0.001, 0.1, clearcoat_roughness), cc_reflection_accum, +#endif + ambient_accum, reflection_accum); } } @@ -2027,6 +2029,9 @@ void fragment_shader(in SceneData scene_data) { if (reflection_accum.a > 0.0) { indirect_specular_light = reflection_accum.rgb; +#ifdef LIGHT_CLEARCOAT_USED + cc_specular_light = cc_reflection_accum.rgb; +#endif } #if !defined(USE_LIGHTMAP) @@ -2134,6 +2139,15 @@ void fragment_shader(in SceneData scene_data) { //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); + +#ifdef LIGHT_CLEARCOAT_USED + // The base layer's f0 is computed assuming an interface from air to an IOR + // of 1.5, but the clear coat layer forms an interface from IOR 1.5 to IOR + // 1.5. We recompute f0 by first computing its IOR, then reconverting to f0 + // by using the correct interface + f0 = mix(f0, f0_Clear_Coat_To_Surface(f0), clearcoat); +#endif + #ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) @@ -2149,7 +2163,24 @@ void fragment_shader(in SceneData scene_data) { // cheap luminance approximation float f90 = clamp(50.0 * f0.g, metallic, 1.0); indirect_specular_light *= energy_compensation * ((f90 - f0) * envBRDF.x + f0 * envBRDF.y); + +#ifdef LIGHT_CLEARCOAT_USED + float geo_NdotV = max(dot(geo_normal, view), 0.0001); // We want to use geometric normal, not normal_map + // The clearcoat layer assumes an IOR of 1.5 (4% reflectance). + // Attenuate underlying diffuse/specular by clearcoat fresnel (ONLY fresnel, hence we don't just invert the BRDF below). + float NdotV5 = SchlickFresnel(geo_NdotV); + float F = mix(0.04, 1.0, NdotV5) * clearcoat; + float cc_attenuation = 1.0 - F; + + ambient_light *= cc_attenuation; + indirect_specular_light *= cc_attenuation; + + // Clearcoat Layer + // We don't need DFG for clearcoat, so we can use the fresnel directly. + indirect_specular_light += cc_specular_light * (F * clearcoat); #endif + +#endif // DIFFUSE_TOON } #endif // !AMBIENT_LIGHT_DISABLED diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index b9f1018ccdb3..0f2db1a25671 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1596,33 +1596,28 @@ void main() { ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); #endif // CUSTOM_IRRADIANCE_USED #ifdef LIGHT_CLEARCOAT_USED + hvec3 cc_specular_light = hvec3(0.0); + hvec3 cc_ref_vec = hvec3(0.0); if (sc_scene_use_reflection_cubemap()) { - half NoV = max(dot(geo_normal, view), half(0.0001)); - hvec3 ref_vec = reflect(-view, geo_normal); - ref_vec = mix(ref_vec, geo_normal, clearcoat_roughness * clearcoat_roughness); - // The clear coat layer assumes an IOR of 1.5 (4% reflectance) - half Fc = clearcoat * (half(0.04) + half(0.96) * SchlickFresnel(NoV)); - half attenuation = half(1.0) - Fc; - ambient_light *= attenuation; - indirect_specular_light *= attenuation; + cc_ref_vec = reflect(-view, geo_normal); + cc_ref_vec = mix(cc_ref_vec, geo_normal, mix(half(0.001), half(0.1), clearcoat_roughness)); - half horizon = min(half(1.0) + dot(ref_vec, indirect_normal), half(1.0)); - ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(ref_vec)); - float roughness_lod = mix(0.001, 0.1, sqrt(float(clearcoat_roughness))) * MAX_ROUGHNESS_LOD; + hvec3 cc_radiance_ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(cc_ref_vec)); + float roughness_lod = sqrt(mix(0.001, 0.1, clearcoat_roughness)) * MAX_ROUGHNESS_LOD; #ifdef USE_RADIANCE_CUBEMAP_ARRAY float lod; half blend = half(modf(roughness_lod, lod)); - hvec3 clearcoat_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb); - hvec3 clearcoat_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb); + hvec3 clearcoat_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod)).rgb); + hvec3 clearcoat_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cc_radiance_ref_vec, lod + 1)).rgb); hvec3 clearcoat_light = mix(clearcoat_sample_a, clearcoat_sample_b, blend); #else - hvec3 clearcoat_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_vec), roughness_lod).rgb); + hvec3 clearcoat_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cc_radiance_ref_vec), roughness_lod).rgb); #endif //USE_RADIANCE_CUBEMAP_ARRAY - indirect_specular_light += clearcoat_light * horizon * horizon * Fc * half(scene_data.ambient_light_color_energy.a); + cc_specular_light += clearcoat_light * half(scene_data.IBL_exposure_normalization) * half(scene_data.ambient_light_color_energy.a); } #endif // LIGHT_CLEARCOAT_USED #endif // !AMBIENT_LIGHT_DISABLED @@ -1714,6 +1709,9 @@ void main() { if (reflection_probe_count > 0) { hvec4 reflection_accum = hvec4(0.0); hvec4 ambient_accum = hvec4(0.0); +#ifdef LIGHT_CLEARCOAT_USED + hvec3 cc_reflection_accum = hvec3(0.0); +#endif #ifdef LIGHT_ANISOTROPY_USED // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy @@ -1739,7 +1737,11 @@ void main() { break; } - reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, ambient_accum, reflection_accum); + reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, indirect_specular_light, +#ifdef LIGHT_CLEARCOAT_USED + cc_specular_light, cc_ref_vec, mix(half(0.001), half(0.1), clearcoat_roughness), cc_reflection_accum, +#endif + ambient_accum, reflection_accum); } if (ambient_accum.a < half(1.0)) { @@ -1752,6 +1754,9 @@ void main() { if (reflection_accum.a > half(0.0)) { indirect_specular_light = reflection_accum.rgb; +#ifdef LIGHT_CLEARCOAT_USED + cc_specular_light = cc_reflection_accum.rgb; +#endif } #if !defined(USE_LIGHTMAP) @@ -1791,6 +1796,14 @@ void main() { //this saves some VGPRs hvec3 f0 = F0(metallic, specular, albedo); +#ifdef LIGHT_CLEARCOAT_USED + // The base layer's f0 is computed assuming an interface from air to an IOR + // of 1.5, but the clear coat layer forms an interface from IOR 1.5 to IOR + // 1.5. We recompute f0 by first computing its IOR, then reconverting to f0 + // by using the correct interface + f0 = mix(f0, f0_Clear_Coat_To_Surface(f0), clearcoat); +#endif + #ifndef AMBIENT_LIGHT_DISABLED { #if defined(DIFFUSE_TOON) @@ -1810,7 +1823,23 @@ void main() { hvec2 env = hvec2(-1.04, 1.04) * a004 + r.zw; indirect_specular_light *= env.x * f0 + env.y * clamp(half(50.0) * f0.g, metallic, half(1.0)); + +#ifdef LIGHT_CLEARCOAT_USED + half geo_NdotV = max(dot(geo_normal, view), half(0.0001)); // We want to use geometric normal, not normal_map + // The clearcoat layer assumes an IOR of 1.5 (4% reflectance). + // Attenuate underlying diffuse/specular by clearcoat fresnel (ONLY fresnel, hence we don't just invert the BRDF below). + half NdotV5 = SchlickFresnel(geo_NdotV); + half F = mix(half(0.04), half(1.0), NdotV5) * clearcoat; + half cc_attenuation = half(1.0) - F; + + ambient_light *= cc_attenuation; + indirect_specular_light *= cc_attenuation; + + // We don't need a BRDF approximation for clearcoat, so we can use the fresnel directly. + indirect_specular_light += cc_specular_light * (F * clearcoat); #endif + +#endif // DIFFUSE_TOON } #endif // !AMBIENT_LIGHT_DISABLED diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 726fdfcdf036..dfd28dda6020 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -59,6 +59,18 @@ hvec3 F0(half metallic, half specular, hvec3 albedo) { return mix(hvec3(dielectric), albedo, hvec3(metallic)); } +half V_Kelemen(half LdotH) { + // Kelemen 2001, "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling" + return saturateHalf(half(0.25) / (LdotH * LdotH + 1e-4)); +} + +hvec3 f0_Clear_Coat_To_Surface(hvec3 f0) { + // Approximation of iorTof0(f0ToIor(f0), 1.5) + // This assumes that the clear coat layer has an IOR of 1.5 + // see https://github.com/google/filament/blob/837b2715a05f4656d4f524bce50d1b23ff8f84c9/shaders/src/surface_material.fs#L54-L62 + return saturateHalf(f0 * (f0 * (half(0.941892) - half(0.263008) * f0) + half(0.346479)) - half(0.0285998)); +} + void light_compute(hvec3 N, hvec3 L, hvec3 V, half A, hvec3 light_color, bool is_directional, half attenuation, hvec3 f0, half roughness, half metallic, half specular_amount, hvec3 albedo, inout half alpha, vec2 screen_uv, hvec3 energy_compensation, #ifdef LIGHT_BACKLIGHT_USED hvec3 backlight, @@ -163,6 +175,8 @@ void light_compute(hvec3 N, hvec3 L, hvec3 V, half A, hvec3 light_color, bool is diffuse_light += rim_light * rim * mix(hvec3(1.0), albedo, rim_tint) * light_color; #endif + half cc_attenuation = half(1.0); + // We skip checking on attenuation on directional lights to avoid a branch that is not as beneficial for directional lights as the other ones. if (is_directional || attenuation > HALF_FLT_MIN) { half cNdotL = max(NdotL, half(0.0)); @@ -170,22 +184,20 @@ void light_compute(hvec3 N, hvec3 L, hvec3 V, half A, hvec3 light_color, bool is hvec3 H = normalize(V + L); half cLdotH = clamp(A + dot(L, H), half(0.0), half(1.0)); #endif + #if defined(LIGHT_CLEARCOAT_USED) // Clearcoat ignores normal_map, use vertex normal instead half ccNdotL = clamp(A + dot(vertex_normal, L), half(0.0), half(1.0)); half ccNdotH = clamp(A + dot(vertex_normal, H), half(0.0), half(1.0)); - half ccNdotV = max(dot(vertex_normal, V), half(1e-4)); half cLdotH5 = SchlickFresnel(cLdotH); half Dr = D_GGX(ccNdotH, half(mix(half(0.001), half(0.1), clearcoat_roughness)), vertex_normal, H); - half Gr = half(0.25) / (cLdotH * cLdotH + half(1e-4)); - half Fr = mix(half(0.04), half(1.0), cLdotH5); - half clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL; + half Gr = V_Kelemen(cLdotH); + half Fr = mix(half(0.04), half(1.0), cLdotH5) * clearcoat; + cc_attenuation = half(1.0) - Fr; + half clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * ccNdotL; specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount; - - // TODO: Clearcoat adds light to the scene right now (it is non-energy conserving), both diffuse and specular need to be scaled by (1.0 - FR) - // but to do so we need to rearrange this entire function #endif // LIGHT_CLEARCOAT_USED if (metallic < half(1.0)) { @@ -212,7 +224,7 @@ void light_compute(hvec3 N, hvec3 L, hvec3 V, half A, hvec3 light_color, bool is diffuse_brdf_NL = cNdotL * half(1.0 / M_PI); #endif - diffuse_light += light_color * diffuse_brdf_NL * attenuation; + diffuse_light += light_color * diffuse_brdf_NL * attenuation * cc_attenuation; #if defined(LIGHT_BACKLIGHT_USED) diffuse_light += light_color * (hvec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation; @@ -260,7 +272,7 @@ void light_compute(hvec3 N, hvec3 L, hvec3 V, half A, hvec3 light_color, bool is half f90 = clamp(dot(f0, hvec3(50.0 * 0.33)), metallic, half(1.0)); hvec3 F = f0 + (f90 - f0) * cLdotH5; hvec3 specular_brdf_NL = energy_compensation * cNdotL * D * F * G; - specular_light += specular_brdf_NL * light_color * attenuation * specular_amount; + specular_light += specular_brdf_NL * light_color * attenuation * cc_attenuation * specular_amount; #endif } @@ -920,7 +932,11 @@ void light_process_spot(uint idx, vec3 vertex, hvec3 eye_vec, hvec3 normal, vec3 diffuse_light, specular_light); } -void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal, half roughness, hvec3 ambient_light, hvec3 specular_light, inout hvec4 ambient_accum, inout hvec4 reflection_accum) { +void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal, half roughness, hvec3 ambient_light, hvec3 specular_light, +#ifdef LIGHT_CLEARCOAT_USED + hvec3 cc_specular_light, hvec3 cc_ref_vec, half cc_roughness, inout hvec3 cc_reflection_accum, +#endif + inout hvec4 ambient_accum, inout hvec4 reflection_accum) { vec3 box_extents = reflections.data[ref_index].box_extents; vec3 local_pos = (reflections.data[ref_index].local_matrix * vec4(vertex, 1.0)).xyz; @@ -967,6 +983,31 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal reflection_accum += reflection; } +#ifdef LIGHT_CLEARCOAT_USED + vec3 local_cc_ref_vec = (reflections.data[ref_index].local_matrix * vec4(cc_ref_vec, 0.0)).xyz; + + if (reflections.data[ref_index].box_project) { // Box project. + + vec3 nrdir = normalize(local_cc_ref_vec); + vec3 rbmax = (box_extents - local_pos) / nrdir; + vec3 rbmin = (-box_extents - local_pos) / nrdir; + + vec3 rbminmax = mix(rbmin, rbmax, greaterThan(nrdir, vec3(0.0, 0.0, 0.0))); + + float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); + vec3 posonbox = local_pos + nrdir * fa; + local_cc_ref_vec = posonbox - reflections.data[ref_index].box_offset; + } + + vec3 cc_reflection = textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_cc_ref_vec, reflections.data[ref_index].index), cc_roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier(); + cc_reflection *= reflections.data[ref_index].exposure_normalization; + if (reflections.data[ref_index].exterior) { + cc_reflection = mix(cc_specular_light, cc_reflection, blend); + } + cc_reflection *= reflections.data[ref_index].intensity * blend; + cc_reflection_accum += cc_reflection; +#endif // LIGHT_CLEARCOAT_USED + if (ambient_accum.a >= half(1.0)) { return; }