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