Spaces:
Running
Running
File size: 10,801 Bytes
6cd9596 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
export default /* glsl */`
// Analytical approximation of the DFG LUT, one half of the
// split-sum approximation used in indirect specular lighting.
// via 'environmentBRDF' from "Physically Based Shading on Mobile"
// https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {
const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
vec4 r = roughness * c0 + c1;
float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;
return vec2( -1.04, 1.04 ) * a004 + r.zw;
}
float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
#if defined ( PHYSICALLY_CORRECT_LIGHTS )
// based upon Frostbite 3 Moving to Physically-based Rendering
// page 32, equation 26: E[window1]
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
// this is intended to be used on spot and point lights who are represented as luminous intensity
// but who must be converted to luminous irradiance for surface lighting calculation
float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );
if( cutoffDistance > 0.0 ) {
distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
}
return distanceFalloff;
#else
if( cutoffDistance > 0.0 && decayExponent > 0.0 ) {
return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );
}
return 1.0;
#endif
}
vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
return RECIPROCAL_PI * diffuseColor;
} // validated
vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {
// Original approximation by Christophe Schlick '94
// float fresnel = pow( 1.0 - dotLH, 5.0 );
// Optimized variant (presented by Epic at SIGGRAPH '13)
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );
return ( 1.0 - specularColor ) * fresnel + specularColor;
} // validated
// Microfacet Models for Refraction through Rough Surfaces - equation (34)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// alpha is "roughness squared" in Disney’s reparameterization
float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {
// geometry term (normalized) = G(l)⋅G(v) / 4(n⋅l)(n⋅v)
// also see #12151
float a2 = pow2( alpha );
float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
return 1.0 / ( gl * gv );
} // validated
// 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 G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
float a2 = pow2( alpha );
// dotNL and dotNV are explicitly swapped. This is not a mistake.
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-Smith Visibility
vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {
float alpha = pow2( roughness ); // UE4's roughness
vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );
float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );
float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
float dotNH = saturate( dot( geometry.normal, halfDir ) );
float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
vec3 F = F_Schlick( specularColor, dotLH );
float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
float D = D_GGX( alpha, dotNH );
return F * ( G * D );
} // validated
// 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
// ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {
float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
return specularColor * brdf.x + brdf.y;
} // validated
// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
// Approximates multiscattering in order to preserve energy.
// http://www.jcgt.org/published/0008/01/03/
void BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {
float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
vec3 F = F_Schlick( specularColor, dotNV );
vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
vec3 FssEss = F * brdf.x + brdf.y;
float Ess = brdf.x + brdf.y;
float Ems = 1.0 - Ess;
// Paper incorrect indicates coefficient is PI/21, and will
// be corrected to 1/21 in future updates.
vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; // 1/21
vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );
singleScatter += FssEss;
multiScatter += Fms * Ems;
}
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)
return 0.25;
}
float D_BlinnPhong( const in float shininess, const in float dotNH ) {
return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );
}
vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {
vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );
//float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );
//float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
float dotNH = saturate( dot( geometry.normal, halfDir ) );
float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
vec3 F = F_Schlick( specularColor, dotLH );
float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );
float D = D_BlinnPhong( shininess, dotNH );
return F * ( G * D );
} // validated
// source: http://simonstechblog.blogspot.ca/2011/12/microfacet-brdf.html
float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {
return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );
}
float BlinnExponentToGGXRoughness( const in float blinnExponent ) {
return sqrt( 2.0 / ( blinnExponent + 2.0 ) );
}
`;
|