three-gpu-pathtracer 0.0.5 → 0.0.7

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