three-gpu-pathtracer 0.0.16 → 0.0.18

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 (52) hide show
  1. package/README.md +3 -1
  2. package/build/index.module.js +1202 -471
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +1200 -470
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +5 -6
  7. package/src/core/DynamicPathTracingSceneGenerator.js +80 -35
  8. package/src/core/PathTracingRenderer.js +36 -38
  9. package/src/core/PathTracingSceneGenerator.js +11 -55
  10. package/src/materials/debug/GraphMaterial.js +1 -1
  11. package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
  12. package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
  13. package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
  14. package/src/materials/pathtracing/LambertPathTracingMaterial.js +5 -4
  15. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +87 -50
  16. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +21 -20
  17. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
  18. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +12 -8
  19. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +3 -2
  20. package/src/materials/pathtracing/glsl/traceScene.glsl.js +11 -13
  21. package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
  22. package/src/shader/bsdf/bsdfSampling.glsl.js +21 -17
  23. package/src/shader/common/bvhAnyHit.glsl.js +2 -2
  24. package/src/shader/common/fresnel.glsl.js +15 -9
  25. package/src/shader/common/intersectShapes.glsl.js +2 -2
  26. package/src/shader/rand/pcg.glsl.js +4 -4
  27. package/src/shader/rand/sobol.glsl.js +3 -3
  28. package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
  29. package/src/shader/sampling/equirectSampling.glsl.js +10 -10
  30. package/src/shader/sampling/lightSampling.glsl.js +30 -37
  31. package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
  32. package/src/shader/structs/lightsStruct.glsl.js +15 -6
  33. package/src/shader/structs/materialStruct.glsl.js +16 -15
  34. package/src/textures/BlueNoiseTexture.js +87 -0
  35. package/src/textures/ProceduralEquirectTexture.js +6 -6
  36. package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
  37. package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
  38. package/src/textures/blueNoise/utils.js +24 -0
  39. package/src/uniforms/EquirectHdrInfoUniform.js +59 -21
  40. package/src/uniforms/IESProfilesTexture.js +2 -2
  41. package/src/uniforms/LightsInfoUniformStruct.js +15 -9
  42. package/src/uniforms/MaterialsTexture.js +3 -1
  43. package/src/uniforms/RenderTarget2DArray.js +50 -3
  44. package/src/uniforms/StratifiedSamplesTexture.js +49 -0
  45. package/src/uniforms/stratified/StratifiedSampler.js +73 -0
  46. package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
  47. package/src/uniforms/utils.js +1 -1
  48. package/src/utils/BlurredEnvMapGenerator.js +4 -4
  49. package/src/utils/GeometryPreparationUtils.js +8 -95
  50. package/src/utils/IESLoader.js +7 -5
  51. package/src/utils/TextureUtils.js +15 -0
  52. package/src/workers/PathTracingSceneWorker.js +18 -8
@@ -2,7 +2,7 @@ import { Matrix4, Vector2 } from 'three';
2
2
  import { MaterialBase } from '../MaterialBase.js';
3
3
  import {
4
4
  MeshBVHUniformStruct, UIntVertexAttributeTexture,
5
- shaderStructs, shaderIntersectFunction,
5
+ BVHShaderGLSL,
6
6
  } from 'three-mesh-bvh';
7
7
 
8
8
  // uniforms
@@ -48,6 +48,9 @@ import { attenuateHitGLSL } from './glsl/attenuateHit.glsl.js';
48
48
  import { traceSceneGLSL } from './glsl/traceScene.glsl.js';
49
49
  import { getSurfaceRecordGLSL } from './glsl/getSurfaceRecord.glsl.js';
50
50
  import { directLightContributionGLSL } from './glsl/directLightContribution.glsl.js';
51
+ import { stratifiedTextureGLSL } from '../../shader/rand/stratifiedTexture.glsl.js';
52
+ import { StratifiedSamplesTexture } from '../../uniforms/StratifiedSamplesTexture.js';
53
+ import { BlueNoiseTexture } from '../../textures/BlueNoiseTexture.js';
51
54
 
52
55
  export class PhysicalPathTracingMaterial extends MaterialBase {
53
56
 
@@ -72,6 +75,12 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
72
75
  FEATURE_DOF: 1,
73
76
  FEATURE_BACKGROUND_MAP: 0,
74
77
  FEATURE_FOG: 1,
78
+
79
+ // 0 = PCG
80
+ // 1 = Sobol
81
+ // 2 = Stratified List
82
+ RANDOM_TYPE: 2,
83
+
75
84
  // 0 = Perspective
76
85
  // 1 = Orthographic
77
86
  // 2 = Equirectangular
@@ -113,6 +122,8 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
113
122
 
114
123
  backgroundAlpha: { value: 1.0 },
115
124
  sobolTexture: { value: null },
125
+ stratifiedTexture: { value: new StratifiedSamplesTexture() },
126
+ stratifiedOffsetTexture: { value: new BlueNoiseTexture( 64, 1 ) },
116
127
  },
117
128
 
118
129
  vertexShader: /* glsl */`
@@ -141,13 +152,48 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
141
152
  #include <common>
142
153
 
143
154
  // bvh intersection
144
- ${ shaderStructs }
145
- ${ shaderIntersectFunction }
155
+ ${ BVHShaderGLSL.common_functions }
156
+ ${ BVHShaderGLSL.bvh_struct_definitions }
157
+ ${ BVHShaderGLSL.bvh_ray_functions }
158
+
159
+ // uniform structs
160
+ ${ cameraStructGLSL }
161
+ ${ lightsStructGLSL }
162
+ ${ equirectStructGLSL }
163
+ ${ materialStructGLSL }
146
164
 
147
165
  // random
148
- ${ pcgGLSL }
149
- ${ sobolCommonGLSL }
150
- ${ sobolSamplingGLSL }
166
+ #if RANDOM_TYPE == 2 // Stratified List
167
+
168
+ ${ stratifiedTextureGLSL }
169
+
170
+ #elif RANDOM_TYPE == 1 // Sobol
171
+
172
+ ${ pcgGLSL }
173
+ ${ sobolCommonGLSL }
174
+ ${ sobolSamplingGLSL }
175
+
176
+ #define rand(v) sobol(v)
177
+ #define rand2(v) sobol2(v)
178
+ #define rand3(v) sobol3(v)
179
+ #define rand4(v) sobol4(v)
180
+
181
+ #else // PCG
182
+
183
+ ${ pcgGLSL }
184
+
185
+ // Using the sobol functions seems to break the the compiler on MacOS
186
+ // - specifically the "sobolReverseBits" function.
187
+ uint sobolPixelIndex = 0u;
188
+ uint sobolPathIndex = 0u;
189
+ uint sobolBounceIndex = 0u;
190
+
191
+ #define rand(v) pcgRand()
192
+ #define rand2(v) pcgRand2()
193
+ #define rand3(v) pcgRand3()
194
+ #define rand4(v) pcgRand4()
195
+
196
+ #endif
151
197
 
152
198
  // common
153
199
  ${ arraySamplerTexelFetchGLSL }
@@ -156,20 +202,6 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
156
202
  ${ mathGLSL }
157
203
  ${ intersectShapesGLSL }
158
204
 
159
- // uniform structs
160
- ${ cameraStructGLSL }
161
- ${ lightsStructGLSL }
162
- ${ equirectStructGLSL }
163
- ${ materialStructGLSL }
164
- ${ fogMaterialBvhGLSL }
165
-
166
- // sampling
167
- ${ shapeSamplingGLSL }
168
- ${ bsdfSamplingGLSL }
169
- ${ equirectSamplingGLSL }
170
- ${ lightSamplingGLSL }
171
- ${ fogGLSL }
172
-
173
205
  // environment
174
206
  uniform EquirectHdrInfo envMapInfo;
175
207
  uniform mat4 environmentRotation;
@@ -221,6 +253,14 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
221
253
  mat3 invEnvRotation3x3;
222
254
  float lightsDenom;
223
255
 
256
+ // sampling
257
+ ${ fogMaterialBvhGLSL }
258
+ ${ shapeSamplingGLSL }
259
+ ${ bsdfSamplingGLSL }
260
+ ${ equirectSamplingGLSL }
261
+ ${ lightSamplingGLSL }
262
+ ${ fogGLSL }
263
+
224
264
  float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
225
265
 
226
266
  return clamp(
@@ -260,7 +300,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
260
300
 
261
301
  // init
262
302
  rng_initialize( gl_FragCoord.xy, seed );
263
- sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
303
+ sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
264
304
  sobolPathIndex = uint( seed );
265
305
 
266
306
  // get camera ray
@@ -276,7 +316,6 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
276
316
 
277
317
  // surface results
278
318
  SurfaceHit surfaceHit;
279
- LightRecord lightRec;
280
319
  ScatterRecord scatterRec;
281
320
 
282
321
  // path tracing state
@@ -285,7 +324,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
285
324
  #if FEATURE_FOG
286
325
 
287
326
  state.fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
288
- bvh, ray.origin, - ray.direction,
327
+ ray.origin, - ray.direction,
289
328
  materialIndexAttribute, materials,
290
329
  state.fogMaterial
291
330
  );
@@ -300,48 +339,46 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
300
339
  state.traversals = bounces - i;
301
340
  state.firstRay = i == 0 && state.transmissiveTraversals == transmissiveBounces;
302
341
 
303
- int hitType = traceScene(
304
- ray, bvh, lights, state.fogMaterial,
305
- surfaceHit, lightRec
306
- );
307
-
308
- if ( hitType == LIGHT_HIT ) {
309
-
310
- if ( state.firstRay || state.transmissiveRay ) {
342
+ int hitType = traceScene( ray, state.fogMaterial, surfaceHit );
311
343
 
312
- gl_FragColor.rgb += lightRec.emission * state.throughputColor;
344
+ // check if we intersect any lights and accumulate the light contribution
345
+ // TODO: we can add support for light surface rendering in the else condition if we
346
+ // add the ability to toggle visibility of the the light
347
+ if ( ! state.firstRay && ! state.transmissiveRay ) {
313
348
 
314
- } else {
349
+ LightRecord lightRec;
350
+ float lightDist = hitType == NO_HIT ? INFINITY : surfaceHit.dist;
351
+ for ( uint i = 0u; i < lights.count; i ++ ) {
315
352
 
316
- #if FEATURE_MIS
353
+ if (
354
+ intersectLightAtIndex( lights.tex, ray.origin, ray.direction, i, lightRec ) &&
355
+ lightRec.dist < lightDist
356
+ ) {
317
357
 
318
- // NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
319
- if ( lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ) {
320
-
321
- gl_FragColor.rgb += lightRec.emission * state.throughputColor;
322
-
323
- } else {
358
+ #if FEATURE_MIS
324
359
 
325
360
  // weight the contribution
361
+ // NOTE: Only area lights are supported for forward sampling and can be hit
326
362
  float misWeight = misHeuristic( scatterRec.pdf, lightRec.pdf / lightsDenom );
327
363
  gl_FragColor.rgb += lightRec.emission * state.throughputColor * misWeight;
328
364
 
329
- }
365
+ #else
330
366
 
331
- #else
367
+ gl_FragColor.rgb += lightRec.emission * state.throughputColor;
332
368
 
333
- gl_FragColor.rgb += lightRec.emission * state.throughputColor;
369
+ #endif
334
370
 
335
- #endif
371
+ }
336
372
 
337
373
  }
338
- break;
339
374
 
340
- } else if ( hitType == NO_HIT ) {
375
+ }
376
+
377
+ if ( hitType == NO_HIT ) {
341
378
 
342
379
  if ( state.firstRay || state.transmissiveRay ) {
343
380
 
344
- gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction, sobol2( 2 ) ) * state.throughputColor;
381
+ gl_FragColor.rgb += sampleBackground( envRotation3x3 * ray.direction, rand2( 2 ) ) * state.throughputColor;
345
382
  gl_FragColor.a = backgroundAlpha;
346
383
 
347
384
  } else {
@@ -350,7 +387,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
350
387
 
351
388
  // get the PDF of the hit envmap point
352
389
  vec3 envColor;
353
- float envPdf = sampleEquirect( envMapInfo, envRotation3x3 * ray.direction, envColor );
390
+ float envPdf = sampleEquirect( envRotation3x3 * ray.direction, envColor );
354
391
  envPdf /= lightsDenom;
355
392
 
356
393
  // and weight the contribution
@@ -432,7 +469,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
432
469
  }
433
470
 
434
471
  scatterRec = bsdfSample( - ray.direction, surf );
435
- state.isShadowRay = scatterRec.specularPdf < sobol( 4 );
472
+ state.isShadowRay = scatterRec.specularPdf < rand( 4 );
436
473
 
437
474
  bool isBelowSurface = ! surf.volumeParticle && dot( scatterRec.direction, surf.faceNormal ) < 0.0;
438
475
  vec3 hitPoint = stepRayOrigin( ray.origin, ray.direction, isBelowSurface ? - surf.faceNormal : surf.faceNormal, surfaceHit.dist );
@@ -502,7 +539,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
502
539
  rrProb = sqrt( rrProb );
503
540
  rrProb = max( rrProb, depthProb );
504
541
  rrProb = min( rrProb, 1.0 );
505
- if ( sobol( 8 ) > rrProb ) {
542
+ if ( rand( 8 ) > rrProb ) {
506
543
 
507
544
  break;
508
545
 
@@ -3,11 +3,14 @@ export const attenuateHitGLSL = /* glsl */`
3
3
  // step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
4
4
  // returns true if a solid surface was hit
5
5
  bool attenuateHit(
6
- BVH bvh, RenderState state,
6
+ RenderState state,
7
7
  Ray ray, float rayDist,
8
8
  out vec3 color
9
9
  ) {
10
10
 
11
+ // store the original bounce index so we can reset it after
12
+ uint originalBounceIndex = sobolBounceIndex;
13
+
11
14
  int traversals = state.traversals;
12
15
  int transmissiveTraversals = state.transmissiveTraversals;
13
16
  bool isShadowRay = state.isShadowRay;
@@ -17,34 +20,28 @@ export const attenuateHitGLSL = /* glsl */`
17
20
 
18
21
  // hit results
19
22
  SurfaceHit surfaceHit;
20
- LightRecord lightRec;
21
23
 
22
24
  color = vec3( 1.0 );
23
25
 
24
- // TODO: we should be using sobol sampling here instead of rand but the sobol bounce and path indices need to be incremented
25
- // and then reset.
26
+ bool result = true;
26
27
  for ( int i = 0; i < traversals; i ++ ) {
27
28
 
28
- int hitType = traceScene(
29
- ray, bvh, lights, fogMaterial,
30
- surfaceHit, lightRec
31
- );
32
-
33
- if ( hitType == FOG_HIT ) {
29
+ sobolBounceIndex ++;
34
30
 
35
- return true;
31
+ int hitType = traceScene( ray, fogMaterial, surfaceHit );
36
32
 
37
- } else if ( hitType == LIGHT_HIT ) {
33
+ if ( hitType == FOG_HIT ) {
38
34
 
39
- float totalDist = distance( startPoint, ray.origin + ray.direction * lightRec.dist );
40
- return totalDist < rayDist - max( totalDist, rayDist ) * 1e-4;
35
+ result = true;
36
+ break;
41
37
 
42
38
  } else if ( hitType == SURFACE_HIT ) {
43
39
 
44
40
  float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
45
41
  if ( totalDist > rayDist ) {
46
42
 
47
- return false;
43
+ result = false;
44
+ break;
48
45
 
49
46
  }
50
47
 
@@ -126,7 +123,7 @@ export const attenuateHitGLSL = /* glsl */`
126
123
  bool useAlphaTest = alphaTest != 0.0;
127
124
  float transmissionFactor = ( 1.0 - metalness ) * transmission;
128
125
  if (
129
- transmissionFactor < rand() && ! (
126
+ transmissionFactor < rand( 9 ) && ! (
130
127
  // material sidedness
131
128
  material.side != 0.0 && surfaceHit.side == material.side
132
129
 
@@ -134,11 +131,12 @@ export const attenuateHitGLSL = /* glsl */`
134
131
  || useAlphaTest && albedo.a < alphaTest
135
132
 
136
133
  // opacity
137
- || material.transparent && ! useAlphaTest && albedo.a < rand()
134
+ || material.transparent && ! useAlphaTest && albedo.a < rand( 10 )
138
135
  )
139
136
  ) {
140
137
 
141
- return true;
138
+ result = true;
139
+ break;
142
140
 
143
141
  }
144
142
 
@@ -164,13 +162,16 @@ export const attenuateHitGLSL = /* glsl */`
164
162
 
165
163
  } else {
166
164
 
167
- return false;
165
+ result = false;
166
+ break;
168
167
 
169
168
  }
170
169
 
171
170
  }
172
171
 
173
- return true;
172
+ // reset the bounce index
173
+ sobolBounceIndex = originalBounceIndex;
174
+ return result;
174
175
 
175
176
  }
176
177
 
@@ -12,7 +12,7 @@ export const cameraUtilsGLSL = /* glsl */`
12
12
 
13
13
  // Jitter the camera ray by finding a uv coordinate at a random sample
14
14
  // around this pixel's UV coordinate for AA
15
- vec2 ruv = sobol2( 0 );
15
+ vec2 ruv = rand2( 0 );
16
16
  vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
17
17
  Ray ray;
18
18
 
@@ -57,7 +57,7 @@ export const cameraUtilsGLSL = /* glsl */`
57
57
 
58
58
  // get the aperture sample
59
59
  // if blades === 0 then we assume a circle
60
- vec3 shapeUVW= sobol3( 1 );
60
+ vec3 shapeUVW= rand3( 1 );
61
61
  int blades = physicalCamera.apertureBlades;
62
62
  float anamorphicRatio = physicalCamera.anamorphicRatio;
63
63
  vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
@@ -2,11 +2,13 @@ export const directLightContributionGLSL = /*glsl*/`
2
2
 
3
3
  vec3 directLightContribution( vec3 worldWo, SurfaceRecord surf, RenderState state, vec3 rayOrigin ) {
4
4
 
5
+ vec3 result = vec3( 0.0 );
6
+
5
7
  // uniformly pick a light or environment map
6
- if( lightsDenom != 0.0 && sobol( 5 ) < float( lights.count ) / lightsDenom ) {
8
+ if( lightsDenom != 0.0 && rand( 5 ) < float( lights.count ) / lightsDenom ) {
7
9
 
8
10
  // sample a light or environment
9
- LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, sobol3( 6 ) );
11
+ LightRecord lightRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, rand3( 6 ) );
10
12
 
11
13
  bool isSampleBelowSurface = ! surf.volumeParticle && dot( surf.faceNormal, lightRec.direction ) < 0.0;
12
14
  if ( isSampleBelowSurface ) {
@@ -23,7 +25,7 @@ export const directLightContributionGLSL = /*glsl*/`
23
25
  if (
24
26
  lightRec.pdf > 0.0 &&
25
27
  isDirectionValid( lightRec.direction, surf.normal, surf.faceNormal ) &&
26
- ! attenuateHit( bvh, state, lightRay, lightRec.dist, attenuatedColor )
28
+ ! attenuateHit( state, lightRay, lightRec.dist, attenuatedColor )
27
29
  ) {
28
30
 
29
31
  // get the material pdf
@@ -35,7 +37,7 @@ export const directLightContributionGLSL = /*glsl*/`
35
37
  // weight the direct light contribution
36
38
  float lightPdf = lightRec.pdf / lightsDenom;
37
39
  float misWeight = lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
38
- return attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
40
+ result = attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
39
41
 
40
42
  }
41
43
 
@@ -45,7 +47,7 @@ export const directLightContributionGLSL = /*glsl*/`
45
47
 
46
48
  // find a sample in the environment map to include in the contribution
47
49
  vec3 envColor, envDirection;
48
- float envPdf = sampleEquirectProbability( envMapInfo, sobol2( 7 ), envColor, envDirection );
50
+ float envPdf = sampleEquirectProbability( rand2( 7 ), envColor, envDirection );
49
51
  envDirection = invEnvRotation3x3 * envDirection;
50
52
 
51
53
  // this env sampling is not set up for transmissive sampling and yields overly bright
@@ -66,7 +68,7 @@ export const directLightContributionGLSL = /*glsl*/`
66
68
  if (
67
69
  envPdf > 0.0 &&
68
70
  isDirectionValid( envDirection, surf.normal, surf.faceNormal ) &&
69
- ! attenuateHit( bvh, state, envRay, INFINITY, attenuatedColor )
71
+ ! attenuateHit( state, envRay, INFINITY, attenuatedColor )
70
72
  ) {
71
73
 
72
74
  // get the material pdf
@@ -78,7 +80,7 @@ export const directLightContributionGLSL = /*glsl*/`
78
80
  // weight the direct light contribution
79
81
  envPdf /= lightsDenom;
80
82
  float misWeight = misHeuristic( envPdf, envMaterialPdf );
81
- return attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
83
+ result = attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
82
84
 
83
85
  }
84
86
 
@@ -86,7 +88,9 @@ export const directLightContributionGLSL = /*glsl*/`
86
88
 
87
89
  }
88
90
 
89
- return vec3( 0.0 );
91
+ // Function changed to have a single return statement to potentially help with crashes on Mac OS.
92
+ // See issue #470
93
+ return result;
90
94
 
91
95
  }
92
96
 
@@ -6,7 +6,7 @@ export const getSurfaceRecordGLSL = /* glsl */`
6
6
  int getSurfaceRecord(
7
7
  Material material, SurfaceHit surfaceHit, sampler2DArray attributesArray,
8
8
  float accumulatedRoughness,
9
- out SurfaceRecord surf
9
+ inout SurfaceRecord surf
10
10
  ) {
11
11
 
12
12
  if ( material.fogVolume ) {
@@ -36,6 +36,7 @@ export const getSurfaceRecordGLSL = /* glsl */`
36
36
 
37
37
  vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
38
38
  albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
39
+
39
40
  }
40
41
 
41
42
  if ( material.vertexColors ) {
@@ -67,7 +68,7 @@ export const getSurfaceRecordGLSL = /* glsl */`
67
68
  || useAlphaTest && albedo.a < alphaTest
68
69
 
69
70
  // opacity
70
- || material.transparent && ! useAlphaTest && albedo.a < sobol( 3 )
71
+ || material.transparent && ! useAlphaTest && albedo.a < rand( 3 )
71
72
  ) {
72
73
 
73
74
  return SKIP_SURFACE;
@@ -5,22 +5,26 @@ export const traceSceneGLSL = /* glsl */`
5
5
  #define LIGHT_HIT 2
6
6
  #define FOG_HIT 3
7
7
 
8
+ // Passing the global variable 'lights' into this function caused shader program errors.
9
+ // So global variables like 'lights' and 'bvh' were moved out of the function parameters.
10
+ // For more information, refer to: https://github.com/gkjohnson/three-gpu-pathtracer/pull/457
8
11
  int traceScene(
9
12
 
10
- Ray ray, BVH bvh, LightsInfo lights, Material fogMaterial,
11
- out SurfaceHit surfaceHit, out LightRecord lightRec
13
+ Ray ray, Material fogMaterial, inout SurfaceHit surfaceHit
12
14
 
13
15
  ) {
14
16
 
17
+ int result = NO_HIT;
15
18
  bool hit = bvhIntersectFirstHit( bvh, ray.origin, ray.direction, surfaceHit.faceIndices, surfaceHit.faceNormal, surfaceHit.barycoord, surfaceHit.side, surfaceHit.dist );
16
- bool lightHit = lightsClosestHit( lights.tex, lights.count, ray.origin, ray.direction, lightRec );
17
19
 
18
20
  #if FEATURE_FOG
19
21
 
20
22
  if ( fogMaterial.fogVolume ) {
21
23
 
22
- float particleDist = intersectFogVolume( fogMaterial, sobol( 1 ) );
23
- if ( particleDist + 1e-4 < surfaceHit.dist && ( particleDist + 1e-4 < lightRec.dist || ! lightHit ) ) {
24
+ // offset the distance so we don't run into issues with particles on the same surface
25
+ // as other objects
26
+ float particleDist = intersectFogVolume( fogMaterial, rand( 1 ) );
27
+ if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
24
28
 
25
29
  surfaceHit.side = 1.0;
26
30
  surfaceHit.faceNormal = normalize( - ray.direction );
@@ -33,19 +37,13 @@ export const traceSceneGLSL = /* glsl */`
33
37
 
34
38
  #endif
35
39
 
36
- if ( lightHit && ( lightRec.dist < surfaceHit.dist || ! hit ) ) {
37
-
38
- return LIGHT_HIT;
39
-
40
- }
41
-
42
40
  if ( hit ) {
43
41
 
44
- return SURFACE_HIT;
42
+ result = SURFACE_HIT;
45
43
 
46
44
  }
47
45
 
48
- return NO_HIT;
46
+ return result;
49
47
 
50
48
  }
51
49
 
@@ -1,6 +1,6 @@
1
1
  import { TangentSpaceNormalMap, Vector2 } from 'three';
2
2
  import { MaterialBase } from '../MaterialBase.js';
3
- import { MeshBVHUniformStruct, shaderStructs, shaderIntersectFunction } from 'three-mesh-bvh';
3
+ import { MeshBVHUniformStruct, BVHShaderGLSL } from 'three-mesh-bvh';
4
4
 
5
5
  import { materialStructGLSL } from '../../shader/structs/materialStruct.glsl.js';
6
6
  import { shapeSamplingGLSL } from '../../shader/sampling/shapeSampling.glsl.js';
@@ -97,8 +97,9 @@ export class AmbientOcclusionMaterial extends MaterialBase {
97
97
  #include <cube_uv_reflection_fragment>
98
98
 
99
99
  // bvh
100
- ${ shaderStructs }
101
- ${ shaderIntersectFunction }
100
+ ${ BVHShaderGLSL.common_functions }
101
+ ${ BVHShaderGLSL.bvh_struct_definitions }
102
+ ${ BVHShaderGLSL.bvh_ray_functions }
102
103
 
103
104
  // uniform structs
104
105
  ${ materialStructGLSL }