three-gpu-pathtracer 0.0.14 → 0.0.16

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 (76) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1004 -981
  3. package/build/index.module.js +7413 -6902
  4. package/build/index.module.js.map +1 -1
  5. package/build/index.umd.cjs +7446 -6933
  6. package/build/index.umd.cjs.map +1 -1
  7. package/package.json +73 -73
  8. package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
  9. package/src/core/MaterialReducer.js +256 -256
  10. package/src/core/PathTracingRenderer.js +346 -346
  11. package/src/core/PathTracingSceneGenerator.js +69 -69
  12. package/src/core/QuiltPathTracingRenderer.js +223 -223
  13. package/src/detectors/CompatibilityDetector.js +38 -0
  14. package/src/detectors/MaterialCompileDetector.js +50 -0
  15. package/src/detectors/PrecisionDetector.js +85 -0
  16. package/src/detectors/PrecisionMaterial.js +160 -0
  17. package/src/index.js +40 -36
  18. package/src/materials/MaterialBase.js +56 -56
  19. package/src/materials/debug/GraphMaterial.js +243 -243
  20. package/src/materials/fullscreen/AlphaDisplayMaterial.js +50 -48
  21. package/src/materials/fullscreen/BlendMaterial.js +67 -67
  22. package/src/materials/fullscreen/DenoiseMaterial.js +142 -142
  23. package/src/materials/fullscreen/GradientMapMaterial.js +82 -0
  24. package/src/materials/pathtracing/LambertPathTracingMaterial.js +296 -296
  25. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +118 -196
  26. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +177 -179
  27. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +84 -81
  28. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +93 -0
  29. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +323 -317
  30. package/src/materials/pathtracing/glsl/renderStructs.glsl.js +50 -0
  31. package/src/materials/pathtracing/glsl/traceScene.glsl.js +52 -54
  32. package/src/materials/surface/AmbientOcclusionMaterial.js +207 -207
  33. package/src/materials/surface/FogVolumeMaterial.js +23 -23
  34. package/src/objects/EquirectCamera.js +13 -13
  35. package/src/objects/PhysicalCamera.js +42 -28
  36. package/src/objects/PhysicalSpotLight.js +25 -14
  37. package/src/objects/ShapedAreaLight.js +22 -12
  38. package/src/shader/bsdf/bsdfSampling.glsl.js +499 -490
  39. package/src/shader/bsdf/fog.glsl.js +22 -23
  40. package/src/shader/bsdf/ggx.glsl.js +102 -102
  41. package/src/shader/bsdf/iridescence.glsl.js +135 -135
  42. package/src/shader/bsdf/sheen.glsl.js +98 -98
  43. package/src/shader/common/arraySamplerTexelFetch.glsl.js +25 -25
  44. package/src/shader/common/bvhAnyHit.glsl.js +76 -76
  45. package/src/shader/common/fresnel.glsl.js +98 -98
  46. package/src/shader/common/intersectShapes.glsl.js +62 -62
  47. package/src/shader/common/math.glsl.js +81 -81
  48. package/src/shader/common/utils.glsl.js +116 -116
  49. package/src/shader/rand/pcg.glsl.js +57 -57
  50. package/src/shader/rand/sobol.glsl.js +256 -256
  51. package/src/shader/sampling/equirectSampling.glsl.js +62 -62
  52. package/src/shader/sampling/lightSampling.glsl.js +223 -223
  53. package/src/shader/sampling/shapeSampling.glsl.js +86 -86
  54. package/src/shader/structs/cameraStruct.glsl.js +13 -13
  55. package/src/shader/structs/equirectStruct.glsl.js +13 -14
  56. package/src/shader/structs/fogMaterialBvh.glsl.js +62 -62
  57. package/src/shader/structs/lightsStruct.glsl.js +78 -78
  58. package/src/shader/structs/materialStruct.glsl.js +207 -207
  59. package/src/textures/GradientEquirectTexture.js +35 -35
  60. package/src/textures/ProceduralEquirectTexture.js +75 -75
  61. package/src/uniforms/AttributesTextureArray.js +35 -35
  62. package/src/uniforms/EquirectHdrInfoUniform.js +269 -277
  63. package/src/uniforms/FloatAttributeTextureArray.js +169 -169
  64. package/src/uniforms/IESProfilesTexture.js +100 -100
  65. package/src/uniforms/LightsInfoUniformStruct.js +212 -212
  66. package/src/uniforms/MaterialsTexture.js +503 -503
  67. package/src/uniforms/PhysicalCameraUniform.js +36 -36
  68. package/src/uniforms/RenderTarget2DArray.js +97 -97
  69. package/src/uniforms/utils.js +30 -30
  70. package/src/utils/BlurredEnvMapGenerator.js +116 -116
  71. package/src/utils/GeometryPreparationUtils.js +214 -214
  72. package/src/utils/IESLoader.js +325 -325
  73. package/src/utils/SobolNumberMapGenerator.js +80 -80
  74. package/src/utils/UVUnwrapper.js +101 -101
  75. package/src/utils/macroify.js +9 -9
  76. package/src/workers/PathTracingSceneWorker.js +42 -42
@@ -1,490 +1,499 @@
1
- import { ggxGLSL } from './ggx.glsl.js';
2
- import { sheenGLSL } from './sheen.glsl.js';
3
- import { iridescenceGLSL } from './iridescence.glsl.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
- f90 : Amount of light reflected at grazing angles
14
- */
15
-
16
- export const bsdfSamplingGLSL = /* glsl */`
17
-
18
- struct SurfaceRecord {
19
-
20
- // surface type
21
- bool volumeParticle;
22
-
23
- // geometry
24
- vec3 faceNormal;
25
- bool frontFace;
26
- vec3 normal;
27
-
28
- // cached properties
29
- float eta;
30
- float f0;
31
-
32
- // material
33
- float roughness;
34
- float filteredRoughness;
35
- float metalness;
36
- vec3 color;
37
- vec3 emission;
38
-
39
- // transmission
40
- float ior;
41
- float transmission;
42
- bool thinFilm;
43
- vec3 attenuationColor;
44
- float attenuationDistance;
45
-
46
- // clearcoat
47
- vec3 clearcoatNormal;
48
- float clearcoat;
49
- float clearcoatRoughness;
50
- float filteredClearcoatRoughness;
51
-
52
- // sheen
53
- float sheen;
54
- vec3 sheenColor;
55
- float sheenRoughness;
56
-
57
- // iridescence
58
- float iridescence;
59
- float iridescenceIor;
60
- float iridescenceThickness;
61
-
62
- // specular
63
- vec3 specularColor;
64
- float specularIntensity;
65
- };
66
-
67
- struct ScatterRecord {
68
- float specularPdf;
69
- float pdf;
70
-
71
- vec3 direction;
72
- vec3 clearcoatDirection;
73
-
74
- vec3 color;
75
- };
76
-
77
- ${ ggxGLSL }
78
- ${ sheenGLSL }
79
- ${ iridescenceGLSL }
80
-
81
- // diffuse
82
- float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
83
-
84
- // https://schuttejoe.github.io/post/disneybsdf/
85
- float fl = schlickFresnel( wi.z, 0.0 );
86
- float fv = schlickFresnel( wo.z, 0.0 );
87
-
88
- float metalFactor = ( 1.0 - surf.metalness );
89
- float transFactor = ( 1.0 - surf.transmission );
90
- float rr = 0.5 + 2.0 * surf.roughness * fl * fl;
91
- float retro = rr * ( fl + fv + fl * fv * ( rr - 1.0f ) );
92
- float lambert = ( 1.0f - 0.5f * fl ) * ( 1.0f - 0.5f * fv );
93
-
94
- // TODO: subsurface approx?
95
-
96
- float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
97
- color = ( 1.0 - F ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
98
- return wi.z / PI;
99
-
100
- }
101
-
102
- vec3 diffuseDirection( vec3 wo, SurfaceRecord surf ) {
103
-
104
- vec3 lightDirection = sampleSphere( sobol2( 11 ) );
105
- lightDirection.z += 1.0;
106
- lightDirection = normalize( lightDirection );
107
-
108
- return lightDirection;
109
-
110
- }
111
-
112
- // specular
113
- float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
114
-
115
- // if roughness is set to 0 then D === NaN which results in black pixels
116
- float metalness = surf.metalness;
117
- float roughness = surf.filteredRoughness;
118
-
119
- float eta = surf.eta;
120
- float f0 = surf.f0;
121
-
122
- vec3 f0Color = mix( f0 * surf.specularColor * surf.specularIntensity, surf.color, surf.metalness );
123
- vec3 f90Color = vec3( mix( surf.specularIntensity, 1.0, surf.metalness ) );
124
- vec3 F = evaluateFresnel( dot( wo, wh ), eta, f0Color, f90Color );
125
-
126
- vec3 iridescenceF = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, wh ), surf.iridescenceThickness, f0Color );
127
- F = mix( F, iridescenceF, surf.iridescence );
128
-
129
- // PDF
130
- // See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
131
- float incidentTheta = acos( wo.z );
132
- float G = ggxShadowMaskG2( wi, wo, roughness );
133
- float D = ggxDistribution( wh, roughness );
134
- float G1 = ggxShadowMaskG1( incidentTheta, roughness );
135
- float ggxPdf = D * G1 * max( 0.0, abs( dot( wo, wh ) ) ) / abs ( wo.z );
136
-
137
- color = wi.z * F * G * D / ( 4.0 * abs( wi.z * wo.z ) );
138
- return ggxPdf / ( 4.0 * dot( wo, wh ) );
139
-
140
- }
141
-
142
- vec3 specularDirection( vec3 wo, SurfaceRecord surf ) {
143
-
144
- // sample ggx vndf distribution which gives a new normal
145
- float roughness = surf.filteredRoughness;
146
- vec3 halfVector = ggxDirection(
147
- wo,
148
- vec2( roughness ),
149
- sobol2( 12 )
150
- );
151
-
152
- // apply to new ray by reflecting off the new normal
153
- return - reflect( wo, halfVector );
154
-
155
- }
156
-
157
-
158
- // transmission
159
- /*
160
- float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
161
-
162
- // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
163
-
164
- float filteredRoughness = surf.filteredRoughness;
165
- float eta = surf.eta;
166
- bool frontFace = surf.frontFace;
167
- bool thinFilm = surf.thinFilm;
168
-
169
- color = surf.transmission * surf.color;
170
-
171
- float denom = pow( eta * dot( wi, wh ) + dot( wo, wh ), 2.0 );
172
- return ggxPDF( wo, wh, filteredRoughness ) / denom;
173
-
174
- }
175
-
176
- vec3 transmissionDirection( vec3 wo, SurfaceRecord surf ) {
177
-
178
- float filteredRoughness = surf.filteredRoughness;
179
- float eta = surf.eta;
180
- bool frontFace = surf.frontFace;
181
-
182
- // sample ggx vndf distribution which gives a new normal
183
- vec3 halfVector = ggxDirection(
184
- wo,
185
- vec2( filteredRoughness ),
186
- sobol2( 13 )
187
- );
188
-
189
- vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
190
- if ( surf.thinFilm ) {
191
-
192
- lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
193
-
194
- }
195
-
196
- return normalize( lightDirection );
197
-
198
- }
199
- */
200
-
201
- // TODO: This is just using a basic cosine-weighted specular distribution with an
202
- // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
203
- float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
204
-
205
- color = surf.transmission * surf.color;
206
-
207
- // PDF
208
- float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
209
- if ( F >= 1.0 ) {
210
-
211
- return 0.0;
212
-
213
- }
214
-
215
- return 1.0 / ( 1.0 - F );
216
-
217
- }
218
-
219
- vec3 transmissionDirection( vec3 wo, SurfaceRecord surf ) {
220
-
221
- float roughness = surf.filteredRoughness;
222
- float eta = surf.eta;
223
- vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( sobol2( 13 ) ) * roughness );
224
- vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
225
-
226
- if ( surf.thinFilm ) {
227
-
228
- lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
229
-
230
- }
231
- return normalize( lightDirection );
232
-
233
- }
234
-
235
- // clearcoat
236
- float clearcoatEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
237
-
238
- float ior = 1.5;
239
- float f0 = iorRatioToF0( ior );
240
- bool frontFace = surf.frontFace;
241
- float roughness = surf.filteredClearcoatRoughness;
242
-
243
- float eta = frontFace ? 1.0 / ior : ior;
244
- float G = ggxShadowMaskG2( wi, wo, roughness );
245
- float D = ggxDistribution( wh, roughness );
246
- float F = schlickFresnel( dot( wi, wh ), f0 );
247
-
248
- float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
249
- color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
250
-
251
- // PDF
252
- // See equation (27) in http://jcgt.org/published/0003/02/03/
253
- return ggxPDF( wo, wh, roughness ) / ( 4.0 * dot( wi, wh ) );
254
-
255
- }
256
-
257
- vec3 clearcoatDirection( vec3 wo, SurfaceRecord surf ) {
258
-
259
- // sample ggx vndf distribution which gives a new normal
260
- float roughness = surf.filteredClearcoatRoughness;
261
- vec3 halfVector = ggxDirection(
262
- wo,
263
- vec2( roughness ),
264
- sobol2( 14 )
265
- );
266
-
267
- // apply to new ray by reflecting off the new normal
268
- return - reflect( wo, halfVector );
269
-
270
- }
271
-
272
- // sheen
273
- vec3 sheenColor( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf ) {
274
-
275
- float cosThetaO = saturateCos( wo.z );
276
- float cosThetaI = saturateCos( wi.z );
277
- float cosThetaH = wh.z;
278
-
279
- float D = velvetD( cosThetaH, surf.sheenRoughness );
280
- float G = velvetG( cosThetaO, cosThetaI, surf.sheenRoughness );
281
-
282
- // See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
283
- vec3 color = surf.sheenColor;
284
- color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
285
- color *= wi.z;
286
-
287
- return color;
288
-
289
- }
290
-
291
- // bsdf
292
- void getLobeWeights(
293
- vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
294
- out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight
295
- ) {
296
-
297
- float metalness = surf.metalness;
298
- float transmission = surf.transmission;
299
- float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
300
-
301
- float transSpecularProb = mix( max( 0.25, fEstimate ), 1.0, metalness );
302
- float diffSpecularProb = 0.5 + 0.5 * metalness;
303
-
304
- diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
305
- specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb;
306
- transmissionWeight = transmission * ( 1.0 - transSpecularProb );
307
- clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
308
-
309
- float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
310
- diffuseWeight /= totalWeight;
311
- specularWeight /= totalWeight;
312
- transmissionWeight /= totalWeight;
313
- clearcoatWeight /= totalWeight;
314
- }
315
-
316
- float bsdfEval(
317
- vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
318
- float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, out float specularPdf, out vec3 color
319
- ) {
320
-
321
- float metalness = surf.metalness;
322
- float transmission = surf.transmission;
323
-
324
- float spdf = 0.0;
325
- float dpdf = 0.0;
326
- float tpdf = 0.0;
327
- float cpdf = 0.0;
328
- color = vec3( 0.0 );
329
-
330
- vec3 halfVector = getHalfVector( wi, wo, surf.eta );
331
-
332
- // diffuse
333
- if ( diffuseWeight > 0.0 && wi.z > 0.0 ) {
334
-
335
- dpdf = diffuseEval( wo, wi, halfVector, surf, color );
336
- color *= 1.0 - surf.transmission;
337
-
338
- }
339
-
340
- // ggx specular
341
- if ( specularWeight > 0.0 && wi.z > 0.0 ) {
342
-
343
- vec3 outColor;
344
- spdf = specularEval( wo, wi, getHalfVector( wi, wo ), surf, outColor );
345
- color += outColor;
346
-
347
- }
348
-
349
- // transmission
350
- if ( transmissionWeight > 0.0 && wi.z < 0.0 ) {
351
-
352
- tpdf = transmissionEval( wo, wi, halfVector, surf, color );
353
-
354
- }
355
-
356
- // sheen
357
- color *= mix( 1.0, sheenAlbedoScaling( wo, wi, surf ), surf.sheen );
358
- color += sheenColor( wo, wi, halfVector, surf ) * surf.sheen;
359
-
360
- // clearcoat
361
- if ( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
362
-
363
- vec3 clearcoatHalfVector = getHalfVector( clearcoatWo, clearcoatWi );
364
- cpdf = clearcoatEval( clearcoatWo, clearcoatWi, clearcoatHalfVector, surf, color );
365
-
366
- }
367
-
368
- float pdf =
369
- dpdf * diffuseWeight
370
- + spdf * specularWeight
371
- + tpdf * transmissionWeight
372
- + cpdf * clearcoatWeight;
373
-
374
- // retrieve specular rays for the shadows flag
375
- specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
376
-
377
- return pdf;
378
-
379
- }
380
-
381
- float bsdfResult( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf, out vec3 color ) {
382
-
383
- if ( surf.volumeParticle ) {
384
-
385
- color = surf.color / ( 4.0 * PI );
386
- return 1.0 / ( 4.0 * PI );
387
-
388
- }
389
-
390
- vec3 wh = getHalfVector( wo, wi, surf.eta );
391
- float diffuseWeight;
392
- float specularWeight;
393
- float transmissionWeight;
394
- float clearcoatWeight;
395
- getLobeWeights( wo, wi, wh, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
396
-
397
- float specularPdf;
398
- return bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight, specularPdf, color );
399
-
400
- }
401
-
402
- ScatterRecord bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis, mat3 clearcoatNormalBasis, mat3 clearcoatInvBasis, SurfaceRecord surf ) {
403
-
404
- if ( surf.volumeParticle ) {
405
-
406
- vec3 wi = sampleSphere( sobol2( 16 ) );
407
- vec3 wh = normalize( wo + wi );
408
-
409
- ScatterRecord sampleRec;
410
- sampleRec.specularPdf = 0.0;
411
- sampleRec.pdf = 1.0 / ( 4.0 * PI );
412
- sampleRec.direction = wi;
413
- sampleRec.clearcoatDirection = wi;
414
- sampleRec.color = surf.color / ( 4.0 * PI );
415
- return sampleRec;
416
-
417
- }
418
-
419
- float diffuseWeight;
420
- float specularWeight;
421
- float transmissionWeight;
422
- float clearcoatWeight;
423
- // using normal and basically-reflected ray since we don't have proper half vector here
424
- getLobeWeights( wo, wo, vec3( 0, 0, 1 ), clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
425
-
426
- float pdf[4];
427
- pdf[0] = diffuseWeight;
428
- pdf[1] = specularWeight;
429
- pdf[2] = transmissionWeight;
430
- pdf[3] = clearcoatWeight;
431
-
432
- float cdf[4];
433
- cdf[0] = pdf[0];
434
- cdf[1] = pdf[1] + cdf[0];
435
- cdf[2] = pdf[2] + cdf[1];
436
- cdf[3] = pdf[3] + cdf[2];
437
-
438
- if( cdf[3] != 0.0 ) {
439
-
440
- float invMaxCdf = 1.0 / cdf[3];
441
- cdf[0] *= invMaxCdf;
442
- cdf[1] *= invMaxCdf;
443
- cdf[2] *= invMaxCdf;
444
- cdf[3] *= invMaxCdf;
445
-
446
- } else {
447
-
448
- cdf[0] = 1.0;
449
- cdf[1] = 0.0;
450
- cdf[2] = 0.0;
451
- cdf[3] = 0.0;
452
-
453
- }
454
-
455
- vec3 wi;
456
- vec3 clearcoatWi;
457
-
458
- float r = sobol( 15 );
459
- if ( r <= cdf[0] ) { // diffuse
460
-
461
- wi = diffuseDirection( wo, surf );
462
- clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
463
-
464
- } else if ( r <= cdf[1] ) { // specular
465
-
466
- wi = specularDirection( wo, surf );
467
- clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
468
-
469
- } else if ( r <= cdf[2] ) { // transmission / refraction
470
-
471
- wi = transmissionDirection( wo, surf );
472
- clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
473
-
474
- } else if ( r <= cdf[3] ) { // clearcoat
475
-
476
- clearcoatWi = clearcoatDirection( clearcoatWo, surf );
477
- wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
478
-
479
- }
480
-
481
- ScatterRecord result;
482
- result.pdf = bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight, result.specularPdf, result.color );
483
- result.direction = wi;
484
- result.clearcoatDirection = clearcoatWi;
485
-
486
- return result;
487
-
488
- }
489
-
490
- `;
1
+ import { ggxGLSL } from './ggx.glsl.js';
2
+ import { sheenGLSL } from './sheen.glsl.js';
3
+ import { iridescenceGLSL } from './iridescence.glsl.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
+ f90 : Amount of light reflected at grazing angles
14
+ */
15
+
16
+ export const bsdfSamplingGLSL = /* glsl */`
17
+
18
+ struct SurfaceRecord {
19
+
20
+ // surface type
21
+ bool volumeParticle;
22
+
23
+ // geometry
24
+ vec3 faceNormal;
25
+ bool frontFace;
26
+ vec3 normal;
27
+ mat3 normalBasis;
28
+ mat3 normalInvBasis;
29
+
30
+ // cached properties
31
+ float eta;
32
+ float f0;
33
+
34
+ // material
35
+ float roughness;
36
+ float filteredRoughness;
37
+ float metalness;
38
+ vec3 color;
39
+ vec3 emission;
40
+
41
+ // transmission
42
+ float ior;
43
+ float transmission;
44
+ bool thinFilm;
45
+ vec3 attenuationColor;
46
+ float attenuationDistance;
47
+
48
+ // clearcoat
49
+ vec3 clearcoatNormal;
50
+ mat3 clearcoatBasis;
51
+ mat3 clearcoatInvBasis;
52
+ float clearcoat;
53
+ float clearcoatRoughness;
54
+ float filteredClearcoatRoughness;
55
+
56
+ // sheen
57
+ float sheen;
58
+ vec3 sheenColor;
59
+ float sheenRoughness;
60
+
61
+ // iridescence
62
+ float iridescence;
63
+ float iridescenceIor;
64
+ float iridescenceThickness;
65
+
66
+ // specular
67
+ vec3 specularColor;
68
+ float specularIntensity;
69
+ };
70
+
71
+ struct ScatterRecord {
72
+ float specularPdf;
73
+ float pdf;
74
+ vec3 direction;
75
+ vec3 color;
76
+ };
77
+
78
+ ${ ggxGLSL }
79
+ ${ sheenGLSL }
80
+ ${ iridescenceGLSL }
81
+
82
+ // diffuse
83
+ float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
84
+
85
+ // https://schuttejoe.github.io/post/disneybsdf/
86
+ float fl = schlickFresnel( wi.z, 0.0 );
87
+ float fv = schlickFresnel( wo.z, 0.0 );
88
+
89
+ float metalFactor = ( 1.0 - surf.metalness );
90
+ float transFactor = ( 1.0 - surf.transmission );
91
+ float rr = 0.5 + 2.0 * surf.roughness * fl * fl;
92
+ float retro = rr * ( fl + fv + fl * fv * ( rr - 1.0f ) );
93
+ float lambert = ( 1.0f - 0.5f * fl ) * ( 1.0f - 0.5f * fv );
94
+
95
+ // TODO: subsurface approx?
96
+
97
+ float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
98
+ color = ( 1.0 - F ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
99
+ return wi.z / PI;
100
+
101
+ }
102
+
103
+ vec3 diffuseDirection( vec3 wo, SurfaceRecord surf ) {
104
+
105
+ vec3 lightDirection = sampleSphere( sobol2( 11 ) );
106
+ lightDirection.z += 1.0;
107
+ lightDirection = normalize( lightDirection );
108
+
109
+ return lightDirection;
110
+
111
+ }
112
+
113
+ // specular
114
+ float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
115
+
116
+ // if roughness is set to 0 then D === NaN which results in black pixels
117
+ float metalness = surf.metalness;
118
+ float roughness = surf.filteredRoughness;
119
+
120
+ float eta = surf.eta;
121
+ float f0 = surf.f0;
122
+
123
+ vec3 f0Color = mix( f0 * surf.specularColor * surf.specularIntensity, surf.color, surf.metalness );
124
+ vec3 f90Color = vec3( mix( surf.specularIntensity, 1.0, surf.metalness ) );
125
+ vec3 F = evaluateFresnel( dot( wo, wh ), eta, f0Color, f90Color );
126
+
127
+ vec3 iridescenceF = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, wh ), surf.iridescenceThickness, f0Color );
128
+ F = mix( F, iridescenceF, surf.iridescence );
129
+
130
+ // PDF
131
+ // See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
132
+ float incidentTheta = acos( wo.z );
133
+ float G = ggxShadowMaskG2( wi, wo, roughness );
134
+ float D = ggxDistribution( wh, roughness );
135
+ float G1 = ggxShadowMaskG1( incidentTheta, roughness );
136
+ float ggxPdf = D * G1 * max( 0.0, abs( dot( wo, wh ) ) ) / abs ( wo.z );
137
+
138
+ color = wi.z * F * G * D / ( 4.0 * abs( wi.z * wo.z ) );
139
+ return ggxPdf / ( 4.0 * dot( wo, wh ) );
140
+
141
+ }
142
+
143
+ vec3 specularDirection( vec3 wo, SurfaceRecord surf ) {
144
+
145
+ // sample ggx vndf distribution which gives a new normal
146
+ float roughness = surf.filteredRoughness;
147
+ vec3 halfVector = ggxDirection(
148
+ wo,
149
+ vec2( roughness ),
150
+ sobol2( 12 )
151
+ );
152
+
153
+ // apply to new ray by reflecting off the new normal
154
+ return - reflect( wo, halfVector );
155
+
156
+ }
157
+
158
+
159
+ // transmission
160
+ /*
161
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
162
+
163
+ // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
164
+
165
+ float filteredRoughness = surf.filteredRoughness;
166
+ float eta = surf.eta;
167
+ bool frontFace = surf.frontFace;
168
+ bool thinFilm = surf.thinFilm;
169
+
170
+ color = surf.transmission * surf.color;
171
+
172
+ float denom = pow( eta * dot( wi, wh ) + dot( wo, wh ), 2.0 );
173
+ return ggxPDF( wo, wh, filteredRoughness ) / denom;
174
+
175
+ }
176
+
177
+ vec3 transmissionDirection( vec3 wo, SurfaceRecord surf ) {
178
+
179
+ float filteredRoughness = surf.filteredRoughness;
180
+ float eta = surf.eta;
181
+ bool frontFace = surf.frontFace;
182
+
183
+ // sample ggx vndf distribution which gives a new normal
184
+ vec3 halfVector = ggxDirection(
185
+ wo,
186
+ vec2( filteredRoughness ),
187
+ sobol2( 13 )
188
+ );
189
+
190
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
191
+ if ( surf.thinFilm ) {
192
+
193
+ lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
194
+
195
+ }
196
+
197
+ return normalize( lightDirection );
198
+
199
+ }
200
+ */
201
+
202
+ // TODO: This is just using a basic cosine-weighted specular distribution with an
203
+ // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
204
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
205
+
206
+ color = surf.transmission * surf.color;
207
+
208
+ // PDF
209
+ float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
210
+ if ( F >= 1.0 ) {
211
+
212
+ return 0.0;
213
+
214
+ }
215
+
216
+ return 1.0 / ( 1.0 - F );
217
+
218
+ }
219
+
220
+ vec3 transmissionDirection( vec3 wo, SurfaceRecord surf ) {
221
+
222
+ float roughness = surf.filteredRoughness;
223
+ float eta = surf.eta;
224
+ vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( sobol2( 13 ) ) * roughness );
225
+ vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
226
+
227
+ if ( surf.thinFilm ) {
228
+
229
+ lightDirection = - refract( normalize( - lightDirection ), - vec3( 0.0, 0.0, 1.0 ), 1.0 / eta );
230
+
231
+ }
232
+ return normalize( lightDirection );
233
+
234
+ }
235
+
236
+ // clearcoat
237
+ float clearcoatEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
238
+
239
+ float ior = 1.5;
240
+ float f0 = iorRatioToF0( ior );
241
+ bool frontFace = surf.frontFace;
242
+ float roughness = surf.filteredClearcoatRoughness;
243
+
244
+ float eta = frontFace ? 1.0 / ior : ior;
245
+ float G = ggxShadowMaskG2( wi, wo, roughness );
246
+ float D = ggxDistribution( wh, roughness );
247
+ float F = schlickFresnel( dot( wi, wh ), f0 );
248
+
249
+ float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
250
+ color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
251
+
252
+ // PDF
253
+ // See equation (27) in http://jcgt.org/published/0003/02/03/
254
+ return ggxPDF( wo, wh, roughness ) / ( 4.0 * dot( wi, wh ) );
255
+
256
+ }
257
+
258
+ vec3 clearcoatDirection( vec3 wo, SurfaceRecord surf ) {
259
+
260
+ // sample ggx vndf distribution which gives a new normal
261
+ float roughness = surf.filteredClearcoatRoughness;
262
+ vec3 halfVector = ggxDirection(
263
+ wo,
264
+ vec2( roughness ),
265
+ sobol2( 14 )
266
+ );
267
+
268
+ // apply to new ray by reflecting off the new normal
269
+ return - reflect( wo, halfVector );
270
+
271
+ }
272
+
273
+ // sheen
274
+ vec3 sheenColor( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf ) {
275
+
276
+ float cosThetaO = saturateCos( wo.z );
277
+ float cosThetaI = saturateCos( wi.z );
278
+ float cosThetaH = wh.z;
279
+
280
+ float D = velvetD( cosThetaH, surf.sheenRoughness );
281
+ float G = velvetG( cosThetaO, cosThetaI, surf.sheenRoughness );
282
+
283
+ // See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
284
+ vec3 color = surf.sheenColor;
285
+ color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
286
+ color *= wi.z;
287
+
288
+ return color;
289
+
290
+ }
291
+
292
+ // bsdf
293
+ void getLobeWeights(
294
+ vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
295
+ out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight
296
+ ) {
297
+
298
+ float metalness = surf.metalness;
299
+ float transmission = surf.transmission;
300
+ float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
301
+
302
+ float transSpecularProb = mix( max( 0.25, fEstimate ), 1.0, metalness );
303
+ float diffSpecularProb = 0.5 + 0.5 * metalness;
304
+
305
+ diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
306
+ specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb;
307
+ transmissionWeight = transmission * ( 1.0 - transSpecularProb );
308
+ clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
309
+
310
+ float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
311
+ diffuseWeight /= totalWeight;
312
+ specularWeight /= totalWeight;
313
+ transmissionWeight /= totalWeight;
314
+ clearcoatWeight /= totalWeight;
315
+ }
316
+
317
+ float bsdfEval(
318
+ vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
319
+ float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, out float specularPdf, out vec3 color
320
+ ) {
321
+
322
+ float metalness = surf.metalness;
323
+ float transmission = surf.transmission;
324
+
325
+ float spdf = 0.0;
326
+ float dpdf = 0.0;
327
+ float tpdf = 0.0;
328
+ float cpdf = 0.0;
329
+ color = vec3( 0.0 );
330
+
331
+ vec3 halfVector = getHalfVector( wi, wo, surf.eta );
332
+
333
+ // diffuse
334
+ if ( diffuseWeight > 0.0 && wi.z > 0.0 ) {
335
+
336
+ dpdf = diffuseEval( wo, wi, halfVector, surf, color );
337
+ color *= 1.0 - surf.transmission;
338
+
339
+ }
340
+
341
+ // ggx specular
342
+ if ( specularWeight > 0.0 && wi.z > 0.0 ) {
343
+
344
+ vec3 outColor;
345
+ spdf = specularEval( wo, wi, getHalfVector( wi, wo ), surf, outColor );
346
+ color += outColor;
347
+
348
+ }
349
+
350
+ // transmission
351
+ if ( transmissionWeight > 0.0 && wi.z < 0.0 ) {
352
+
353
+ tpdf = transmissionEval( wo, wi, halfVector, surf, color );
354
+
355
+ }
356
+
357
+ // sheen
358
+ color *= mix( 1.0, sheenAlbedoScaling( wo, wi, surf ), surf.sheen );
359
+ color += sheenColor( wo, wi, halfVector, surf ) * surf.sheen;
360
+
361
+ // clearcoat
362
+ if ( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
363
+
364
+ vec3 clearcoatHalfVector = getHalfVector( clearcoatWo, clearcoatWi );
365
+ cpdf = clearcoatEval( clearcoatWo, clearcoatWi, clearcoatHalfVector, surf, color );
366
+
367
+ }
368
+
369
+ float pdf =
370
+ dpdf * diffuseWeight
371
+ + spdf * specularWeight
372
+ + tpdf * transmissionWeight
373
+ + cpdf * clearcoatWeight;
374
+
375
+ // retrieve specular rays for the shadows flag
376
+ specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
377
+
378
+ return pdf;
379
+
380
+ }
381
+
382
+ float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, out vec3 color ) {
383
+
384
+ if ( surf.volumeParticle ) {
385
+
386
+ color = surf.color / ( 4.0 * PI );
387
+ return 1.0 / ( 4.0 * PI );
388
+
389
+ }
390
+
391
+ vec3 wo = normalize( surf.normalInvBasis * worldWo );
392
+ vec3 wi = normalize( surf.normalInvBasis * worldWi );
393
+
394
+ vec3 clearcoatWo = normalize( surf.clearcoatInvBasis * worldWo );
395
+ vec3 clearcoatWi = normalize( surf.clearcoatInvBasis * worldWi );
396
+
397
+ vec3 wh = getHalfVector( wo, wi, surf.eta );
398
+ float diffuseWeight;
399
+ float specularWeight;
400
+ float transmissionWeight;
401
+ float clearcoatWeight;
402
+ getLobeWeights( wo, wi, wh, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
403
+
404
+ float specularPdf;
405
+ return bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight, specularPdf, color );
406
+
407
+ }
408
+
409
+ ScatterRecord bsdfSample( vec3 worldWo, SurfaceRecord surf ) {
410
+
411
+ if ( surf.volumeParticle ) {
412
+
413
+ ScatterRecord sampleRec;
414
+ sampleRec.specularPdf = 0.0;
415
+ sampleRec.pdf = 1.0 / ( 4.0 * PI );
416
+ sampleRec.direction = sampleSphere( sobol2( 16 ) );
417
+ sampleRec.color = surf.color / ( 4.0 * PI );
418
+ return sampleRec;
419
+
420
+ }
421
+
422
+ vec3 wo = normalize( surf.normalInvBasis * worldWo );
423
+ vec3 clearcoatWo = normalize( surf.clearcoatInvBasis * worldWo );
424
+ mat3 normalBasis = surf.normalBasis;
425
+ mat3 invBasis = surf.normalInvBasis;
426
+ mat3 clearcoatNormalBasis = surf.clearcoatBasis;
427
+ mat3 clearcoatInvBasis = surf.clearcoatInvBasis;
428
+
429
+ float diffuseWeight;
430
+ float specularWeight;
431
+ float transmissionWeight;
432
+ float clearcoatWeight;
433
+ // using normal and basically-reflected ray since we don't have proper half vector here
434
+ getLobeWeights( wo, wo, vec3( 0, 0, 1 ), clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
435
+
436
+ float pdf[4];
437
+ pdf[0] = diffuseWeight;
438
+ pdf[1] = specularWeight;
439
+ pdf[2] = transmissionWeight;
440
+ pdf[3] = clearcoatWeight;
441
+
442
+ float cdf[4];
443
+ cdf[0] = pdf[0];
444
+ cdf[1] = pdf[1] + cdf[0];
445
+ cdf[2] = pdf[2] + cdf[1];
446
+ cdf[3] = pdf[3] + cdf[2];
447
+
448
+ if( cdf[3] != 0.0 ) {
449
+
450
+ float invMaxCdf = 1.0 / cdf[3];
451
+ cdf[0] *= invMaxCdf;
452
+ cdf[1] *= invMaxCdf;
453
+ cdf[2] *= invMaxCdf;
454
+ cdf[3] *= invMaxCdf;
455
+
456
+ } else {
457
+
458
+ cdf[0] = 1.0;
459
+ cdf[1] = 0.0;
460
+ cdf[2] = 0.0;
461
+ cdf[3] = 0.0;
462
+
463
+ }
464
+
465
+ vec3 wi;
466
+ vec3 clearcoatWi;
467
+
468
+ float r = sobol( 15 );
469
+ if ( r <= cdf[0] ) { // diffuse
470
+
471
+ wi = diffuseDirection( wo, surf );
472
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
473
+
474
+ } else if ( r <= cdf[1] ) { // specular
475
+
476
+ wi = specularDirection( wo, surf );
477
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
478
+
479
+ } else if ( r <= cdf[2] ) { // transmission / refraction
480
+
481
+ wi = transmissionDirection( wo, surf );
482
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
483
+
484
+ } else if ( r <= cdf[3] ) { // clearcoat
485
+
486
+ clearcoatWi = clearcoatDirection( clearcoatWo, surf );
487
+ wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
488
+
489
+ }
490
+
491
+ ScatterRecord result;
492
+ result.pdf = bsdfEval( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight, result.specularPdf, result.color );
493
+ result.direction = normalize( surf.normalBasis * wi );
494
+
495
+ return result;
496
+
497
+ }
498
+
499
+ `;