浏览代码

Improve clearcoat energy conservation (#22389)

WestLangley 3 年之前
父节点
当前提交
e0dfbc1b44

+ 11 - 37
src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

@@ -16,14 +16,10 @@ struct PhysicalMaterial {
 
 };
 
-#define DEFAULT_SPECULAR_COEFFICIENT 0.04
-
-// Clear coat directional hemishperical reflectance (this approximation should be improved)
-float clearcoatDHRApprox( const in float roughness, const in float dotNL ) {
+// temporary
+vec3 clearcoatSpecular = vec3( 0.0 );
 
-	return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );
-
-}
+#define DEFAULT_SPECULAR_COEFFICIENT 0.04
 
 // Analytical approximation of the DFG LUT, one half of the
 // split-sum approximation used in indirect specular lighting.
@@ -131,9 +127,9 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 	#ifdef CLEARCOAT
 
-		float ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );
+		float dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );
 
-		vec3 ccIrradiance = ccDotNL * directLight.color;
+		vec3 ccIrradiance = dotNLcc * directLight.color;
 
 		#ifndef PHYSICALLY_CORRECT_LIGHTS
 
@@ -141,32 +137,21 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 		#endif
 
-		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
-
-		reflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), 1.0, material.clearcoatRoughness );
-
-	#else
-
-		float clearcoatDHR = 0.0;
+		clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), 1.0, material.clearcoatRoughness );
 
 	#endif
 
 	#ifdef USE_SHEEN
 
-		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Sheen(
-			material.roughness,
-			directLight.direction,
-			geometry,
-			material.sheenTint
-		);
+		reflectedLight.directSpecular += irradiance * BRDF_Sheen( material.roughness, directLight.direction, geometry, material.sheenTint );
 
 	#else
 
-		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );
+		reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );
 
 	#endif
 
-	reflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Lambert( material.diffuseColor );
+	reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
 }
 
 void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
@@ -179,21 +164,10 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia
 
 	#ifdef CLEARCOAT
 
-		float ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
-
-		reflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, vec3( DEFAULT_SPECULAR_COEFFICIENT ), 1.0, material.clearcoatRoughness );
-
-		float ccDotNL = ccDotNV;
-		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
-
-	#else
-
-		float clearcoatDHR = 0.0;
+		clearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, vec3( DEFAULT_SPECULAR_COEFFICIENT ), 1.0, material.clearcoatRoughness );
 
 	#endif
 
-	float clearcoatInv = 1.0 - clearcoatDHR;
-
 	// Both indirect specular and indirect diffuse light accumulate here
 
 	vec3 singleScattering = vec3( 0.0 );
@@ -204,7 +178,7 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia
 
 	vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );
 
-	reflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;
+	reflectedLight.indirectSpecular += radiance * singleScattering;
 	reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;
 
 	reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;

+ 10 - 0
src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js

@@ -107,6 +107,16 @@ void main() {
 
 	vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;
 
+	#ifdef CLEARCOAT
+
+		float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
+
+		vec3 Fcc = F_Schlick( vec3( DEFAULT_SPECULAR_COEFFICIENT ), 1.0, dotNVcc );
+
+		outgoingLight = outgoingLight * ( 1.0 - clearcoat * Fcc ) + clearcoatSpecular * clearcoat;
+
+	#endif
+
 	gl_FragColor = vec4( outgoingLight, diffuseColor.a );
 
 	#include <tonemapping_fragment>