bsdfs.glsl.js 10.9 KB
Newer Older
M
Mr.doob 已提交
1
export default /* glsl */`
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// 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;

}

20
float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
21

22
#if defined ( PHYSICALLY_CORRECT_LIGHTS )
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37
	// 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;
38

39
#else
40

41
	if( cutoffDistance > 0.0 && decayExponent > 0.0 ) {
42

43
		return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );
44

45 46 47
	}

	return 1.0;
48

49 50
#endif

51
}
52

B
Ben Houston 已提交
53
vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
54

W
WestLangley 已提交
55
	return RECIPROCAL_PI * diffuseColor;
56

57
} // validated
58

59
vec3 F_Schlick( const in vec3 f0, const in vec3 f90, const in float dotVH ) {
60 61

	// Original approximation by Christophe Schlick '94
qq_23353503's avatar
qq_23353503 已提交
62
	// float fresnel = pow( 1.0 - dotVH, 5.0 );
63 64

	// Optimized variant (presented by Epic at SIGGRAPH '13)
65
	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
qq_23353503's avatar
qq_23353503 已提交
66
	float fresnel = exp2( ( -5.55473 * dotVH - 6.98316 ) * dotVH );
67

68
	return ( f90 - f0 ) * fresnel + f0;
69

70
} // validated
71

72 73
// Microfacet Models for Refraction through Rough Surfaces - equation (34)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
74
// alpha is "roughness squared" in Disney’s reparameterization
75
float G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {
76

M
Mugen87 已提交
77 78
	// geometry term (normalized) = G(l)⋅G(v) / 4(n⋅l)(n⋅v)
	// also see #12151
79

80
	float a2 = pow2( alpha );
81

82 83
	float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
	float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
84 85

	return 1.0 / ( gl * gv );
86

87
} // validated
88

89 90
// 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
91 92 93 94
float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {

	float a2 = pow2( alpha );

95
	// dotNL and dotNV are explicitly swapped. This is not a mistake.
96 97 98
	float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
	float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );

99
	return 0.5 / max( gv + gl, EPSILON );
100

101 102
}

103 104
// Microfacet Models for Refraction through Rough Surfaces - equation (33)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
105
// alpha is "roughness squared" in Disney’s reparameterization
106
float D_GGX( const in float alpha, const in float dotNH ) {
107

B
Ben Houston 已提交
108
	float a2 = pow2( alpha );
109

B
Ben Houston 已提交
110
	float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
111

B
Ben Houston 已提交
112
	return RECIPROCAL_PI * a2 / pow2( denom );
113 114 115

}

116
// GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
117
vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in vec3 f90, const in float roughness ) {
118

119
	float alpha = pow2( roughness ); // UE4's roughness
120

A
arobertson0 已提交
121
	vec3 halfDir = normalize( incidentLight.direction + viewDir );
122

A
arobertson0 已提交
123 124 125
	float dotNL = saturate( dot( normal, incidentLight.direction ) );
	float dotNV = saturate( dot( normal, viewDir ) );
	float dotNH = saturate( dot( normal, halfDir ) );
W
WestLangley 已提交
126
	float dotVH = saturate( dot( viewDir, halfDir ) );
127

W
WestLangley 已提交
128
	vec3 F = F_Schlick( f0, f90, dotVH );
129

130
	float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );
131 132

	float D = D_GGX( alpha, dotNH );
133

134
	return F * ( G * D );
135

136 137
} // validated

138
// Rect Area Light
139 140

// Real-Time Polygonal-Light Shading with Linearly Transformed Cosines
141 142
// by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt
// code: https://github.com/selfshadow/ltc_code/
143

144
vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {
145

L
linbingquan 已提交
146
	const float LUT_SIZE = 64.0;
147
	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
L
linbingquan 已提交
148
	const float LUT_BIAS = 0.5 / LUT_SIZE;
149

150
	float dotNV = saturate( dot( N, V ) );
151

152 153
	// texture parameterized by sqrt( GGX alpha ) and sqrt( 1 - cos( theta ) )
	vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );
154 155 156 157 158 159 160

	uv = uv * LUT_SCALE + LUT_BIAS;

	return uv;

}

161
float LTC_ClippedSphereFormFactor( const in vec3 f ) {
162

163 164 165
	// Real-Time Area Lighting: a Journey from Research to Production (p.102)
	// An approximation of the form factor of a horizon-clipped rectangle.

166
	float l = length( f );
167

168
	return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );
169 170 171

}

172
vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {
173

174
	float x = dot( v1, v2 );
175

176
	float y = abs( x );
177 178 179 180

	// 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;
181
	float v = a / b;
182

W
WestLangley 已提交
183
	float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;
184

185
	return cross( v1, v2 ) * theta_sintheta;
186 187 188

}

189
vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {
190

191 192 193 194 195
	// 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 );
196

197
	if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );
198 199 200

	// construct orthonormal basis around N
	vec3 T1, T2;
201
	T1 = normalize( V - N * dot( V, N ) );
202
	T2 = - cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system
203

204
	// compute transform
205
	mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );
206

207 208 209 210 211 212
	// 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 );
213

214 215 216 217 218
	// project rect onto sphere
	coords[ 0 ] = normalize( coords[ 0 ] );
	coords[ 1 ] = normalize( coords[ 1 ] );
	coords[ 2 ] = normalize( coords[ 2 ] );
	coords[ 3 ] = normalize( coords[ 3 ] );
219

220 221 222 223 224 225
	// 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 ] );
226

227
	// adjust for horizon clipping
228 229 230 231 232 233 234 235
	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;

L
linbingquan 已提交
236
	const float LUT_SIZE = 64.0;
237
	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
L
linbingquan 已提交
238
	const float LUT_BIAS = 0.5 / LUT_SIZE;
239 240 241 242 243 244 245 246 247

	// 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;
*/
248

249
	return vec3( result );
250 251 252

}

253
// End Rect Area Light
254 255

// ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
A
arobertson0 已提交
256
vec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {
257

A
arobertson0 已提交
258
	float dotNV = saturate( dot( normal, viewDir ) );
259

260
	vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
261

262
	return specularColor * brdf.x + brdf.y;
263

264
} // validated
265

266 267 268 269
// 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 ) {
270

271
	float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
272

273
	vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
274 275

	vec3 FssEss = specularColor * brdf.x + brdf.y;
276

277 278 279 280 281
	float Ess = brdf.x + brdf.y;
	float Ems = 1.0 - Ess;

	vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; // 1/21
	vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );
282

283 284 285 286
	singleScatter += FssEss;
	multiScatter += Fms * Ems;

}
287

288
float G_BlinnPhong_Implicit( /* const in float dotNL, const in float dotNV */ ) {
289 290 291 292 293 294 295 296

	// 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 ) {

W
WestLangley 已提交
297
	return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );
298 299 300

}

301
vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {
302 303

	vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );
304

305
	float dotNH = saturate( dot( geometry.normal, halfDir ) );
W
WestLangley 已提交
306
	float dotVH = saturate( dot( geometry.viewDir, halfDir ) );
307

W
WestLangley 已提交
308
	vec3 F = F_Schlick( specularColor, vec3( 1.0 ), dotVH );
309

310
	float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );
311

312 313
	float D = D_BlinnPhong( shininess, dotNH );

314
	return F * ( G * D );
315

316 317
} // validated

318 319
#if defined( USE_SHEEN )

320
// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L94
W
WestLangley 已提交
321 322
float D_Charlie( float roughness, float NoH ) {

323
	// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
L
linbingquan 已提交
324
	float invAlpha = 1.0 / roughness;
325
	float cos2h = NoH * NoH;
W
WestLangley 已提交
326 327 328 329
	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 );

330 331 332
}

// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L136
W
WestLangley 已提交
333 334
float V_Neubelt( float NoV, float NoL ) {

335
	// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
W
WestLangley 已提交
336 337
	return saturate( 1.0 / ( 4.0 * ( NoL + NoV - NoL * NoV ) ) );

338 339
}

340
vec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {
D
Daniel Sturk 已提交
341 342 343

	vec3 N = geometry.normal;
	vec3 V = geometry.viewDir;
344

D
Daniel Sturk 已提交
345 346 347
	vec3 H = normalize( V + L );
	float dotNH = saturate( dot( N, H ) );

348
	return specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );
D
Daniel Sturk 已提交
349

350
}
D
Daniel Sturk 已提交
351

352
#endif
M
Mr.doob 已提交
353
`;