diff --git a/shaders/src/ambient_occlusion.fs b/shaders/src/ambient_occlusion.fs index 501784e893..c723fa0589 100644 --- a/shaders/src/ambient_occlusion.fs +++ b/shaders/src/ambient_occlusion.fs @@ -8,6 +8,12 @@ float evaluateSSAO() { return textureLod(light_ssao, uv, 0.0).r; } +#if SPECULAR_AMBIENT_OCCLUSION == 1 +float SpecularAO_Lagarde(float NoV, float visibility, float roughness) { + // Lagarde and de Rousiers 2014, "Moving Frostbite to PBR" + return saturate(pow(NoV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility); +} + #if defined(MATERIAL_HAS_BENT_NORMAL) float sphericalCapsIntersection(float cosCap1, float cosCap2, float cosDistance) { // Oat and Sander 2007, "Ambient Aperture Lighting" @@ -35,8 +41,8 @@ float sphericalCapsIntersection(float cosCap1, float cosCap2, float cosDistance) } // This function could (should?) be implemented as a 3D LUT instead, but we need to save samplers -float computeBentSpecularAO(float visibility, float roughness) { - // Jimenez et al. 2016, "Practical Realtime Strategies for
Accurate Indirect Occlusion" +float SpecularAO_Cones(float visibility, float roughness) { + // Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion" // aperture from ambient occlusion float cosAv = sqrt(1.0 - visibility); @@ -47,19 +53,24 @@ float computeBentSpecularAO(float visibility, float roughness) { // Remove the 2 * PI term from the denominator, it cancels out the same term from // sphericalCapsIntersection() - return sphericalCapsIntersection(cosAv, cosAs, 0.5 * cosB + 0.5) / (1.0 - cosAs); + float ao = sphericalCapsIntersection(cosAv, cosAs, 0.5 * cosB + 0.5) / (1.0 - cosAs); + // Smoothly kill specular AO when entering the perceptual roughness range [0.1..0.3] + // Without this, specular AO can remove all reflections, which looks bad on metals + return mix(1.0, ao, smoothstep(0.01, 0.09, roughness)); } #endif +#endif + /** * Computes a specular occlusion term from the ambient occlusion term. */ float computeSpecularAO(float NoV, float visibility, float roughness) { #if SPECULAR_AMBIENT_OCCLUSION == 1 #if defined(MATERIAL_HAS_BENT_NORMAL) - return computeBentSpecularAO(visibility, roughness); + return SpecularAO_Cones(visibility, roughness); #else - return saturate(pow(NoV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility); + return SpecularAO_Lagarde(NoV, visibility, roughness); #endif #else return 1.0; diff --git a/shaders/src/light_directional.fs b/shaders/src/light_directional.fs index 3fa9b52b06..c6ce1b493a 100644 --- a/shaders/src/light_directional.fs +++ b/shaders/src/light_directional.fs @@ -50,7 +50,7 @@ void evaluateDirectionalLight(const MaterialInputs material, } } - visibility = visibility * (1.0 - ssContactShadowOcclusion); + visibility *= 1.0 - ssContactShadowOcclusion; #if defined(MATERIAL_HAS_AMBIENT_OCCLUSION) visibility *= computeMicroShadowing(light.NoL, material.ambientOcclusion);