Kill specular AO at low roughness (#2348)

On metals (made only of specular light), specular AO can
create large black spots. This change simply kills specular
AO to avoid this artifact. In practice we should take into
account multiple bounces but this is rasterization...
This commit is contained in:
Romain Guy
2020-04-06 15:30:32 -07:00
committed by GitHub
parent 72056ee9e3
commit 92b7ee97f5
2 changed files with 17 additions and 6 deletions

View File

@@ -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;

View File

@@ -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);