three-gpu-pathtracer 0.0.12 → 0.0.14

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 (58) hide show
  1. package/README.md +102 -7
  2. package/build/index.module.js +2891 -2065
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +2886 -2062
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +2 -1
  7. package/src/core/PathTracingRenderer.js +87 -16
  8. package/src/core/PathTracingSceneGenerator.js +1 -1
  9. package/src/core/QuiltPathTracingRenderer.js +223 -0
  10. package/src/index.js +5 -8
  11. package/src/materials/{GraphMaterial.js → debug/GraphMaterial.js} +1 -1
  12. package/src/materials/{AlphaDisplayMaterial.js → fullscreen/AlphaDisplayMaterial.js} +1 -1
  13. package/src/materials/{BlendMaterial.js → fullscreen/BlendMaterial.js} +1 -1
  14. package/src/materials/{DenoiseMaterial.js → fullscreen/DenoiseMaterial.js} +1 -1
  15. package/src/materials/{LambertPathTracingMaterial.js → pathtracing/LambertPathTracingMaterial.js} +18 -7
  16. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +635 -0
  17. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +179 -0
  18. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +81 -0
  19. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +317 -0
  20. package/src/materials/pathtracing/glsl/traceScene.glsl.js +54 -0
  21. package/src/materials/{AmbientOcclusionMaterial.js → surface/AmbientOcclusionMaterial.js} +16 -8
  22. package/src/materials/surface/FogVolumeMaterial.js +23 -0
  23. package/src/shader/bsdf/bsdfSampling.glsl.js +490 -0
  24. package/src/shader/bsdf/fog.glsl.js +23 -0
  25. package/src/shader/bsdf/ggx.glsl.js +102 -0
  26. package/src/shader/bsdf/iridescence.glsl.js +135 -0
  27. package/src/shader/bsdf/sheen.glsl.js +98 -0
  28. package/src/shader/{shaderLayerTexelFetchFunctions.js → common/arraySamplerTexelFetch.glsl.js} +1 -1
  29. package/src/shader/common/bvhAnyHit.glsl.js +76 -0
  30. package/src/shader/common/fresnel.glsl.js +98 -0
  31. package/src/shader/common/intersectShapes.glsl.js +62 -0
  32. package/src/shader/common/math.glsl.js +81 -0
  33. package/src/shader/common/utils.glsl.js +116 -0
  34. package/src/shader/{shaderRandFunctions.js → rand/pcg.glsl.js} +1 -1
  35. package/src/shader/{shaderSobolSampling.js → rand/sobol.glsl.js} +3 -3
  36. package/src/shader/sampling/equirectSampling.glsl.js +62 -0
  37. package/src/shader/sampling/lightSampling.glsl.js +223 -0
  38. package/src/shader/sampling/shapeSampling.glsl.js +86 -0
  39. package/src/shader/structs/cameraStruct.glsl.js +13 -0
  40. package/src/shader/structs/equirectStruct.glsl.js +14 -0
  41. package/src/shader/structs/fogMaterialBvh.glsl.js +62 -0
  42. package/src/shader/structs/lightsStruct.glsl.js +78 -0
  43. package/src/shader/{shaderStructs.js → structs/materialStruct.glsl.js} +5 -123
  44. package/src/uniforms/EquirectHdrInfoUniform.js +29 -11
  45. package/src/uniforms/LightsInfoUniformStruct.js +9 -4
  46. package/src/uniforms/MaterialsTexture.js +80 -3
  47. package/src/utils/BlurredEnvMapGenerator.js +2 -2
  48. package/src/utils/SobolNumberMapGenerator.js +3 -3
  49. package/src/utils/macroify.js +9 -0
  50. package/src/materials/PhysicalPathTracingMaterial.js +0 -982
  51. package/src/shader/shaderBvhAnyHit.js +0 -76
  52. package/src/shader/shaderEnvMapSampling.js +0 -58
  53. package/src/shader/shaderGGXFunctions.js +0 -100
  54. package/src/shader/shaderIridescenceFunctions.js +0 -130
  55. package/src/shader/shaderLightSampling.js +0 -229
  56. package/src/shader/shaderMaterialSampling.js +0 -506
  57. package/src/shader/shaderSheenFunctions.js +0 -98
  58. package/src/shader/shaderUtils.js +0 -361
@@ -0,0 +1,635 @@
1
+ import { Matrix4, Vector2 } from 'three';
2
+ import { MaterialBase } from '../MaterialBase.js';
3
+ import {
4
+ MeshBVHUniformStruct, UIntVertexAttributeTexture,
5
+ shaderStructs, shaderIntersectFunction,
6
+ } from 'three-mesh-bvh';
7
+
8
+ // uniforms
9
+ import { PhysicalCameraUniform } from '../../uniforms/PhysicalCameraUniform.js';
10
+ import { EquirectHdrInfoUniform } from '../../uniforms/EquirectHdrInfoUniform.js';
11
+ import { LightsInfoUniformStruct } from '../../uniforms/LightsInfoUniformStruct.js';
12
+ import { IESProfilesTexture } from '../../uniforms/IESProfilesTexture.js';
13
+ import { AttributesTextureArray } from '../../uniforms/AttributesTextureArray.js';
14
+ import { MaterialsTexture } from '../../uniforms/MaterialsTexture.js';
15
+ import { RenderTarget2DArray } from '../../uniforms/RenderTarget2DArray.js';
16
+
17
+ // glsl
18
+ import { cameraStructGLSL } from '../../shader/structs/cameraStruct.glsl.js';
19
+ import { equirectStructGLSL } from '../../shader/structs/equirectStruct.glsl.js';
20
+ import { lightsStructGLSL } from '../../shader/structs/lightsStruct.glsl.js';
21
+ import { materialStructGLSL } from '../../shader/structs/materialStruct.glsl.js';
22
+ import { fogMaterialBvhGLSL } from '../../shader/structs/fogMaterialBvh.glsl.js';
23
+
24
+ // material sampling
25
+ import { bsdfSamplingGLSL } from '../../shader/bsdf/bsdfSampling.glsl.js';
26
+ import { fogGLSL } from '../../shader/bsdf/fog.glsl.js';
27
+
28
+ // sampling
29
+ import { equirectSamplingGLSL } from '../../shader/sampling/equirectSampling.glsl.js';
30
+ import { lightSamplingGLSL } from '../../shader/sampling/lightSampling.glsl.js';
31
+ import { shapeSamplingGLSL } from '../../shader/sampling/shapeSampling.glsl.js';
32
+
33
+ // common glsl
34
+ import { intersectShapesGLSL } from '../../shader/common/intersectShapes.glsl';
35
+ import { mathGLSL } from '../../shader/common/math.glsl.js';
36
+ import { utilsGLSL } from '../../shader/common/utils.glsl.js';
37
+ import { fresnelGLSL } from '../../shader/common/fresnel.glsl.js';
38
+ import { arraySamplerTexelFetchGLSL } from '../../shader/common/arraySamplerTexelFetch.glsl.js';
39
+
40
+ // random glsl
41
+ import { pcgGLSL } from '../../shader/rand/pcg.glsl.js';
42
+ import { sobolCommonGLSL, sobolSamplingGLSL } from '../../shader/rand/sobol.glsl.js';
43
+
44
+ // path tracer utils
45
+ import { cameraUtilsGLSL } from './glsl/cameraUtils.glsl.js';
46
+ import { attenuateHitGLSL } from './glsl/attenuateHit.glsl.js';
47
+ import { traceSceneGLSL } from './glsl/traceScene.glsl.js';
48
+ import { getSurfaceRecordGLSL } from './glsl/getSurfaceRecord.glsl.js';
49
+
50
+ export class PhysicalPathTracingMaterial extends MaterialBase {
51
+
52
+ onBeforeRender() {
53
+
54
+ this.setDefine( 'FEATURE_DOF', this.physicalCamera.bokehSize === 0 ? 0 : 1 );
55
+ this.setDefine( 'FEATURE_BACKGROUND_MAP', this.backgroundMap ? 1 : 0 );
56
+ this.setDefine( 'FEATURE_FOG', this.materials.features.isUsed( 'FOG' ) ? 1 : 0 );
57
+
58
+ }
59
+
60
+ constructor( parameters ) {
61
+
62
+ super( {
63
+
64
+ transparent: true,
65
+ depthWrite: false,
66
+
67
+ defines: {
68
+ FEATURE_MIS: 1,
69
+ FEATURE_RUSSIAN_ROULETTE: 1,
70
+ FEATURE_DOF: 1,
71
+ FEATURE_BACKGROUND_MAP: 0,
72
+ FEATURE_FOG: 1,
73
+ // 0 = Perspective
74
+ // 1 = Orthographic
75
+ // 2 = Equirectangular
76
+ CAMERA_TYPE: 0,
77
+
78
+ ATTR_NORMAL: 0,
79
+ ATTR_TANGENT: 1,
80
+ ATTR_UV: 2,
81
+ ATTR_COLOR: 3,
82
+ },
83
+
84
+ uniforms: {
85
+ resolution: { value: new Vector2() },
86
+
87
+ bounces: { value: 10 },
88
+ transmissiveBounces: { value: 10 },
89
+ physicalCamera: { value: new PhysicalCameraUniform() },
90
+
91
+ bvh: { value: new MeshBVHUniformStruct() },
92
+ attributesArray: { value: new AttributesTextureArray() },
93
+ materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
94
+ materials: { value: new MaterialsTexture() },
95
+ textures: { value: new RenderTarget2DArray().texture },
96
+ lights: { value: new LightsInfoUniformStruct() },
97
+ iesProfiles: { value: new IESProfilesTexture().texture },
98
+ cameraWorldMatrix: { value: new Matrix4() },
99
+ invProjectionMatrix: { value: new Matrix4() },
100
+ backgroundBlur: { value: 0.0 },
101
+ environmentIntensity: { value: 1.0 },
102
+ environmentRotation: { value: new Matrix4() },
103
+ envMapInfo: { value: new EquirectHdrInfoUniform() },
104
+ backgroundMap: { value: null },
105
+
106
+ seed: { value: 0 },
107
+ opacity: { value: 1 },
108
+ filterGlossyFactor: { value: 0.0 },
109
+
110
+ backgroundAlpha: { value: 1.0 },
111
+ sobolTexture: { value: null },
112
+ },
113
+
114
+ vertexShader: /* glsl */`
115
+
116
+ varying vec2 vUv;
117
+ void main() {
118
+
119
+ vec4 mvPosition = vec4( position, 1.0 );
120
+ mvPosition = modelViewMatrix * mvPosition;
121
+ gl_Position = projectionMatrix * mvPosition;
122
+
123
+ vUv = uv;
124
+
125
+ }
126
+
127
+ `,
128
+
129
+ fragmentShader: /* glsl */`
130
+ #define RAY_OFFSET 1e-4
131
+ #define INFINITY 1e20
132
+
133
+ precision highp isampler2D;
134
+ precision highp usampler2D;
135
+ precision highp sampler2DArray;
136
+ vec4 envMapTexelToLinear( vec4 a ) { return a; }
137
+ #include <common>
138
+
139
+ // bvh intersection
140
+ ${ shaderStructs }
141
+ ${ shaderIntersectFunction }
142
+
143
+ // random
144
+ ${ pcgGLSL }
145
+ ${ sobolCommonGLSL }
146
+ ${ sobolSamplingGLSL }
147
+
148
+ // common
149
+ ${ arraySamplerTexelFetchGLSL }
150
+ ${ fresnelGLSL }
151
+ ${ utilsGLSL }
152
+ ${ mathGLSL }
153
+ ${ intersectShapesGLSL }
154
+
155
+ // uniform structs
156
+ ${ cameraStructGLSL }
157
+ ${ lightsStructGLSL }
158
+ ${ equirectStructGLSL }
159
+ ${ materialStructGLSL }
160
+ ${ fogMaterialBvhGLSL }
161
+
162
+ // sampling
163
+ ${ shapeSamplingGLSL }
164
+ ${ bsdfSamplingGLSL }
165
+ ${ equirectSamplingGLSL }
166
+ ${ lightSamplingGLSL }
167
+ ${ fogGLSL }
168
+
169
+ // environment
170
+ uniform EquirectHdrInfo envMapInfo;
171
+ uniform mat4 environmentRotation;
172
+ uniform float environmentIntensity;
173
+
174
+ // lighting
175
+ uniform sampler2DArray iesProfiles;
176
+ uniform LightsInfo lights;
177
+
178
+ // background
179
+ uniform float backgroundBlur;
180
+ uniform float backgroundAlpha;
181
+ #if FEATURE_BACKGROUND_MAP
182
+
183
+ uniform sampler2D backgroundMap;
184
+
185
+ #endif
186
+
187
+ // camera
188
+ uniform mat4 cameraWorldMatrix;
189
+ uniform mat4 invProjectionMatrix;
190
+ #if FEATURE_DOF
191
+
192
+ uniform PhysicalCamera physicalCamera;
193
+
194
+ #endif
195
+
196
+ // geometry
197
+ uniform sampler2DArray attributesArray;
198
+ uniform usampler2D materialIndexAttribute;
199
+ uniform sampler2D materials;
200
+ uniform sampler2DArray textures;
201
+ uniform BVH bvh;
202
+
203
+ // path tracer
204
+ uniform int bounces;
205
+ uniform int transmissiveBounces;
206
+ uniform float filterGlossyFactor;
207
+ uniform int seed;
208
+
209
+ // image
210
+ uniform vec2 resolution;
211
+ uniform float opacity;
212
+
213
+ varying vec2 vUv;
214
+
215
+ ${ cameraUtilsGLSL }
216
+ ${ traceSceneGLSL }
217
+ ${ attenuateHitGLSL }
218
+
219
+ float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
220
+
221
+ return clamp(
222
+ max(
223
+ roughness,
224
+ accumulatedRoughness * filterGlossyFactor * 5.0 ),
225
+ 0.0,
226
+ 1.0
227
+ );
228
+
229
+ }
230
+
231
+ vec3 sampleBackground( vec3 direction, vec2 uv ) {
232
+
233
+ vec3 sampleDir = normalize( direction + sampleHemisphere( direction, uv ) * 0.5 * backgroundBlur );
234
+
235
+ #if FEATURE_BACKGROUND_MAP
236
+
237
+ return sampleEquirectColor( backgroundMap, sampleDir );
238
+
239
+ #else
240
+
241
+ return environmentIntensity * sampleEquirectColor( envMapInfo.map, sampleDir );
242
+
243
+ #endif
244
+
245
+ }
246
+
247
+ ${ getSurfaceRecordGLSL }
248
+
249
+ void main() {
250
+
251
+ // init
252
+ rng_initialize( gl_FragCoord.xy, seed );
253
+ sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
254
+ sobolPathIndex = uint( seed );
255
+
256
+ // get camera ray
257
+ vec3 rayDirection, rayOrigin;
258
+ getCameraRay( rayDirection, rayOrigin );
259
+
260
+ // inverse environment rotation
261
+ mat3 envRotation3x3 = mat3( environmentRotation );
262
+ mat3 invEnvRotation3x3 = inverse( envRotation3x3 );
263
+ float lightsDenom = environmentIntensity == 0.0 && lights.count != 0u ? float( lights.count ) : float( lights.count + 1u );
264
+
265
+ // final color
266
+ gl_FragColor = vec4( 0.0 );
267
+ gl_FragColor.a = 1.0;
268
+
269
+ // hit results
270
+ uvec4 faceIndices = uvec4( 0u );
271
+ vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
272
+ vec3 barycoord = vec3( 0.0 );
273
+ float side = 1.0;
274
+ float dist = 0.0;
275
+
276
+ // path tracing state
277
+ float accumulatedRoughness = 0.0;
278
+ bool transmissiveRay = true;
279
+ bool isShadowRay = false;
280
+ int transmissiveTraversals = transmissiveBounces;
281
+ vec3 throughputColor = vec3( 1.0 );
282
+ ScatterRecord sampleRec;
283
+ int i;
284
+
285
+ Material fogMaterial;
286
+ #if FEATURE_FOG
287
+
288
+ fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
289
+ bvh, rayOrigin, - rayDirection,
290
+ materialIndexAttribute, materials,
291
+ fogMaterial
292
+ );
293
+
294
+ #endif
295
+
296
+ for ( i = 0; i < bounces; i ++ ) {
297
+
298
+ sobolBounceIndex ++;
299
+
300
+ bool firstRay = i == 0 && transmissiveTraversals == transmissiveBounces;
301
+
302
+ LightSampleRecord lightSampleRec;
303
+ int hitType = traceScene(
304
+ rayOrigin, rayDirection,
305
+ bvh, lights, fogMaterial,
306
+ faceIndices, faceNormal, barycoord, side, dist,
307
+ lightSampleRec
308
+ );
309
+
310
+ if ( hitType == LIGHT_HIT ) {
311
+
312
+ if ( firstRay || transmissiveRay ) {
313
+
314
+ gl_FragColor.rgb += lightSampleRec.emission * throughputColor;
315
+
316
+ } else {
317
+
318
+ #if FEATURE_MIS
319
+
320
+ // NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
321
+ if ( lightSampleRec.type == SPOT_LIGHT_TYPE || lightSampleRec.type == DIR_LIGHT_TYPE || lightSampleRec.type == POINT_LIGHT_TYPE ) {
322
+
323
+ gl_FragColor.rgb += lightSampleRec.emission * throughputColor;
324
+
325
+ } else {
326
+
327
+ // weight the contribution
328
+ float misWeight = misHeuristic( sampleRec.pdf, lightSampleRec.pdf / lightsDenom );
329
+ gl_FragColor.rgb += lightSampleRec.emission * throughputColor * misWeight;
330
+
331
+ }
332
+
333
+ #else
334
+
335
+ gl_FragColor.rgb += lightSampleRec.emission * throughputColor;
336
+
337
+ #endif
338
+
339
+ }
340
+ break;
341
+
342
+ } else if ( hitType == NO_HIT ) {
343
+
344
+ if ( firstRay || transmissiveRay ) {
345
+
346
+ gl_FragColor.rgb += sampleBackground( envRotation3x3 * rayDirection, sobol2( 2 ) ) * throughputColor;
347
+ gl_FragColor.a = backgroundAlpha;
348
+
349
+ } else {
350
+
351
+ #if FEATURE_MIS
352
+
353
+ // get the PDF of the hit envmap point
354
+ vec3 envColor;
355
+ float envPdf = sampleEquirect( envMapInfo, envRotation3x3 * rayDirection, envColor );
356
+ envPdf /= lightsDenom;
357
+
358
+ // and weight the contribution
359
+ float misWeight = misHeuristic( sampleRec.pdf, envPdf );
360
+ gl_FragColor.rgb += environmentIntensity * envColor * throughputColor * misWeight;
361
+
362
+ #else
363
+
364
+ gl_FragColor.rgb +=
365
+ environmentIntensity *
366
+ sampleEquirectColor( envMapInfo.map, envRotation3x3 * rayDirection ) *
367
+ throughputColor;
368
+
369
+ #endif
370
+
371
+ }
372
+ break;
373
+
374
+ }
375
+
376
+ uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
377
+ Material material = readMaterialInfo( materials, materialIndex );
378
+
379
+ #if FEATURE_FOG
380
+
381
+ if ( hitType == FOG_HIT ) {
382
+
383
+ material = fogMaterial;
384
+ accumulatedRoughness += 0.2;
385
+
386
+ } else if ( material.fogVolume ) {
387
+
388
+ fogMaterial = material;
389
+ fogMaterial.fogVolume = side == 1.0;
390
+
391
+ rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
392
+
393
+ i -= sign( transmissiveTraversals );
394
+ transmissiveTraversals -= sign( transmissiveTraversals );
395
+ continue;
396
+
397
+ }
398
+
399
+ #endif
400
+
401
+ if ( material.matte && firstRay ) {
402
+
403
+ gl_FragColor = vec4( 0.0 );
404
+ break;
405
+
406
+ }
407
+
408
+ // if we've determined that this is a shadow ray and we've hit an item with no shadow casting
409
+ // then skip it
410
+ if ( ! material.castShadow && isShadowRay ) {
411
+
412
+ rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
413
+ continue;
414
+
415
+ }
416
+
417
+ SurfaceRecord surf;
418
+ if (
419
+ getSurfaceRecord(
420
+ material, attributesArray, side, barycoord, faceIndices,
421
+ faceNormal, accumulatedRoughness,
422
+ surf
423
+ ) == SKIP_SURFACE
424
+ ) {
425
+
426
+ // only allow a limited number of transparency discards otherwise we could
427
+ // crash the context with too long a loop.
428
+ i -= sign( transmissiveTraversals );
429
+ transmissiveTraversals -= sign( transmissiveTraversals );
430
+
431
+ rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
432
+ continue;
433
+
434
+ }
435
+
436
+ faceNormal = surf.faceNormal;
437
+
438
+ mat3 normalBasis = getBasisFromNormal( surf.normal );
439
+ mat3 invBasis = inverse( normalBasis );
440
+
441
+ mat3 clearcoatNormalBasis = getBasisFromNormal( surf.clearcoatNormal );
442
+ mat3 clearcoatInvBasis = inverse( clearcoatNormalBasis );
443
+
444
+ vec3 outgoing = - normalize( invBasis * rayDirection );
445
+ vec3 clearcoatOutgoing = - normalize( clearcoatInvBasis * rayDirection );
446
+ sampleRec = bsdfSample( outgoing, clearcoatOutgoing, normalBasis, invBasis, clearcoatNormalBasis, clearcoatInvBasis, surf );
447
+
448
+ bool wasBelowSurface = ! surf.volumeParticle && dot( rayDirection, faceNormal ) > 0.0;
449
+ isShadowRay = sampleRec.specularPdf < sobol( 4 );
450
+
451
+ vec3 prevRayDirection = rayDirection;
452
+ rayDirection = normalize( normalBasis * sampleRec.direction );
453
+
454
+ bool isBelowSurface = ! surf.volumeParticle && dot( rayDirection, faceNormal ) < 0.0;
455
+ rayOrigin = stepRayOrigin( rayOrigin, prevRayDirection, isBelowSurface ? - faceNormal : faceNormal, dist );
456
+
457
+ // direct env map sampling
458
+ #if FEATURE_MIS
459
+
460
+ // uniformly pick a light or environment map
461
+ if( lightsDenom != 0.0 && sobol( 5 ) < float( lights.count ) / lightsDenom ) {
462
+
463
+ // sample a light or environment
464
+ LightSampleRecord lightSampleRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, sobol3( 6 ) );
465
+
466
+ bool isSampleBelowSurface = ! surf.volumeParticle && dot( faceNormal, lightSampleRec.direction ) < 0.0;
467
+ if ( isSampleBelowSurface ) {
468
+
469
+ lightSampleRec.pdf = 0.0;
470
+
471
+ }
472
+
473
+ // check if a ray could even reach the light area
474
+ vec3 attenuatedColor;
475
+ if (
476
+ lightSampleRec.pdf > 0.0 &&
477
+ isDirectionValid( lightSampleRec.direction, surf.normal, faceNormal ) &&
478
+ ! attenuateHit( bvh, rayOrigin, lightSampleRec.direction, lightSampleRec.dist, bounces - i, transmissiveTraversals, isShadowRay, fogMaterial, attenuatedColor )
479
+ ) {
480
+
481
+ // get the material pdf
482
+ vec3 sampleColor;
483
+ float lightMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * lightSampleRec.direction ), normalize( clearcoatInvBasis * lightSampleRec.direction ), surf, sampleColor );
484
+ bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
485
+ if ( lightMaterialPdf > 0.0 && isValidSampleColor ) {
486
+
487
+ // weight the direct light contribution
488
+ float lightPdf = lightSampleRec.pdf / lightsDenom;
489
+ float misWeight = lightSampleRec.type == SPOT_LIGHT_TYPE || lightSampleRec.type == DIR_LIGHT_TYPE || lightSampleRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
490
+ gl_FragColor.rgb += attenuatedColor * lightSampleRec.emission * throughputColor * sampleColor * misWeight / lightPdf;
491
+
492
+ }
493
+
494
+ }
495
+
496
+ } else {
497
+
498
+ // find a sample in the environment map to include in the contribution
499
+ vec3 envColor, envDirection;
500
+ float envPdf = sampleEquirectProbability( envMapInfo, sobol2( 7 ), envColor, envDirection );
501
+ envDirection = invEnvRotation3x3 * envDirection;
502
+
503
+ // this env sampling is not set up for transmissive sampling and yields overly bright
504
+ // results so we ignore the sample in this case.
505
+ // TODO: this should be improved but how? The env samples could traverse a few layers?
506
+ bool isSampleBelowSurface = ! surf.volumeParticle && dot( faceNormal, envDirection ) < 0.0;
507
+ if ( isSampleBelowSurface ) {
508
+
509
+ envPdf = 0.0;
510
+
511
+ }
512
+
513
+ // check if a ray could even reach the surface
514
+ vec3 attenuatedColor;
515
+ if (
516
+ envPdf > 0.0 &&
517
+ isDirectionValid( envDirection, surf.normal, faceNormal ) &&
518
+ ! attenuateHit( bvh, rayOrigin, envDirection, INFINITY, bounces - i, transmissiveTraversals, isShadowRay, fogMaterial, attenuatedColor )
519
+ ) {
520
+
521
+ // get the material pdf
522
+ vec3 sampleColor;
523
+ float envMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * envDirection ), normalize( clearcoatInvBasis * envDirection ), surf, sampleColor );
524
+ bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
525
+ if ( envMaterialPdf > 0.0 && isValidSampleColor ) {
526
+
527
+ // weight the direct light contribution
528
+ envPdf /= lightsDenom;
529
+ float misWeight = misHeuristic( envPdf, envMaterialPdf );
530
+ gl_FragColor.rgb += attenuatedColor * environmentIntensity * envColor * throughputColor * sampleColor * misWeight / envPdf;
531
+
532
+ }
533
+
534
+ }
535
+
536
+ }
537
+ #endif
538
+
539
+ // accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
540
+ // to a single pixel resulting in fireflies
541
+ if ( ! surf.volumeParticle && ! isBelowSurface ) {
542
+
543
+ // determine if this is a rough normal or not by checking how far off straight up it is
544
+ vec3 halfVector = normalize( outgoing + sampleRec.direction );
545
+ vec3 clearcoatHalfVector = normalize( clearcoatOutgoing + sampleRec.clearcoatDirection );
546
+ accumulatedRoughness += max( sin( acosApprox( halfVector.z ) ), sin( acosApprox( clearcoatHalfVector.z ) ) );
547
+
548
+ transmissiveRay = false;
549
+
550
+ }
551
+
552
+ // if we're bouncing around the inside a transmissive material then decrement
553
+ // perform this separate from a bounce
554
+ bool isTransmissiveRay = ! surf.volumeParticle && dot( rayDirection, faceNormal * side ) < 0.0;
555
+ if ( ( isTransmissiveRay || isBelowSurface ) && transmissiveTraversals > 0 ) {
556
+
557
+ transmissiveTraversals --;
558
+ i --;
559
+
560
+ }
561
+
562
+ #if FEATURE_FOG
563
+ if ( material.fogVolume ) {
564
+
565
+ transmissiveTraversals --;
566
+ i --;
567
+
568
+ }
569
+ #endif
570
+
571
+ // accumulate color
572
+ gl_FragColor.rgb += ( surf.emission * throughputColor );
573
+
574
+ // skip the sample if our PDF or ray is impossible
575
+ if ( sampleRec.pdf <= 0.0 || ! isDirectionValid( rayDirection, surf.normal, faceNormal ) ) {
576
+
577
+ break;
578
+
579
+ }
580
+
581
+ #if FEATURE_RUSSIAN_ROULETTE
582
+
583
+ // russian roulette path termination
584
+ // https://www.arnoldrenderer.com/research/physically_based_shader_design_in_arnold.pdf
585
+ uint minBounces = 3u;
586
+ float depthProb = float( sobolBounceIndex < minBounces );
587
+
588
+ float rrProb = luminance( throughputColor * sampleRec.color / sampleRec.pdf );
589
+ rrProb /= luminance( throughputColor );
590
+ rrProb = sqrt( rrProb );
591
+ rrProb = max( rrProb, depthProb );
592
+ rrProb = min( rrProb, 1.0 );
593
+ if ( sobol( 8 ) > rrProb ) {
594
+
595
+ break;
596
+
597
+ }
598
+
599
+ // perform sample clamping here to avoid bright pixels
600
+ throughputColor *= min( 1.0 / rrProb, 20.0 );
601
+
602
+ #endif
603
+
604
+ throughputColor *= sampleRec.color / sampleRec.pdf;
605
+
606
+ // attenuate the throughput color by the medium color
607
+ if ( side == - 1.0 ) {
608
+
609
+ throughputColor *= transmissionAttenuation( dist, surf.attenuationColor, surf.attenuationDistance );
610
+
611
+ }
612
+
613
+ // discard the sample if there are any NaNs
614
+ if ( any( isnan( throughputColor ) ) || any( isinf( throughputColor ) ) ) {
615
+
616
+ break;
617
+
618
+ }
619
+
620
+
621
+ }
622
+
623
+ gl_FragColor.a *= opacity;
624
+
625
+ }
626
+
627
+ `
628
+
629
+ } );
630
+
631
+ this.setValues( parameters );
632
+
633
+ }
634
+
635
+ }