bsdfs.glsl.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. export default /* glsl */`
  2. // Analytical approximation of the DFG LUT, one half of the
  3. // split-sum approximation used in indirect specular lighting.
  4. // via 'environmentBRDF' from "Physically Based Shading on Mobile"
  5. // https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
  6. vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {
  7. const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
  8. const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
  9. vec4 r = roughness * c0 + c1;
  10. float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;
  11. return vec2( -1.04, 1.04 ) * a004 + r.zw;
  12. }
  13. float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
  14. #if defined ( PHYSICALLY_CORRECT_LIGHTS )
  15. // based upon Frostbite 3 Moving to Physically-based Rendering
  16. // page 32, equation 26: E[window1]
  17. // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
  18. // this is intended to be used on spot and point lights who are represented as luminous intensity
  19. // but who must be converted to luminous irradiance for surface lighting calculation
  20. float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );
  21. if( cutoffDistance > 0.0 ) {
  22. distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
  23. }
  24. return distanceFalloff;
  25. #else
  26. if( cutoffDistance > 0.0 && decayExponent > 0.0 ) {
  27. return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );
  28. }
  29. return 1.0;
  30. #endif
  31. }
  32. vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
  33. return RECIPROCAL_PI * diffuseColor;
  34. } // validated
  35. vec3 F_Schlick( const in vec3 specularColor, const in float dotVH ) {
  36. // Original approximation by Christophe Schlick '94
  37. // float fresnel = pow( 1.0 - dotVH, 5.0 );
  38. // Optimized variant (presented by Epic at SIGGRAPH '13)
  39. // https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
  40. float fresnel = exp2( ( -5.55473 * dotVH - 6.98316 ) * dotVH );
  41. return ( 1.0 - specularColor ) * fresnel + specularColor;
  42. } // validated
  43. vec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {
  44. // See F_Schlick
  45. float fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );
  46. vec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;
  47. return Fr * fresnel + F0;
  48. }
  49. // Microfacet Models for Refraction through Rough Surfaces - equation (34)
  50. // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
  51. // alpha is "roughness squared" in Disney’s reparameterization
  52. float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {
  53. // geometry term (normalized) = G(l)⋅G(v) / 4(n⋅l)(n⋅v)
  54. // also see #12151
  55. float a2 = pow2( alpha );
  56. float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
  57. float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
  58. return 1.0 / ( gl * gv );
  59. } // validated
  60. // Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
  61. // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
  62. float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
  63. float a2 = pow2( alpha );
  64. // dotNL and dotNV are explicitly swapped. This is not a mistake.
  65. float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
  66. float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
  67. return 0.5 / max( gv + gl, EPSILON );
  68. }
  69. // Microfacet Models for Refraction through Rough Surfaces - equation (33)
  70. // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
  71. // alpha is "roughness squared" in Disney’s reparameterization
  72. float D_GGX( const in float alpha, const in float dotNH ) {
  73. float a2 = pow2( alpha );
  74. float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
  75. return RECIPROCAL_PI * a2 / pow2( denom );
  76. }
  77. // GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
  78. vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {
  79. float alpha = pow2( roughness ); // UE4's roughness
  80. vec3 halfDir = normalize( incidentLight.direction + viewDir );
  81. float dotNL = saturate( dot( normal, incidentLight.direction ) );
  82. float dotNV = saturate( dot( normal, viewDir ) );
  83. float dotNH = saturate( dot( normal, halfDir ) );
  84. float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
  85. vec3 F = F_Schlick( specularColor, dotLH );
  86. float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
  87. float D = D_GGX( alpha, dotNH );
  88. return F * ( G * D );
  89. } // validated
  90. // Rect Area Light
  91. // Real-Time Polygonal-Light Shading with Linearly Transformed Cosines
  92. // by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt
  93. // code: https://github.com/selfshadow/ltc_code/
  94. vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {
  95. const float LUT_SIZE = 64.0;
  96. const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
  97. const float LUT_BIAS = 0.5 / LUT_SIZE;
  98. float dotNV = saturate( dot( N, V ) );
  99. // texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) )
  100. vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );
  101. uv = uv * LUT_SCALE + LUT_BIAS;
  102. return uv;
  103. }
  104. float LTC_ClippedSphereFormFactor( const in vec3 f ) {
  105. // Real-Time Area Lighting: a Journey from Research to Production (p.102)
  106. // An approximation of the form factor of a horizon-clipped rectangle.
  107. float l = length( f );
  108. return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );
  109. }
  110. vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {
  111. float x = dot( v1, v2 );
  112. float y = abs( x );
  113. // rational polynomial approximation to theta / sin( theta ) / 2PI
  114. float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;
  115. float b = 3.4175940 + ( 4.1616724 + y ) * y;
  116. float v = a / b;
  117. float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;
  118. return cross( v1, v2 ) * theta_sintheta;
  119. }
  120. vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {
  121. // bail if point is on back side of plane of light
  122. // assumes ccw winding order of light vertices
  123. vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];
  124. vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];
  125. vec3 lightNormal = cross( v1, v2 );
  126. if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );
  127. // construct orthonormal basis around N
  128. vec3 T1, T2;
  129. T1 = normalize( V - N * dot( V, N ) );
  130. T2 = - cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system
  131. // compute transform
  132. mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );
  133. // transform rect
  134. vec3 coords[ 4 ];
  135. coords[ 0 ] = mat * ( rectCoords[ 0 ] - P );
  136. coords[ 1 ] = mat * ( rectCoords[ 1 ] - P );
  137. coords[ 2 ] = mat * ( rectCoords[ 2 ] - P );
  138. coords[ 3 ] = mat * ( rectCoords[ 3 ] - P );
  139. // project rect onto sphere
  140. coords[ 0 ] = normalize( coords[ 0 ] );
  141. coords[ 1 ] = normalize( coords[ 1 ] );
  142. coords[ 2 ] = normalize( coords[ 2 ] );
  143. coords[ 3 ] = normalize( coords[ 3 ] );
  144. // calculate vector form factor
  145. vec3 vectorFormFactor = vec3( 0.0 );
  146. vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );
  147. vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );
  148. vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );
  149. vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );
  150. // adjust for horizon clipping
  151. float result = LTC_ClippedSphereFormFactor( vectorFormFactor );
  152. /*
  153. // alternate method of adjusting for horizon clipping (see referece)
  154. // refactoring required
  155. float len = length( vectorFormFactor );
  156. float z = vectorFormFactor.z / len;
  157. const float LUT_SIZE = 64.0;
  158. const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
  159. const float LUT_BIAS = 0.5 / LUT_SIZE;
  160. // tabulated horizon-clipped sphere, apparently...
  161. vec2 uv = vec2( z * 0.5 + 0.5, len );
  162. uv = uv * LUT_SCALE + LUT_BIAS;
  163. float scale = texture2D( ltc_2, uv ).w;
  164. float result = len * scale;
  165. */
  166. return vec3( result );
  167. }
  168. // End Rect Area Light
  169. // ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
  170. vec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {
  171. float dotNV = saturate( dot( normal, viewDir ) );
  172. vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
  173. return specularColor * brdf.x + brdf.y;
  174. } // validated
  175. // Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
  176. // Approximates multiscattering in order to preserve energy.
  177. // http://www.jcgt.org/published/0008/01/03/
  178. void BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {
  179. float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
  180. vec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );
  181. vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
  182. vec3 FssEss = F * brdf.x + brdf.y;
  183. float Ess = brdf.x + brdf.y;
  184. float Ems = 1.0 - Ess;
  185. vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; // 1/21
  186. vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );
  187. singleScatter += FssEss;
  188. multiScatter += Fms * Ems;
  189. }
  190. float G_BlinnPhong_Implicit( /* const in float dotNL, const in float dotNV */ ) {
  191. // geometry term is (n dot l)(n dot v) / 4(n dot l)(n dot v)
  192. return 0.25;
  193. }
  194. float D_BlinnPhong( const in float shininess, const in float dotNH ) {
  195. return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );
  196. }
  197. vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {
  198. vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );
  199. //float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );
  200. //float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
  201. float dotNH = saturate( dot( geometry.normal, halfDir ) );
  202. float dotLH = saturate( dot( incidentLight.direction, halfDir ) );
  203. vec3 F = F_Schlick( specularColor, dotLH );
  204. float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );
  205. float D = D_BlinnPhong( shininess, dotNH );
  206. return F * ( G * D );
  207. } // validated
  208. // source: http://simonstechblog.blogspot.ca/2011/12/microfacet-brdf.html
  209. float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {
  210. return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );
  211. }
  212. float BlinnExponentToGGXRoughness( const in float blinnExponent ) {
  213. return sqrt( 2.0 / ( blinnExponent + 2.0 ) );
  214. }
  215. #if defined( USE_SHEEN )
  216. // https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L94
  217. float D_Charlie(float roughness, float NoH) {
  218. // Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
  219. float invAlpha = 1.0 / roughness;
  220. float cos2h = NoH * NoH;
  221. float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
  222. return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
  223. }
  224. // https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L136
  225. float V_Neubelt(float NoV, float NoL) {
  226. // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
  227. return saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));
  228. }
  229. vec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {
  230. vec3 N = geometry.normal;
  231. vec3 V = geometry.viewDir;
  232. vec3 H = normalize( V + L );
  233. float dotNH = saturate( dot( N, H ) );
  234. return specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );
  235. }
  236. #endif
  237. `;