Browse Source

Refactor PBR shaders (#25693)

* pull in shader refactor from anisotropy branch

* removed vestiges of anisotropy

* vTBN -> tbn

* fixed flat shading

* Space -> Frame

* revert so doubleside does not affect flat shaded
Emmett Lalish 2 years ago
parent
commit
08727dd9e9

+ 0 - 271
src/renderers/shaders/ShaderChunk/bsdfs.glsl.js

@@ -1,236 +1,5 @@
 export default /* glsl */`
 
-vec3 BRDF_Lambert( const in vec3 diffuseColor ) {
-
-	return RECIPROCAL_PI * diffuseColor;
-
-} // validated
-
-vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {
-
-	// Original approximation by Christophe Schlick '94
-	// float fresnel = pow( 1.0 - dotVH, 5.0 );
-
-	// Optimized variant (presented by Epic at SIGGRAPH '13)
-	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
-	float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );
-
-	return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );
-
-} // validated
-
-float F_Schlick( const in float f0, const in float f90, const in float dotVH ) {
-
-	// Original approximation by Christophe Schlick '94
-	// float fresnel = pow( 1.0 - dotVH, 5.0 );
-
-	// Optimized variant (presented by Epic at SIGGRAPH '13)
-	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
-	float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );
-
-	return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );
-
-} // validated
-
-vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {
-    float x = clamp( 1.0 - dotVH, 0.0, 1.0 );
-    float x2 = x * x;
-    float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );
-
-    return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );
-}
-
-// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
-// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
-float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
-
-	float a2 = pow2( alpha );
-
-	float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
-	float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
-
-	return 0.5 / max( gv + gl, EPSILON );
-
-}
-
-// Microfacet Models for Refraction through Rough Surfaces - equation (33)
-// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
-// alpha is "roughness squared" in Disney’s reparameterization
-float D_GGX( const in float alpha, const in float dotNH ) {
-
-	float a2 = pow2( alpha );
-
-	float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
-
-	return RECIPROCAL_PI * a2 / pow2( denom );
-
-}
-
-// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
-vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) {
-
-	float alpha = pow2( roughness ); // UE4's roughness
-
-	vec3 halfDir = normalize( lightDir + viewDir );
-
-	float dotNL = saturate( dot( normal, lightDir ) );
-	float dotNV = saturate( dot( normal, viewDir ) );
-	float dotNH = saturate( dot( normal, halfDir ) );
-	float dotVH = saturate( dot( viewDir, halfDir ) );
-
-	vec3 F = F_Schlick( f0, f90, dotVH );
-
-	float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
-
-	float D = D_GGX( alpha, dotNH );
-
-	return F * ( V * D );
-
-}
-
-#ifdef USE_IRIDESCENCE
-
-	vec3 BRDF_GGX_Iridescence( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float iridescence, const in vec3 iridescenceFresnel, const in float roughness ) {
-
-		float alpha = pow2( roughness ); // UE4's roughness
-
-		vec3 halfDir = normalize( lightDir + viewDir );
-
-		float dotNL = saturate( dot( normal, lightDir ) );
-		float dotNV = saturate( dot( normal, viewDir ) );
-		float dotNH = saturate( dot( normal, halfDir ) );
-		float dotVH = saturate( dot( viewDir, halfDir ) );
-
-		vec3 F = mix( F_Schlick( f0, f90, dotVH ), iridescenceFresnel, iridescence );
-
-		float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
-
-		float D = D_GGX( alpha, dotNH );
-
-		return F * ( V * D );
-
-	}
-
-#endif
-
-// Rect Area Light
-
-// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines
-// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt
-// code: https://github.com/selfshadow/ltc_code/
-
-vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {
-
-	const float LUT_SIZE = 64.0;
-	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
-	const float LUT_BIAS = 0.5 / LUT_SIZE;
-
-	float dotNV = saturate( dot( N, V ) );
-
-	// texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) )
-	vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );
-
-	uv = uv * LUT_SCALE + LUT_BIAS;
-
-	return uv;
-
-}
-
-float LTC_ClippedSphereFormFactor( const in vec3 f ) {
-
-	// Real-Time Area Lighting: a Journey from Research to Production (p.102)
-	// An approximation of the form factor of a horizon-clipped rectangle.
-
-	float l = length( f );
-
-	return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );
-
-}
-
-vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {
-
-	float x = dot( v1, v2 );
-
-	float y = abs( x );
-
-	// rational polynomial approximation to theta / sin( theta ) / 2PI
-	float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;
-	float b = 3.4175940 + ( 4.1616724 + y ) * y;
-	float v = a / b;
-
-	float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;
-
-	return cross( v1, v2 ) * theta_sintheta;
-
-}
-
-vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {
-
-	// bail if point is on back side of plane of light
-	// assumes ccw winding order of light vertices
-	vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];
-	vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];
-	vec3 lightNormal = cross( v1, v2 );
-
-	if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );
-
-	// construct orthonormal basis around N
-	vec3 T1, T2;
-	T1 = normalize( V - N * dot( V, N ) );
-	T2 = - cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system
-
-	// compute transform
-	mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );
-
-	// transform rect
-	vec3 coords[ 4 ];
-	coords[ 0 ] = mat * ( rectCoords[ 0 ] - P );
-	coords[ 1 ] = mat * ( rectCoords[ 1 ] - P );
-	coords[ 2 ] = mat * ( rectCoords[ 2 ] - P );
-	coords[ 3 ] = mat * ( rectCoords[ 3 ] - P );
-
-	// project rect onto sphere
-	coords[ 0 ] = normalize( coords[ 0 ] );
-	coords[ 1 ] = normalize( coords[ 1 ] );
-	coords[ 2 ] = normalize( coords[ 2 ] );
-	coords[ 3 ] = normalize( coords[ 3 ] );
-
-	// calculate vector form factor
-	vec3 vectorFormFactor = vec3( 0.0 );
-	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );
-	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );
-	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );
-	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );
-
-	// adjust for horizon clipping
-	float result = LTC_ClippedSphereFormFactor( vectorFormFactor );
-
-/*
-	// alternate method of adjusting for horizon clipping (see referece)
-	// refactoring required
-	float len = length( vectorFormFactor );
-	float z = vectorFormFactor.z / len;
-
-	const float LUT_SIZE = 64.0;
-	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
-	const float LUT_BIAS = 0.5 / LUT_SIZE;
-
-	// tabulated horizon-clipped sphere, apparently...
-	vec2 uv = vec2( z * 0.5 + 0.5, len );
-	uv = uv * LUT_SCALE + LUT_BIAS;
-
-	float scale = texture2D( ltc_2, uv ).w;
-
-	float result = len * scale;
-*/
-
-	return vec3( result );
-
-}
-
-// End Rect Area Light
-
-
 float G_BlinnPhong_Implicit( /* const in float dotNL, const in float dotNV */ ) {
 
 	// geometry term is (n dot l)(n dot v) / 4(n dot l)(n dot v)
@@ -261,44 +30,4 @@ vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in ve
 
 } // validated
 
-#if defined( USE_SHEEN )
-
-// https://github.com/google/filament/blob/master/shaders/src/brdf.fs
-float D_Charlie( float roughness, float dotNH ) {
-
-	float alpha = pow2( roughness );
-
-	// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
-	float invAlpha = 1.0 / alpha;
-	float cos2h = dotNH * dotNH;
-	float sin2h = max( 1.0 - cos2h, 0.0078125 ); // 2^(-14/2), so sin2h^2 > 0 in fp16
-
-	return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );
-
-}
-
-// https://github.com/google/filament/blob/master/shaders/src/brdf.fs
-float V_Neubelt( float dotNV, float dotNL ) {
-
-	// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
-	return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );
-
-}
-
-vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {
-
-	vec3 halfDir = normalize( lightDir + viewDir );
-
-	float dotNL = saturate( dot( normal, lightDir ) );
-	float dotNV = saturate( dot( normal, viewDir ) );
-	float dotNH = saturate( dot( normal, halfDir ) );
-
-	float D = D_Charlie( sheenRoughness, dotNH );
-	float V = V_Neubelt( dotNV, dotNL );
-
-	return sheenColor * ( D * V );
-
-}
-
-#endif
 `;

+ 1 - 9
src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js

@@ -4,15 +4,7 @@ export default /* glsl */`
 	vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;
 	clearcoatMapN.xy *= clearcoatNormalScale;
 
-	#ifdef USE_TANGENT
-
-		clearcoatNormal = normalize( vTBN * clearcoatMapN );
-
-	#else
-
-		clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );
-
-	#endif
+	clearcoatNormal = normalize( tbn * clearcoatMapN );
 
 #endif
 `;

+ 32 - 0
src/renderers/shaders/ShaderChunk/common.glsl.js

@@ -115,4 +115,36 @@ vec2 equirectUv( in vec3 dir ) {
 	return vec2( u, v );
 
 }
+
+vec3 BRDF_Lambert( const in vec3 diffuseColor ) {
+
+	return RECIPROCAL_PI * diffuseColor;
+
+} // validated
+
+vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {
+
+	// Original approximation by Christophe Schlick '94
+	// float fresnel = pow( 1.0 - dotVH, 5.0 );
+
+	// Optimized variant (presented by Epic at SIGGRAPH '13)
+	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
+	float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );
+
+	return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );
+
+} // validated
+
+float F_Schlick( const in float f0, const in float f90, const in float dotVH ) {
+
+	// Original approximation by Christophe Schlick '94
+	// float fresnel = pow( 1.0 - dotVH, 5.0 );
+
+	// Optimized variant (presented by Epic at SIGGRAPH '13)
+	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
+	float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );
+
+	return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );
+
+} // validated
 `;

+ 256 - 10
src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

@@ -1,4 +1,5 @@
 export default /* glsl */`
+
 struct PhysicalMaterial {
 
 	vec3 diffuseColor;
@@ -44,6 +45,259 @@ struct PhysicalMaterial {
 vec3 clearcoatSpecular = vec3( 0.0 );
 vec3 sheenSpecular = vec3( 0.0 );
 
+vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {
+    float x = clamp( 1.0 - dotVH, 0.0, 1.0 );
+    float x2 = x * x;
+    float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );
+
+    return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );
+}
+
+// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
+// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
+float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
+
+	float a2 = pow2( alpha );
+
+	float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
+	float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
+
+	return 0.5 / max( gv + gl, EPSILON );
+
+}
+
+// Microfacet Models for Refraction through Rough Surfaces - equation (33)
+// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
+// alpha is "roughness squared" in Disney’s reparameterization
+float D_GGX( const in float alpha, const in float dotNH ) {
+
+	float a2 = pow2( alpha );
+
+	float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
+
+	return RECIPROCAL_PI * a2 / pow2( denom );
+
+}
+
+#ifdef USE_CLEARCOAT
+
+	// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
+	vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {
+
+		vec3 f0 = material.clearcoatF0;
+		float f90 = material.clearcoatF90;
+		float roughness = material.clearcoatRoughness;
+
+		float alpha = pow2( roughness ); // UE4's roughness
+
+		vec3 halfDir = normalize( lightDir + viewDir );
+
+		float dotNL = saturate( dot( normal, lightDir ) );
+		float dotNV = saturate( dot( normal, viewDir ) );
+		float dotNH = saturate( dot( normal, halfDir ) );
+		float dotVH = saturate( dot( viewDir, halfDir ) );
+
+		vec3 F = F_Schlick( f0, f90, dotVH );
+
+		float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
+
+		float D = D_GGX( alpha, dotNH );
+
+		return F * ( V * D );
+
+	}
+
+#endif
+
+vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {
+
+	vec3 f0 = material.specularColor;
+	float f90 = material.specularF90;
+	float roughness = material.roughness;
+
+	float alpha = pow2( roughness ); // UE4's roughness
+
+	vec3 halfDir = normalize( lightDir + viewDir );
+
+	float dotNL = saturate( dot( normal, lightDir ) );
+	float dotNV = saturate( dot( normal, viewDir ) );
+	float dotNH = saturate( dot( normal, halfDir ) );
+	float dotVH = saturate( dot( viewDir, halfDir ) );
+
+	vec3 F = F_Schlick( f0, f90, dotVH );
+
+	#ifdef USE_IRIDESCENCE
+
+		F = mix( F, material.iridescenceFresnel, material.iridescence );
+
+	#endif
+
+	float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
+
+	float D = D_GGX( alpha, dotNH );
+
+	return F * ( V * D );
+
+}
+
+// Rect Area Light
+
+// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines
+// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt
+// code: https://github.com/selfshadow/ltc_code/
+
+vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {
+
+	const float LUT_SIZE = 64.0;
+	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
+	const float LUT_BIAS = 0.5 / LUT_SIZE;
+
+	float dotNV = saturate( dot( N, V ) );
+
+	// texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) )
+	vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );
+
+	uv = uv * LUT_SCALE + LUT_BIAS;
+
+	return uv;
+
+}
+
+float LTC_ClippedSphereFormFactor( const in vec3 f ) {
+
+	// Real-Time Area Lighting: a Journey from Research to Production (p.102)
+	// An approximation of the form factor of a horizon-clipped rectangle.
+
+	float l = length( f );
+
+	return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );
+
+}
+
+vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {
+
+	float x = dot( v1, v2 );
+
+	float y = abs( x );
+
+	// rational polynomial approximation to theta / sin( theta ) / 2PI
+	float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;
+	float b = 3.4175940 + ( 4.1616724 + y ) * y;
+	float v = a / b;
+
+	float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;
+
+	return cross( v1, v2 ) * theta_sintheta;
+
+}
+
+vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {
+
+	// bail if point is on back side of plane of light
+	// assumes ccw winding order of light vertices
+	vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];
+	vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];
+	vec3 lightNormal = cross( v1, v2 );
+
+	if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );
+
+	// construct orthonormal basis around N
+	vec3 T1, T2;
+	T1 = normalize( V - N * dot( V, N ) );
+	T2 = - cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system
+
+	// compute transform
+	mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );
+
+	// transform rect
+	vec3 coords[ 4 ];
+	coords[ 0 ] = mat * ( rectCoords[ 0 ] - P );
+	coords[ 1 ] = mat * ( rectCoords[ 1 ] - P );
+	coords[ 2 ] = mat * ( rectCoords[ 2 ] - P );
+	coords[ 3 ] = mat * ( rectCoords[ 3 ] - P );
+
+	// project rect onto sphere
+	coords[ 0 ] = normalize( coords[ 0 ] );
+	coords[ 1 ] = normalize( coords[ 1 ] );
+	coords[ 2 ] = normalize( coords[ 2 ] );
+	coords[ 3 ] = normalize( coords[ 3 ] );
+
+	// calculate vector form factor
+	vec3 vectorFormFactor = vec3( 0.0 );
+	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );
+	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );
+	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );
+	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );
+
+	// adjust for horizon clipping
+	float result = LTC_ClippedSphereFormFactor( vectorFormFactor );
+
+/*
+	// alternate method of adjusting for horizon clipping (see referece)
+	// refactoring required
+	float len = length( vectorFormFactor );
+	float z = vectorFormFactor.z / len;
+
+	const float LUT_SIZE = 64.0;
+	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
+	const float LUT_BIAS = 0.5 / LUT_SIZE;
+
+	// tabulated horizon-clipped sphere, apparently...
+	vec2 uv = vec2( z * 0.5 + 0.5, len );
+	uv = uv * LUT_SCALE + LUT_BIAS;
+
+	float scale = texture2D( ltc_2, uv ).w;
+
+	float result = len * scale;
+*/
+
+	return vec3( result );
+
+}
+
+// End Rect Area Light
+
+#if defined( USE_SHEEN )
+
+// https://github.com/google/filament/blob/master/shaders/src/brdf.fs
+float D_Charlie( float roughness, float dotNH ) {
+
+	float alpha = pow2( roughness );
+
+	// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
+	float invAlpha = 1.0 / alpha;
+	float cos2h = dotNH * dotNH;
+	float sin2h = max( 1.0 - cos2h, 0.0078125 ); // 2^(-14/2), so sin2h^2 > 0 in fp16
+
+	return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );
+
+}
+
+// https://github.com/google/filament/blob/master/shaders/src/brdf.fs
+float V_Neubelt( float dotNV, float dotNL ) {
+
+	// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
+	return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );
+
+}
+
+vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {
+
+	vec3 halfDir = normalize( lightDir + viewDir );
+
+	float dotNL = saturate( dot( normal, lightDir ) );
+	float dotNV = saturate( dot( normal, viewDir ) );
+	float dotNH = saturate( dot( normal, halfDir ) );
+
+	float D = D_Charlie( sheenRoughness, dotNH );
+	float V = V_Neubelt( dotNV, dotNL );
+
+	return sheenColor * ( D * V );
+
+}
+
+#endif
+
 // This is a curve-fit approxmation to the "Charlie sheen" BRDF integrated over the hemisphere from 
 // Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF". The analysis can be found
 // in the Sheen section of https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing
@@ -181,7 +435,7 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 		vec3 ccIrradiance = dotNLcc * directLight.color;
 
-		clearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );
+		clearcoatSpecular += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material );
 
 	#endif
 
@@ -191,15 +445,7 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 	#endif
 
-	#ifdef USE_IRIDESCENCE
-
-		reflectedLight.directSpecular += irradiance * BRDF_GGX_Iridescence( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness );
-
-	#else
-
-		reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );
-
-	#endif
+	reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material );
 
 	reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
 }

+ 12 - 11
src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js

@@ -13,27 +13,28 @@ float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;
 
 	#ifdef DOUBLE_SIDED
 
-		normal = normal * faceDirection;
+		normal *= faceDirection;
 
 	#endif
 
-	#ifdef USE_TANGENT
+#endif
 
-		vec3 tangent = normalize( vTangent );
-		vec3 bitangent = normalize( vBitangent );
+#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )
 
-		#ifdef DOUBLE_SIDED
+	#ifdef USE_TANGENT
 
-			tangent = tangent * faceDirection;
-			bitangent = bitangent * faceDirection;
+		mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal );
 
-		#endif
+	#else
 
-		#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )
+		mat3 tbn = getTangentFrame( - vViewPosition, normal );
+
+	#endif
 
-			mat3 vTBN = mat3( tangent, bitangent, normal );
+	#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )
 
-		#endif
+		tbn[0] *= faceDirection;
+		tbn[1] *= faceDirection;
 
 	#endif
 

+ 1 - 9
src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js

@@ -23,15 +23,7 @@ export default /* glsl */`
 	vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
 	mapN.xy *= normalScale;
 
-	#ifdef USE_TANGENT
-
-		normal = normalize( vTBN * mapN );
-
-	#else
-
-		normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection );
-
-	#endif
+	normal = normalize( tbn * mapN );
 
 #elif defined( USE_BUMPMAP )
 

+ 3 - 3
src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js

@@ -17,7 +17,7 @@ export default /* glsl */`
 	// Normal Mapping Without Precomputed Tangents
 	// http://www.thetenthplanet.de/archives/1180
 
-	vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {
+	mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm ) {
 
 		vec3 q0 = dFdx( eye_pos.xyz );
 		vec3 q1 = dFdy( eye_pos.xyz );
@@ -33,9 +33,9 @@ export default /* glsl */`
 		vec3 B = q1perp * st0.y + q0perp * st1.y;
 
 		float det = max( dot( T, T ), dot( B, B ) );
-		float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );
+		float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det );
 
-		return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );
+		return mat3( T * scale, B * scale, N );
 
 	}
 

+ 0 - 1
src/renderers/shaders/ShaderLib/meshphysical.glsl.js

@@ -128,7 +128,6 @@ varying vec3 vViewPosition;
 #include <aomap_pars_fragment>
 #include <lightmap_pars_fragment>
 #include <emissivemap_pars_fragment>
-#include <bsdfs>
 #include <iridescence_fragment>
 #include <cube_uv_reflection_fragment>
 #include <envmap_common_pars_fragment>