three-gpu-pathtracer 0.0.6 → 0.0.8

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 (49) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +887 -781
  3. package/build/index.module.js +6070 -5224
  4. package/build/index.module.js.map +1 -1
  5. package/build/index.umd.cjs +6071 -5221
  6. package/build/index.umd.cjs.map +1 -1
  7. package/package.json +69 -68
  8. package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
  9. package/src/core/MaterialReducer.js +256 -256
  10. package/src/core/PathTracingRenderer.js +270 -255
  11. package/src/core/PathTracingSceneGenerator.js +69 -68
  12. package/src/index.js +39 -33
  13. package/src/materials/AlphaDisplayMaterial.js +48 -48
  14. package/src/materials/AmbientOcclusionMaterial.js +197 -197
  15. package/src/materials/BlendMaterial.js +67 -67
  16. package/src/materials/DenoiseMaterial.js +142 -0
  17. package/src/materials/GraphMaterial.js +243 -0
  18. package/src/materials/LambertPathTracingMaterial.js +285 -285
  19. package/src/materials/MaterialBase.js +56 -56
  20. package/src/materials/PhysicalPathTracingMaterial.js +973 -922
  21. package/src/objects/EquirectCamera.js +13 -13
  22. package/src/objects/PhysicalCamera.js +28 -28
  23. package/src/objects/PhysicalSpotLight.js +14 -14
  24. package/src/objects/ShapedAreaLight.js +12 -12
  25. package/src/shader/shaderEnvMapSampling.js +59 -59
  26. package/src/shader/shaderGGXFunctions.js +100 -108
  27. package/src/shader/shaderIridescenceFunctions.js +130 -130
  28. package/src/shader/shaderLayerTexelFetchFunctions.js +25 -0
  29. package/src/shader/shaderLightSampling.js +231 -231
  30. package/src/shader/shaderMaterialSampling.js +504 -546
  31. package/src/shader/shaderSheenFunctions.js +98 -98
  32. package/src/shader/shaderStructs.js +321 -307
  33. package/src/shader/shaderUtils.js +403 -350
  34. package/src/textures/GradientEquirectTexture.js +35 -0
  35. package/src/textures/ProceduralEquirectTexture.js +75 -0
  36. package/src/uniforms/AttributesTextureArray.js +35 -0
  37. package/src/uniforms/EquirectHdrInfoUniform.js +259 -259
  38. package/src/uniforms/FloatAttributeTextureArray.js +169 -0
  39. package/src/uniforms/IESProfilesTexture.js +100 -100
  40. package/src/uniforms/LightsInfoUniformStruct.js +162 -162
  41. package/src/uniforms/MaterialsTexture.js +420 -406
  42. package/src/uniforms/PhysicalCameraUniform.js +36 -36
  43. package/src/uniforms/RenderTarget2DArray.js +97 -93
  44. package/src/uniforms/utils.js +21 -0
  45. package/src/utils/BlurredEnvMapGenerator.js +116 -113
  46. package/src/utils/GeometryPreparationUtils.js +214 -194
  47. package/src/utils/IESLoader.js +325 -325
  48. package/src/utils/UVUnwrapper.js +101 -101
  49. package/src/workers/PathTracingSceneWorker.js +42 -42
@@ -1,546 +1,504 @@
1
- import { shaderGGXFunctions } from './shaderGGXFunctions.js';
2
- import { shaderSheenFunctions } from './shaderSheenFunctions.js';
3
- import { shaderIridescenceFunctions } from './shaderIridescenceFunctions.js';
4
-
5
- export const shaderMaterialSampling = /* glsl */`
6
-
7
- struct SurfaceRec {
8
- vec3 normal;
9
- vec3 faceNormal;
10
- bool frontFace;
11
- float roughness;
12
- float filteredRoughness;
13
- float metalness;
14
- vec3 color;
15
- vec3 emission;
16
- float transmission;
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;
28
- };
29
-
30
- struct SampleRec {
31
- float specularPdf;
32
- float pdf;
33
- vec3 direction;
34
- vec3 clearcoatDirection;
35
- vec3 color;
36
- };
37
-
38
- ${ shaderGGXFunctions }
39
- ${ shaderSheenFunctions }
40
- ${ shaderIridescenceFunctions }
41
-
42
- // diffuse
43
- float diffusePDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
44
-
45
- // https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html#lightscattering/thescatteringpdf
46
- float cosValue = wi.z;
47
- return cosValue / PI;
48
-
49
- }
50
-
51
- vec3 diffuseDirection( vec3 wo, SurfaceRec surf ) {
52
-
53
- vec3 lightDirection = randDirection();
54
- lightDirection.z += 1.0;
55
- lightDirection = normalize( lightDirection );
56
-
57
- return lightDirection;
58
-
59
- }
60
-
61
- vec3 diffuseColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
62
-
63
- // TODO: scale by 1 - F here
64
- // note on division by PI
65
- // https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
66
- float metalFactor = ( 1.0 - surf.metalness ) * wi.z / ( PI * PI );
67
- float transmissionFactor = 1.0 - surf.transmission;
68
- return surf.color * metalFactor * transmissionFactor;
69
-
70
- }
71
-
72
- // specular
73
- float specularPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
74
-
75
- // See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
76
- float filteredRoughness = surf.filteredRoughness;
77
- vec3 halfVector = getHalfVector( wi, wo );
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 ) );
84
-
85
- }
86
-
87
- vec3 specularDirection( vec3 wo, SurfaceRec surf ) {
88
-
89
- // sample ggx vndf distribution which gives a new normal
90
- float filteredRoughness = surf.filteredRoughness;
91
- vec3 halfVector = ggxDirection(
92
- wo,
93
- filteredRoughness,
94
- filteredRoughness,
95
- rand(),
96
- rand()
97
- );
98
-
99
- // apply to new ray by reflecting off the new normal
100
- return - reflect( wo, halfVector );
101
-
102
- }
103
-
104
- vec3 specularColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
105
-
106
- // if roughness is set to 0 then D === NaN which results in black pixels
107
- float metalness = surf.metalness;
108
- float ior = surf.ior;
109
- bool frontFace = surf.frontFace;
110
- float filteredRoughness = surf.filteredRoughness;
111
-
112
- vec3 halfVector = getHalfVector( wo, wi );
113
- float iorRatio = frontFace ? 1.0 / ior : ior;
114
- float G = ggxShadowMaskG2( wi, wo, filteredRoughness );
115
- float D = ggxDistribution( halfVector, filteredRoughness );
116
- vec3 F = vec3( schlickFresnelFromIor( dot( wi, halfVector ), iorRatio ) ) * surf.specularColor * surf.specularIntensity;
117
-
118
- float cosTheta = min( wo.z, 1.0 );
119
- float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
120
- bool cannotRefract = iorRatio * sinTheta > 1.0;
121
- if ( cannotRefract ) {
122
-
123
- F = vec3( 1.0 );
124
-
125
- }
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
-
131
- vec3 color = mix( vec3( 1.0 ), surf.color, metalness );
132
- color = mix( color, vec3( 1.0 ), F );
133
- color *= G * D / ( 4.0 * abs( wi.z * wo.z ) );
134
- color *= mix( F, vec3( 1.0 ), metalness );
135
- color *= wi.z; // scale the light by the direction the light is coming in from
136
-
137
- return color;
138
-
139
- }
140
-
141
- /*
142
- // transmission
143
- function transmissionPDF( wo, wi, material, surf ) {
144
-
145
- // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
146
-
147
- const { roughness, ior } = material;
148
- const { frontFace } = hit;
149
- const ratio = frontFace ? ior : 1 / ior;
150
- const minRoughness = Math.max( roughness, MIN_ROUGHNESS );
151
-
152
- halfVector.set( 0, 0, 0 ).addScaledVector( wi, ratio ).addScaledVector( wo, 1.0 ).normalize().multiplyScalar( - 1 );
153
-
154
- const denom = Math.pow( ratio * halfVector.dot( wi ) + 1.0 * halfVector.dot( wo ), 2.0 );
155
- return ggxPDF( wo, halfVector, minRoughness ) / denom;
156
-
157
- }
158
-
159
- function transmissionDirection( wo, hit, material, lightDirection ) {
160
-
161
- const { roughness, ior } = material;
162
- const { frontFace } = hit;
163
- const ratio = frontFace ? 1 / ior : ior;
164
- const minRoughness = Math.max( roughness, MIN_ROUGHNESS );
165
-
166
- // sample ggx vndf distribution which gives a new normal
167
- ggxDirection(
168
- wo,
169
- minRoughness,
170
- minRoughness,
171
- Math.random(),
172
- Math.random(),
173
- halfVector,
174
- );
175
-
176
- // apply to new ray by reflecting off the new normal
177
- tempDir.copy( wo ).multiplyScalar( - 1 );
178
- refract( tempDir, halfVector, ratio, lightDirection );
179
-
180
- }
181
-
182
- function transmissionColor( wo, wi, material, hit, colorTarget ) {
183
-
184
- const { metalness, transmission } = material;
185
- colorTarget
186
- .copy( material.color )
187
- .multiplyScalar( ( 1.0 - metalness ) * wo.z )
188
- .multiplyScalar( transmission );
189
-
190
- }
191
- */
192
-
193
- // TODO: This is just using a basic cosine-weighted specular distribution with an
194
- // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
195
- float transmissionPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
196
-
197
- float ior = surf.ior;
198
- bool frontFace = surf.frontFace;
199
-
200
- float ratio = frontFace ? 1.0 / ior : ior;
201
- float cosTheta = min( wo.z, 1.0 );
202
- float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
203
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
204
- bool cannotRefract = ratio * sinTheta > 1.0;
205
- if ( cannotRefract ) {
206
-
207
- return 0.0;
208
-
209
- }
210
-
211
- return 1.0 / ( 1.0 - reflectance );
212
-
213
- }
214
-
215
- vec3 transmissionDirection( vec3 wo, SurfaceRec surf ) {
216
-
217
- float roughness = surf.roughness;
218
- float ior = surf.ior;
219
- bool frontFace = surf.frontFace;
220
- float ratio = frontFace ? 1.0 / ior : ior;
221
-
222
- vec3 lightDirection = refract( - wo, vec3( 0.0, 0.0, 1.0 ), ratio );
223
- lightDirection += randDirection() * roughness;
224
- return normalize( lightDirection );
225
-
226
- }
227
-
228
- vec3 transmissionColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
229
-
230
- float metalness = surf.metalness;
231
- float transmission = surf.transmission;
232
-
233
- vec3 color = surf.color;
234
- color *= ( 1.0 - metalness );
235
- color *= transmission;
236
-
237
- return color;
238
-
239
- }
240
-
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 ) {
354
-
355
- float ior = surf.ior;
356
- float metalness = surf.metalness;
357
- float transmission = surf.transmission;
358
- bool frontFace = surf.frontFace;
359
-
360
- float ratio = frontFace ? 1.0 / ior : ior;
361
- float cosTheta = min( wo.z, 1.0 );
362
- float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
363
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
364
- bool cannotRefract = ratio * sinTheta > 1.0;
365
- if ( cannotRefract ) {
366
-
367
- reflectance = 1.0;
368
-
369
- }
370
-
371
- float spdf = 0.0;
372
- float dpdf = 0.0;
373
- float tpdf = 0.0;
374
- float cpdf = 0.0;
375
-
376
- if ( wi.z < 0.0 ) {
377
-
378
- if( transmissionWeight > 0.0 ) {
379
-
380
- tpdf = transmissionPDF( wo, wi, surf );
381
-
382
- }
383
-
384
- } else {
385
-
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 );
403
-
404
- }
405
-
406
- float pdf =
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;
414
-
415
- return pdf;
416
-
417
- }
418
-
419
- vec3 bsdfColor( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
420
-
421
- vec3 color = vec3( 0.0 );
422
- if ( wi.z < 0.0 ) {
423
-
424
- if( transmissionWeight > 0.0 ) {
425
-
426
- color = transmissionColor( wo, wi, surf );
427
-
428
- }
429
-
430
- } else {
431
-
432
- if( diffuseWeight > 0.0 ) {
433
-
434
- color = diffuseColor( wo, wi, surf );
435
- color *= 1.0 - surf.transmission;
436
-
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 );
453
-
454
- }
455
-
456
- return color;
457
-
458
- }
459
-
460
- float bsdfResult( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out vec3 color ) {
461
-
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 );
471
-
472
- }
473
-
474
- SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis, mat3 clearcoatNormalBasis, mat3 clearcoatInvBasis, SurfaceRec surf ) {
475
-
476
- float diffuseWeight;
477
- float specularWeight;
478
- float transmissionWeight;
479
- float clearcoatWeight;
480
- getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
481
-
482
- float pdf[4];
483
- pdf[0] = diffuseWeight;
484
- pdf[1] = specularWeight;
485
- pdf[2] = transmissionWeight;
486
- pdf[3] = clearcoatWeight;
487
-
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];
493
-
494
- if( cdf[3] != 0.0 ) {
495
-
496
- float invMaxCdf = 1.0 / cdf[3];
497
- cdf[0] *= invMaxCdf;
498
- cdf[1] *= invMaxCdf;
499
- cdf[2] *= invMaxCdf;
500
- cdf[3] *= invMaxCdf;
501
-
502
- } else {
503
-
504
- cdf[0] = 1.0;
505
- cdf[1] = 0.0;
506
- cdf[2] = 0.0;
507
- cdf[3] = 0.0;
508
-
509
- }
510
-
511
- vec3 wi;
512
- vec3 clearcoatWi;
513
-
514
- float r = rand();
515
- if ( r <= cdf[0] ) {
516
-
517
- wi = diffuseDirection( wo, surf );
518
- clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
519
-
520
- } else if ( r <= cdf[1] ) {
521
-
522
- wi = specularDirection( wo, surf );
523
- clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
524
-
525
- } else if ( r <= cdf[2] ) {
526
-
527
- wi = transmissionDirection( wo, surf );
528
- clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
529
-
530
- } else if ( r <= cdf[3] ) {
531
-
532
- clearcoatWi = clearcoatDirection( clearcoatWo, surf );
533
- wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
534
-
535
- }
536
-
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
-
543
- return result;
544
-
545
- }
546
- `;
1
+ import { shaderGGXFunctions } from './shaderGGXFunctions.js';
2
+ import { shaderSheenFunctions } from './shaderSheenFunctions.js';
3
+ import { shaderIridescenceFunctions } from './shaderIridescenceFunctions.js';
4
+
5
+ /*
6
+ wi : incident vector or light vector (pointing toward the light)
7
+ wo : outgoing vector or view vector (pointing towards the camera)
8
+ wh : computed half vector from wo and wi
9
+ Eval : Get the color and pdf for a direction
10
+ Sample : Get the direction, color, and pdf for a sample
11
+ eta : Greek character used to denote the "ratio of ior"
12
+ f0 : Amount of light reflected when looking at a surface head on - "fresnel 0"
13
+ */
14
+
15
+ export const shaderMaterialSampling = /* glsl */`
16
+
17
+ struct SurfaceRec {
18
+ vec3 normal;
19
+ vec3 faceNormal;
20
+ bool frontFace;
21
+ float roughness;
22
+ float filteredRoughness;
23
+ float metalness;
24
+ vec3 color;
25
+ vec3 emission;
26
+ float transmission;
27
+ bool thinFilm;
28
+ float ior;
29
+ float eta;
30
+ float f0;
31
+ float clearcoat;
32
+ float clearcoatRoughness;
33
+ float filteredClearcoatRoughness;
34
+ vec3 sheenColor;
35
+ float sheenRoughness;
36
+ float iridescence;
37
+ float iridescenceIor;
38
+ float iridescenceThickness;
39
+ vec3 specularColor;
40
+ float specularIntensity;
41
+ vec3 attenuationColor;
42
+ float attenuationDistance;
43
+ };
44
+
45
+ struct SampleRec {
46
+ float specularPdf;
47
+ float pdf;
48
+ vec3 direction;
49
+ vec3 clearcoatDirection;
50
+ vec3 color;
51
+ };
52
+
53
+ ${ shaderGGXFunctions }
54
+ ${ shaderSheenFunctions }
55
+ ${ shaderIridescenceFunctions }
56
+
57
+ float disneyFresnel( SurfaceRec surf, vec3 wo, vec3 wi, vec3 wh ) {
58
+
59
+ float dotHV = dot( wo, wh );
60
+ float dotHL = dot( wi, wh );
61
+
62
+ // TODO: some model-viewer test models look better when surf.eta is set to a non 1.5 eta here here?
63
+ // and the furnace test seems to pass when it === 1.0
64
+ // float dielectricFresnel = dielectricFresnel( abs( dotHV ), surf.eta );
65
+ float dielectricFresnel = dielectricFresnel( abs( dotHV ), 1.0 / 1.1 );
66
+ float metallicFresnel = schlickFresnel( dotHL, surf.f0 );
67
+
68
+ return mix( dielectricFresnel, metallicFresnel, surf.metalness );
69
+
70
+ }
71
+
72
+ // diffuse
73
+ float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf, out vec3 color ) {
74
+
75
+ // https://schuttejoe.github.io/post/disneybsdf/
76
+ float fl = schlickFresnel( wi.z, 0.0 );
77
+ float fv = schlickFresnel( wo.z, 0.0 );
78
+
79
+ float metalFactor = ( 1.0 - surf.metalness );
80
+ float transFactor = ( 1.0 - surf.transmission );
81
+ float rr = 0.5 + 2.0 * surf.roughness * fl * fl;
82
+ float retro = rr * ( fl + fv + fl * fv * ( rr - 1.0f ) );
83
+ float lambert = ( 1.0f - 0.5f * fl ) * ( 1.0f - 0.5f * fv );
84
+
85
+ // TODO: subsurface approx?
86
+
87
+ float FM = disneyFresnel( surf, wo, wi, wh );
88
+
89
+ color = ( 1.0 - FM ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
90
+ return wi.z / PI;
91
+
92
+ }
93
+
94
+ vec3 diffuseDirection( vec3 wo, SurfaceRec surf ) {
95
+
96
+ vec3 lightDirection = randDirection();
97
+ lightDirection.z += 1.0;
98
+ lightDirection = normalize( lightDirection );
99
+
100
+ return lightDirection;
101
+
102
+ }
103
+
104
+ // specular
105
+ float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf, out vec3 color ) {
106
+
107
+ // if roughness is set to 0 then D === NaN which results in black pixels
108
+ float metalness = surf.metalness;
109
+ float filteredRoughness = surf.filteredRoughness;
110
+
111
+ float eta = surf.eta;
112
+ float f0 = surf.f0;
113
+ float G = ggxShadowMaskG2( wi, wo, filteredRoughness );
114
+ float D = ggxDistribution( wh, filteredRoughness );
115
+ float FM = disneyFresnel( surf, wo, wi, wh );
116
+ float cosTheta = min( wo.z, 1.0 );
117
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
118
+ bool cannotRefract = eta * sinTheta > 1.0;
119
+ if ( cannotRefract ) {
120
+
121
+ FM = 1.0;
122
+
123
+ }
124
+
125
+ vec3 metalColor = surf.color;
126
+ vec3 dielectricColor = f0 * surf.specularColor;
127
+ vec3 specColor = mix( dielectricColor, metalColor, surf.metalness );
128
+
129
+ vec3 iridescenceF = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, wh ), surf.iridescenceThickness, vec3( f0 ) );
130
+ vec3 iridescenceMix = mix( vec3( FM ), iridescenceF, surf.iridescence );
131
+ vec3 F = mix( specColor, vec3( 1.0 ), iridescenceMix );
132
+
133
+ color = mix( surf.specularIntensity, 1.0, surf.metalness ) * wi.z * F * G * D / ( 4.0 * abs( wi.z * wo.z ) );
134
+
135
+ // PDF
136
+ // See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
137
+ float incidentTheta = acos( wo.z );
138
+ float G1 = ggxShadowMaskG1( incidentTheta, filteredRoughness );
139
+ float ggxPdf = D * G1 * max( 0.0, abs( dot( wo, wh ) ) ) / abs ( wo.z );
140
+ return ggxPdf / ( 4.0 * dot( wo, wh ) );
141
+
142
+ }
143
+
144
+ vec3 specularDirection( vec3 wo, SurfaceRec surf ) {
145
+
146
+ // sample ggx vndf distribution which gives a new normal
147
+ float filteredRoughness = surf.filteredRoughness;
148
+ vec3 halfVector = ggxDirection(
149
+ wo,
150
+ filteredRoughness,
151
+ filteredRoughness,
152
+ rand(),
153
+ rand()
154
+ );
155
+
156
+ // apply to new ray by reflecting off the new normal
157
+ return - reflect( wo, halfVector );
158
+
159
+ }
160
+
161
+
162
+ // transmission
163
+ /*
164
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf, out vec3 color ) {
165
+
166
+ // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
167
+
168
+ float filteredRoughness = surf.filteredRoughness;
169
+ float eta = surf.eta;
170
+ bool frontFace = surf.frontFace;
171
+ bool thinFilm = surf.thinFilm;
172
+
173
+ vec3 col = thinFilm || frontFace ? surf.color : vec3( 1.0 );
174
+ color = surf.transmission * col;
175
+
176
+ float denom = pow( eta * dot( wi, wh ) + dot( wo, wh ), 2.0 );
177
+ return ggxPDF( wo, wh, filteredRoughness ) / denom;
178
+
179
+ }
180
+
181
+ vec3 transmissionDirection( vec3 wo, SurfaceRec surf ) {
182
+
183
+ float filteredRoughness = surf.filteredRoughness;
184
+ float eta = surf.eta;
185
+ bool frontFace = surf.frontFace;
186
+
187
+ // sample ggx vndf distribution which gives a new normal
188
+ vec3 halfVector = ggxDirection(
189
+ wo,
190
+ filteredRoughness,
191
+ filteredRoughness,
192
+ rand(),
193
+ rand()
194
+ );
195
+
196
+
197
+ // TODO: support thin film
198
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
199
+ return normalize( lightDirection );
200
+
201
+ }
202
+ */
203
+
204
+ // TODO: This is just using a basic cosine-weighted specular distribution with an
205
+ // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
206
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf, out vec3 color ) {
207
+
208
+ // only attenuate the color if it's on the way in
209
+ vec3 col = surf.thinFilm || surf.frontFace ? surf.color : vec3( 1.0 );
210
+ color = surf.transmission * col;
211
+
212
+ // PDF
213
+ float eta = surf.eta;
214
+ float f0 = surf.f0;
215
+ float cosTheta = min( wo.z, 1.0 );
216
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
217
+ float reflectance = schlickFresnel( cosTheta, f0 );
218
+ bool cannotRefract = eta * sinTheta > 1.0;
219
+ if ( cannotRefract ) {
220
+
221
+ return 0.0;
222
+
223
+ }
224
+
225
+ return 1.0 / ( 1.0 - reflectance );
226
+
227
+ }
228
+
229
+ vec3 transmissionDirection( vec3 wo, SurfaceRec surf ) {
230
+
231
+ float roughness = surf.roughness;
232
+ float eta = surf.eta;
233
+ vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + randDirection() * roughness );
234
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
235
+
236
+ if ( surf.thinFilm ) {
237
+
238
+ lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
239
+
240
+ }
241
+ return normalize( lightDirection );
242
+
243
+ }
244
+
245
+ // clearcoat
246
+ float clearcoatEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf, inout vec3 color ) {
247
+
248
+ float ior = 1.5;
249
+ float f0 = iorRatioToF0( ior );
250
+ bool frontFace = surf.frontFace;
251
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
252
+
253
+ float eta = frontFace ? 1.0 / ior : ior;
254
+ float G = ggxShadowMaskG2( wi, wo, filteredClearcoatRoughness );
255
+ float D = ggxDistribution( wh, filteredClearcoatRoughness );
256
+ float F = schlickFresnel( dot( wi, wh ), f0 );
257
+ float cosTheta = min( wo.z, 1.0 );
258
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
259
+ bool cannotRefract = eta * sinTheta > 1.0;
260
+ if ( cannotRefract ) {
261
+
262
+ F = 1.0;
263
+
264
+ }
265
+
266
+ float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
267
+ color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
268
+
269
+ // PDF
270
+ // See equation (27) in http://jcgt.org/published/0003/02/03/
271
+ return ggxPDF( wo, wh, filteredClearcoatRoughness ) / ( 4.0 * dot( wi, wh ) );
272
+
273
+ }
274
+
275
+ vec3 clearcoatDirection( vec3 wo, SurfaceRec surf ) {
276
+
277
+ // sample ggx vndf distribution which gives a new normal
278
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
279
+ vec3 halfVector = ggxDirection(
280
+ wo,
281
+ filteredClearcoatRoughness,
282
+ filteredClearcoatRoughness,
283
+ rand(),
284
+ rand()
285
+ );
286
+
287
+ // apply to new ray by reflecting off the new normal
288
+ return - reflect( wo, halfVector );
289
+
290
+ }
291
+
292
+ // sheen
293
+ vec3 sheenColor( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf ) {
294
+
295
+ float cosThetaO = saturateCos( wo.z );
296
+ float cosThetaI = saturateCos( wi.z );
297
+ float cosThetaH = wh.z;
298
+
299
+ float D = velvetD( cosThetaH, surf.sheenRoughness );
300
+ float G = velvetG( cosThetaO, cosThetaI, surf.sheenRoughness );
301
+
302
+ // See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
303
+ vec3 color = surf.sheenColor;
304
+ color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
305
+ color *= wi.z;
306
+
307
+ return color;
308
+
309
+ }
310
+
311
+ // bsdf
312
+ #define DIFF_WEIGHT 0
313
+ #define SPEC_WEIGHT 1
314
+ #define TRANS_WEIGHT 2
315
+ #define CC_WEIGHT 3
316
+ void getLobeWeights( vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRec surf, out float[ 4 ] weights ) {
317
+
318
+ float metalness = surf.metalness;
319
+ float transmission = surf.transmission;
320
+
321
+ float eta = surf.eta;
322
+ float f0 = surf.f0;
323
+ float cosTheta = min( wo.z, 1.0 );
324
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
325
+
326
+ // TODO: does "cannot refract" belong in disney fresnel?
327
+ float reflectance = disneyFresnel( surf, wo, wi, wh );
328
+ bool cannotRefract = eta * sinTheta > 1.0;
329
+ if ( cannotRefract ) {
330
+
331
+ reflectance = 1.0;
332
+
333
+ }
334
+
335
+ float transSpecularProb = mix( max( 0.25, reflectance ), 1.0, metalness );
336
+ float diffSpecularProb = 0.5 + 0.5 * metalness;
337
+
338
+ float diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
339
+ float specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb;
340
+ float transmissionWeight = transmission * ( 1.0 - transSpecularProb );
341
+ float clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
342
+
343
+ float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
344
+ weights[ DIFF_WEIGHT ] = diffuseWeight / totalWeight;
345
+ weights[ SPEC_WEIGHT ] = specularWeight / totalWeight;
346
+ weights[ TRANS_WEIGHT ] = transmissionWeight / totalWeight;
347
+ weights[ CC_WEIGHT ] = clearcoatWeight / totalWeight;
348
+
349
+ }
350
+
351
+ float bsdfEval( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, float[ 4 ] weights, out float specularPdf, out vec3 color ) {
352
+
353
+ float diffuseWeight = weights[ DIFF_WEIGHT ];
354
+ float specularWeight = weights[ SPEC_WEIGHT ];
355
+ float transmissionWeight = weights[ TRANS_WEIGHT ];
356
+ float clearcoatWeight = weights[ CC_WEIGHT ];
357
+
358
+ float metalness = surf.metalness;
359
+ float transmission = surf.transmission;
360
+
361
+ float eta = surf.eta;
362
+ float f0 = surf.f0;
363
+ float cosTheta = min( wo.z, 1.0 );
364
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
365
+ float reflectance = schlickFresnel( cosTheta, f0 );
366
+ bool cannotRefract = eta * sinTheta > 1.0;
367
+ if ( cannotRefract ) {
368
+
369
+ reflectance = 1.0;
370
+
371
+ }
372
+
373
+ float spdf = 0.0;
374
+ float dpdf = 0.0;
375
+ float tpdf = 0.0;
376
+ float cpdf = 0.0;
377
+ color = vec3( 0.0 );
378
+
379
+ vec3 halfVector = getHalfVector( wi, wo, surf.eta );
380
+
381
+ // diffuse
382
+ if ( diffuseWeight > 0.0 && wi.z > 0.0 ) {
383
+
384
+ dpdf = diffuseEval( wo, wi, halfVector, surf, color );
385
+ color *= 1.0 - surf.transmission;
386
+
387
+ }
388
+
389
+ // ggx specular
390
+ if ( specularWeight > 0.0 && wi.z > 0.0 ) {
391
+
392
+ vec3 outColor;
393
+ spdf = specularEval( wo, wi, getHalfVector( wi, wo ), surf, outColor );
394
+ color += outColor;
395
+
396
+ }
397
+
398
+ // transmission
399
+ if ( transmissionWeight > 0.0 && wi.z < 0.0 ) {
400
+
401
+ tpdf = transmissionEval( wo, wi, halfVector, surf, color );
402
+
403
+ }
404
+
405
+ // sheen
406
+ color *= sheenAlbedoScaling( wo, wi, surf );
407
+ color += sheenColor( wo, wi, halfVector, surf );
408
+
409
+ // clearcoat
410
+ if ( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
411
+
412
+ vec3 clearcoatHalfVector = getHalfVector( clearcoatWo, clearcoatWi );
413
+ cpdf = clearcoatEval( clearcoatWo, clearcoatWi, clearcoatHalfVector, surf, color );
414
+
415
+ }
416
+
417
+ float pdf =
418
+ dpdf * diffuseWeight
419
+ + spdf * specularWeight
420
+ + tpdf * transmissionWeight
421
+ + cpdf * clearcoatWeight;
422
+
423
+ // retrieve specular rays for the shadows flag
424
+ specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
425
+
426
+ return pdf;
427
+
428
+ }
429
+
430
+ float bsdfResult( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out vec3 color ) {
431
+
432
+ float[ 4 ] pdf;
433
+ vec3 wh = getHalfVector( wo, wi, surf.eta );
434
+ getLobeWeights( wo, wi, wh, clearcoatWo, surf, pdf );
435
+
436
+ float specularPdf;
437
+ return bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, pdf, specularPdf, color );
438
+
439
+ }
440
+
441
+ SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis, mat3 clearcoatNormalBasis, mat3 clearcoatInvBasis, SurfaceRec surf ) {
442
+
443
+ // using normal and basically-reflected ray since we don't have proper half vector here
444
+ float pdf[4];
445
+ getLobeWeights( wo, wo, vec3( 0, 0, 1 ), clearcoatWo, surf, pdf );
446
+
447
+ float cdf[4];
448
+ cdf[0] = pdf[0];
449
+ cdf[1] = pdf[1] + cdf[0];
450
+ cdf[2] = pdf[2] + cdf[1];
451
+ cdf[3] = pdf[3] + cdf[2];
452
+
453
+ if( cdf[3] != 0.0 ) {
454
+
455
+ float invMaxCdf = 1.0 / cdf[3];
456
+ cdf[0] *= invMaxCdf;
457
+ cdf[1] *= invMaxCdf;
458
+ cdf[2] *= invMaxCdf;
459
+ cdf[3] *= invMaxCdf;
460
+
461
+ } else {
462
+
463
+ cdf[0] = 1.0;
464
+ cdf[1] = 0.0;
465
+ cdf[2] = 0.0;
466
+ cdf[3] = 0.0;
467
+
468
+ }
469
+
470
+ vec3 wi;
471
+ vec3 clearcoatWi;
472
+
473
+ float r = rand();
474
+ if ( r <= cdf[0] ) { // diffuse
475
+
476
+ wi = diffuseDirection( wo, surf );
477
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
478
+
479
+ } else if ( r <= cdf[1] ) { // specular
480
+
481
+ wi = specularDirection( wo, surf );
482
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
483
+
484
+ } else if ( r <= cdf[2] ) { // transmission / refraction
485
+
486
+ wi = transmissionDirection( wo, surf );
487
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
488
+
489
+ } else if ( r <= cdf[3] ) { // clearcoat
490
+
491
+ clearcoatWi = clearcoatDirection( clearcoatWo, surf );
492
+ wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
493
+
494
+ }
495
+
496
+ SampleRec result;
497
+ result.pdf = bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, pdf, result.specularPdf, result.color );
498
+ result.direction = wi;
499
+ result.clearcoatDirection = clearcoatWi;
500
+
501
+ return result;
502
+
503
+ }
504
+ `;