three-gpu-pathtracer 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +136 -15
  2. package/build/index.module.js +2706 -529
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +2713 -529
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +68 -60
  7. package/src/core/DynamicPathTracingSceneGenerator.js +24 -11
  8. package/src/core/PathTracingRenderer.js +22 -0
  9. package/src/core/PathTracingSceneGenerator.js +36 -20
  10. package/src/index.js +9 -1
  11. package/src/materials/PhysicalPathTracingMaterial.js +350 -60
  12. package/src/objects/EquirectCamera.js +13 -0
  13. package/src/{core → objects}/PhysicalCamera.js +0 -0
  14. package/src/objects/PhysicalSpotLight.js +14 -0
  15. package/src/objects/ShapedAreaLight.js +12 -0
  16. package/src/shader/shaderEnvMapSampling.js +59 -67
  17. package/src/shader/shaderGGXFunctions.js +3 -2
  18. package/src/shader/shaderIridescenceFunctions.js +130 -0
  19. package/src/shader/shaderLightSampling.js +231 -0
  20. package/src/shader/shaderMaterialSampling.js +259 -53
  21. package/src/shader/shaderSheenFunctions.js +98 -0
  22. package/src/shader/shaderStructs.js +307 -92
  23. package/src/shader/shaderUtils.js +122 -0
  24. package/src/uniforms/EquirectHdrInfoUniform.js +10 -14
  25. package/src/uniforms/IESProfilesTexture.js +100 -0
  26. package/src/uniforms/LightsInfoUniformStruct.js +162 -0
  27. package/src/uniforms/MaterialsTexture.js +266 -33
  28. package/src/uniforms/PhysicalCameraUniform.js +1 -1
  29. package/src/uniforms/RenderTarget2DArray.js +93 -80
  30. package/src/utils/GeometryPreparationUtils.js +1 -1
  31. package/src/utils/IESLoader.js +325 -0
  32. package/src/workers/PathTracingSceneWorker.js +3 -1
@@ -1,4 +1,6 @@
1
1
  import { shaderGGXFunctions } from './shaderGGXFunctions.js';
2
+ import { shaderSheenFunctions } from './shaderSheenFunctions.js';
3
+ import { shaderIridescenceFunctions } from './shaderIridescenceFunctions.js';
2
4
 
3
5
  export const shaderMaterialSampling = /* glsl */`
4
6
 
@@ -13,15 +15,29 @@ struct SurfaceRec {
13
15
  vec3 emission;
14
16
  float transmission;
15
17
  float ior;
18
+ float clearcoat;
19
+ float clearcoatRoughness;
20
+ float filteredClearcoatRoughness;
21
+ vec3 sheenColor;
22
+ float sheenRoughness;
23
+ float iridescence;
24
+ float iridescenceIor;
25
+ float iridescenceThickness;
26
+ vec3 specularColor;
27
+ float specularIntensity;
16
28
  };
17
29
 
18
30
  struct SampleRec {
31
+ float specularPdf;
19
32
  float pdf;
20
33
  vec3 direction;
34
+ vec3 clearcoatDirection;
21
35
  vec3 color;
22
36
  };
23
37
 
24
38
  ${ shaderGGXFunctions }
39
+ ${ shaderSheenFunctions }
40
+ ${ shaderIridescenceFunctions }
25
41
 
26
42
  // diffuse
27
43
  float diffusePDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
@@ -56,10 +72,15 @@ vec3 diffuseColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
56
72
  // specular
57
73
  float specularPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
58
74
 
59
- // See equation (17) in http://jcgt.org/published/0003/02/03/
75
+ // See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
60
76
  float filteredRoughness = surf.filteredRoughness;
61
77
  vec3 halfVector = getHalfVector( wi, wo );
62
- return ggxPDF( wi, halfVector, filteredRoughness ) / ( 4.0 * dot( wi, halfVector ) );
78
+
79
+ float incidentTheta = acos( wo.z );
80
+ float D = ggxDistribution( halfVector, filteredRoughness );
81
+ float G1 = ggxShadowMaskG1( incidentTheta, filteredRoughness );
82
+ float ggxPdf = D * G1 * max( 0.0, abs( dot( wo, halfVector ) ) ) / abs ( wo.z );
83
+ return ggxPdf / ( 4.0 * dot( wo, halfVector ) );
63
84
 
64
85
  }
65
86
 
@@ -92,21 +113,25 @@ vec3 specularColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
92
113
  float iorRatio = frontFace ? 1.0 / ior : ior;
93
114
  float G = ggxShadowMaskG2( wi, wo, filteredRoughness );
94
115
  float D = ggxDistribution( halfVector, filteredRoughness );
116
+ vec3 F = vec3( schlickFresnelFromIor( dot( wi, halfVector ), iorRatio ) ) * surf.specularColor * surf.specularIntensity;
95
117
 
96
- float F = schlickFresnelFromIor( dot( wi, halfVector ), iorRatio );
97
118
  float cosTheta = min( wo.z, 1.0 );
98
119
  float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
99
120
  bool cannotRefract = iorRatio * sinTheta > 1.0;
100
121
  if ( cannotRefract ) {
101
122
 
102
- F = 1.0;
123
+ F = vec3( 1.0 );
103
124
 
104
125
  }
105
126
 
127
+ float f0 = pow( ( iorRatio - 1.0 ) / ( iorRatio + 1.0 ), 2.0 );
128
+ vec3 iridescenceFresnel = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, halfVector ), surf.iridescenceThickness, vec3( f0 ) );
129
+ F = mix( F, iridescenceFresnel, surf.iridescence );
130
+
106
131
  vec3 color = mix( vec3( 1.0 ), surf.color, metalness );
107
132
  color = mix( color, vec3( 1.0 ), F );
108
133
  color *= G * D / ( 4.0 * abs( wi.z * wo.z ) );
109
- color *= mix( F, 1.0, metalness );
134
+ color *= mix( F, vec3( 1.0 ), metalness );
110
135
  color *= wi.z; // scale the light by the direction the light is coming in from
111
136
 
112
137
  return color;
@@ -213,7 +238,119 @@ vec3 transmissionColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
213
238
 
214
239
  }
215
240
 
216
- float bsdfPdf( vec3 wo, vec3 wi, SurfaceRec surf ) {
241
+ // clearcoat
242
+ float clearcoatPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
243
+
244
+ // See equation (27) in http://jcgt.org/published/0003/02/03/
245
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
246
+ vec3 halfVector = getHalfVector( wi, wo );
247
+ return ggxPDF( wo, halfVector, filteredClearcoatRoughness ) / ( 4.0 * dot( wi, halfVector ) );
248
+
249
+ }
250
+
251
+ vec3 clearcoatDirection( vec3 wo, SurfaceRec surf ) {
252
+
253
+ // sample ggx vndf distribution which gives a new normal
254
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
255
+ vec3 halfVector = ggxDirection(
256
+ wo,
257
+ filteredClearcoatRoughness,
258
+ filteredClearcoatRoughness,
259
+ rand(),
260
+ rand()
261
+ );
262
+
263
+ // apply to new ray by reflecting off the new normal
264
+ return - reflect( wo, halfVector );
265
+
266
+ }
267
+
268
+ void clearcoatColor( inout vec3 color, vec3 wo, vec3 wi, SurfaceRec surf ) {
269
+
270
+ float ior = 1.5;
271
+ bool frontFace = surf.frontFace;
272
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
273
+
274
+ vec3 halfVector = getHalfVector( wo, wi );
275
+ float iorRatio = frontFace ? 1.0 / ior : ior;
276
+ float G = ggxShadowMaskG2( wi, wo, filteredClearcoatRoughness );
277
+ float D = ggxDistribution( halfVector, filteredClearcoatRoughness );
278
+
279
+ float F = schlickFresnelFromIor( dot( wi, halfVector ), ior );
280
+ float cosTheta = min( wo.z, 1.0 );
281
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
282
+ bool cannotRefract = iorRatio * sinTheta > 1.0;
283
+ if ( cannotRefract ) {
284
+
285
+ F = 1.0;
286
+
287
+ }
288
+
289
+ float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
290
+
291
+ color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
292
+
293
+ }
294
+
295
+ // sheen
296
+ vec3 sheenColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
297
+
298
+ vec3 halfVector = getHalfVector( wo, wi );
299
+
300
+ float cosThetaO = saturateCos( wo.z );
301
+ float cosThetaI = saturateCos( wi.z );
302
+ float cosThetaH = halfVector.z;
303
+
304
+ float D = velvetD( cosThetaH, surf.sheenRoughness );
305
+ float G = velvetG( cosThetaO, cosThetaI, surf.sheenRoughness );
306
+
307
+ // See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
308
+ vec3 color = surf.sheenColor;
309
+ color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
310
+ color *= wi.z;
311
+
312
+ return color;
313
+
314
+ }
315
+
316
+ // bsdf
317
+ void getLobeWeights( vec3 wo, vec3 clearcoatWo, SurfaceRec surf, out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight ) {
318
+
319
+ float ior = surf.ior;
320
+ float metalness = surf.metalness;
321
+ float transmission = surf.transmission;
322
+ bool frontFace = surf.frontFace;
323
+
324
+ float ratio = frontFace ? 1.0 / ior : ior;
325
+ float cosTheta = min( wo.z, 1.0 );
326
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
327
+ float reflectance = schlickFresnelFromIor( cosTheta, ratio );
328
+ bool cannotRefract = ratio * sinTheta > 1.0;
329
+ if ( cannotRefract ) {
330
+
331
+ reflectance = 1.0;
332
+
333
+ }
334
+
335
+ float transSpecularProb = mix( reflectance, 1.0, metalness );
336
+ float diffSpecularProb = 0.5 + 0.5 * metalness;
337
+
338
+ clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
339
+ diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb ) * ( 1.0 - clearcoatWeight );
340
+ specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb * ( 1.0 - clearcoatWeight );
341
+ transmissionWeight = transmission * ( 1.0 - transSpecularProb ) * ( 1.0 - clearcoatWeight );
342
+
343
+ float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
344
+ float invTotalWeight = 1.0 / totalWeight;
345
+
346
+ diffuseWeight *= invTotalWeight;
347
+ specularWeight *= invTotalWeight;
348
+ transmissionWeight *= invTotalWeight;
349
+ clearcoatWeight *= invTotalWeight;
350
+
351
+ }
352
+
353
+ float bsdfPdf( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out float specularPdf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
217
354
 
218
355
  float ior = surf.ior;
219
356
  float metalness = surf.metalness;
@@ -234,43 +371,85 @@ float bsdfPdf( vec3 wo, vec3 wi, SurfaceRec surf ) {
234
371
  float spdf = 0.0;
235
372
  float dpdf = 0.0;
236
373
  float tpdf = 0.0;
374
+ float cpdf = 0.0;
237
375
 
238
376
  if ( wi.z < 0.0 ) {
239
377
 
240
- tpdf = transmissionPDF( wo, wi, surf );
378
+ if( transmissionWeight > 0.0 ) {
379
+
380
+ tpdf = transmissionPDF( wo, wi, surf );
381
+
382
+ }
241
383
 
242
384
  } else {
243
385
 
244
- spdf = specularPDF( wo, wi, surf );
245
- dpdf = diffusePDF( wo, wi, surf );
386
+ if( diffuseWeight > 0.0 ) {
387
+
388
+ dpdf = diffusePDF( wo, wi, surf );
389
+
390
+ }
391
+
392
+ if( specularWeight > 0.0 ) {
393
+
394
+ spdf = specularPDF( wo, wi, surf );
395
+
396
+ }
397
+
398
+ }
399
+
400
+ if( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
401
+
402
+ cpdf = clearcoatPDF( clearcoatWo, clearcoatWi, surf );
246
403
 
247
404
  }
248
405
 
249
- float transSpecularProb = mix( reflectance, 1.0, metalness );
250
- float diffSpecularProb = 0.5 + 0.5 * metalness;
251
406
  float pdf =
252
- spdf * transmission * transSpecularProb
253
- + tpdf * transmission * ( 1.0 - transSpecularProb )
254
- + spdf * ( 1.0 - transmission ) * diffSpecularProb
255
- + dpdf * ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
407
+ dpdf * diffuseWeight
408
+ + spdf * specularWeight
409
+ + tpdf * transmissionWeight
410
+ + cpdf * clearcoatWeight;
411
+
412
+ // retrieve specular rays for the shadows flag
413
+ specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
256
414
 
257
415
  return pdf;
258
416
 
259
417
  }
260
418
 
261
- vec3 bsdfColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
419
+ vec3 bsdfColor( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
262
420
 
263
421
  vec3 color = vec3( 0.0 );
264
422
  if ( wi.z < 0.0 ) {
265
423
 
266
- color = transmissionColor( wo, wi, surf );
424
+ if( transmissionWeight > 0.0 ) {
425
+
426
+ color = transmissionColor( wo, wi, surf );
427
+
428
+ }
267
429
 
268
430
  } else {
269
431
 
270
- color = diffuseColor( wo, wi, surf );
271
- color *= 1.0 - surf.transmission;
432
+ if( diffuseWeight > 0.0 ) {
433
+
434
+ color = diffuseColor( wo, wi, surf );
435
+ color *= 1.0 - surf.transmission;
272
436
 
273
- color += specularColor( wo, wi, surf );
437
+ }
438
+
439
+ if( specularWeight > 0.0 ) {
440
+
441
+ color += specularColor( wo, wi, surf );
442
+
443
+ }
444
+
445
+ color *= sheenAlbedoScaling( wo, wi, surf );
446
+ color += sheenColor( wo, wi, surf );
447
+
448
+ }
449
+
450
+ if( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
451
+
452
+ clearcoatColor( color, clearcoatWo, clearcoatWi, surf );
274
453
 
275
454
  }
276
455
 
@@ -278,62 +457,89 @@ vec3 bsdfColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
278
457
 
279
458
  }
280
459
 
281
- float bsdfResult( vec3 wo, vec3 wi, SurfaceRec surf, out vec3 color ) {
460
+ float bsdfResult( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out vec3 color ) {
282
461
 
283
- color = bsdfColor( wo, wi, surf );
284
- return bsdfPdf( wo, wi, surf );
462
+ float diffuseWeight;
463
+ float specularWeight;
464
+ float transmissionWeight;
465
+ float clearcoatWeight;
466
+ getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
467
+
468
+ float specularPdf;
469
+ color = bsdfColor( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
470
+ return bsdfPdf( wo, clearcoatWo, wi, clearcoatWi, surf, specularPdf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
285
471
 
286
472
  }
287
473
 
288
- SampleRec bsdfSample( vec3 wo, SurfaceRec surf ) {
474
+ SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis, mat3 clearcoatNormalBasis, mat3 clearcoatInvBasis, SurfaceRec surf ) {
289
475
 
290
- float ior = surf.ior;
291
- float metalness = surf.metalness;
292
- float transmission = surf.transmission;
293
- bool frontFace = surf.frontFace;
476
+ float diffuseWeight;
477
+ float specularWeight;
478
+ float transmissionWeight;
479
+ float clearcoatWeight;
480
+ getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
294
481
 
295
- float ratio = frontFace ? 1.0 / ior : ior;
296
- float cosTheta = min( wo.z, 1.0 );
297
- float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
298
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
299
- bool cannotRefract = ratio * sinTheta > 1.0;
300
- if ( cannotRefract ) {
482
+ float pdf[4];
483
+ pdf[0] = diffuseWeight;
484
+ pdf[1] = specularWeight;
485
+ pdf[2] = transmissionWeight;
486
+ pdf[3] = clearcoatWeight;
301
487
 
302
- reflectance = 1.0;
488
+ float cdf[4];
489
+ cdf[0] = pdf[0];
490
+ cdf[1] = pdf[1] + cdf[0];
491
+ cdf[2] = pdf[2] + cdf[1];
492
+ cdf[3] = pdf[3] + cdf[2];
303
493
 
304
- }
494
+ if( cdf[3] != 0.0 ) {
305
495
 
306
- SampleRec result;
307
- if ( rand() < transmission ) {
496
+ float invMaxCdf = 1.0 / cdf[3];
497
+ cdf[0] *= invMaxCdf;
498
+ cdf[1] *= invMaxCdf;
499
+ cdf[2] *= invMaxCdf;
500
+ cdf[3] *= invMaxCdf;
308
501
 
309
- float specularProb = mix( reflectance, 1.0, metalness );
310
- if ( rand() < specularProb ) {
502
+ } else {
311
503
 
312
- result.direction = specularDirection( wo, surf );
504
+ cdf[0] = 1.0;
505
+ cdf[1] = 0.0;
506
+ cdf[2] = 0.0;
507
+ cdf[3] = 0.0;
313
508
 
314
- } else {
509
+ }
315
510
 
316
- result.direction = transmissionDirection( wo, surf );
511
+ vec3 wi;
512
+ vec3 clearcoatWi;
317
513
 
318
- }
514
+ float r = rand();
515
+ if ( r <= cdf[0] ) {
319
516
 
320
- } else {
517
+ wi = diffuseDirection( wo, surf );
518
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
321
519
 
322
- float specularProb = 0.5 + 0.5 * metalness;
323
- if ( rand() < specularProb ) {
520
+ } else if ( r <= cdf[1] ) {
324
521
 
325
- result.direction = specularDirection( wo, surf );
522
+ wi = specularDirection( wo, surf );
523
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
326
524
 
327
- } else {
525
+ } else if ( r <= cdf[2] ) {
328
526
 
329
- result.direction = diffuseDirection( wo, surf );
527
+ wi = transmissionDirection( wo, surf );
528
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
330
529
 
331
- }
530
+ } else if ( r <= cdf[3] ) {
531
+
532
+ clearcoatWi = clearcoatDirection( clearcoatWo, surf );
533
+ wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
332
534
 
333
535
  }
334
536
 
335
- result.pdf = bsdfPdf( wo, result.direction, surf );
336
- result.color = bsdfColor( wo, result.direction, surf );
537
+ SampleRec result;
538
+ result.pdf = bsdfPdf( wo, clearcoatWo, wi, clearcoatWi, surf, result.specularPdf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
539
+ result.color = bsdfColor( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
540
+ result.direction = wi;
541
+ result.clearcoatDirection = clearcoatWi;
542
+
337
543
  return result;
338
544
 
339
545
  }
@@ -0,0 +1,98 @@
1
+ export const shaderSheenFunctions = /* glsl */`
2
+
3
+ // See equation (2) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
4
+ float velvetD( float cosThetaH, float roughness ) {
5
+
6
+ float alpha = max( roughness, 0.07 );
7
+ alpha = alpha * alpha;
8
+
9
+ float invAlpha = 1.0 / alpha;
10
+
11
+ float sqrCosThetaH = cosThetaH * cosThetaH;
12
+ float sinThetaH = max( 1.0 - sqrCosThetaH, 0.001 );
13
+
14
+ return ( 2.0 + invAlpha ) * pow( sinThetaH, 0.5 * invAlpha ) / ( 2.0 * PI );
15
+
16
+ }
17
+
18
+ float velvetParamsInterpolate( int i, float oneMinusAlphaSquared ) {
19
+
20
+ const float p0[5] = float[5]( 25.3245, 3.32435, 0.16801, -1.27393, -4.85967 );
21
+ const float p1[5] = float[5]( 21.5473, 3.82987, 0.19823, -1.97760, -4.32054 );
22
+
23
+ return mix( p1[i], p0[i], oneMinusAlphaSquared );
24
+
25
+ }
26
+
27
+ float velvetL( float x, float alpha ) {
28
+
29
+ float oneMinusAlpha = 1.0 - alpha;
30
+ float oneMinusAlphaSquared = oneMinusAlpha * oneMinusAlpha;
31
+
32
+ float a = velvetParamsInterpolate( 0, oneMinusAlphaSquared );
33
+ float b = velvetParamsInterpolate( 1, oneMinusAlphaSquared );
34
+ float c = velvetParamsInterpolate( 2, oneMinusAlphaSquared );
35
+ float d = velvetParamsInterpolate( 3, oneMinusAlphaSquared );
36
+ float e = velvetParamsInterpolate( 4, oneMinusAlphaSquared );
37
+
38
+ return a / ( 1.0 + b * pow( abs( x ), c ) ) + d * x + e;
39
+
40
+ }
41
+
42
+ // See equation (3) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
43
+ float velvetLambda( float cosTheta, float alpha ) {
44
+
45
+ return abs( cosTheta ) < 0.5 ? exp( velvetL( cosTheta, alpha ) ) : exp( 2.0 * velvetL( 0.5, alpha ) - velvetL( 1.0 - cosTheta, alpha ) );
46
+
47
+ }
48
+
49
+ // See Section 3, Shadowing Term, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
50
+ float velvetG( float cosThetaO, float cosThetaI, float roughness ) {
51
+
52
+ float alpha = max( roughness, 0.07 );
53
+ alpha = alpha * alpha;
54
+
55
+ return 1.0 / ( 1.0 + velvetLambda( cosThetaO, alpha ) + velvetLambda( cosThetaI, alpha ) );
56
+
57
+ }
58
+
59
+ float directionalAlbedoSheen( float cosTheta, float alpha ) {
60
+
61
+ cosTheta = saturate( cosTheta );
62
+
63
+ float c = 1.0 - cosTheta;
64
+ float c3 = c * c * c;
65
+
66
+ return 0.65584461 * c3 + 1.0 / ( 4.16526551 + exp( -7.97291361 * sqrt( alpha ) + 6.33516894 ) );
67
+
68
+ }
69
+
70
+ float sheenAlbedoScaling( vec3 wo, vec3 wi, SurfaceRec surf ) {
71
+
72
+ float alpha = max( surf.sheenRoughness, 0.07 );
73
+ alpha = alpha * alpha;
74
+
75
+ float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
76
+
77
+ float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
78
+ float eWi = directionalAlbedoSheen( saturateCos( wi.z ), alpha );
79
+
80
+ return min( 1.0 - maxSheenColor * eWo, 1.0 - maxSheenColor * eWi );
81
+
82
+ }
83
+
84
+ // See Section 5, Layering, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
85
+ float sheenAlbedoScaling( vec3 wo, SurfaceRec surf ) {
86
+
87
+ float alpha = max( surf.sheenRoughness, 0.07 );
88
+ alpha = alpha * alpha;
89
+
90
+ float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
91
+
92
+ float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
93
+
94
+ return 1.0 - maxSheenColor * eWo;
95
+
96
+ }
97
+
98
+ `;