three-gpu-pathtracer 0.0.16 → 0.0.17

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 (38) hide show
  1. package/README.md +3 -1
  2. package/build/index.module.js +248 -198
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +245 -195
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +5 -6
  7. package/src/core/DynamicPathTracingSceneGenerator.js +8 -3
  8. package/src/core/PathTracingRenderer.js +8 -4
  9. package/src/core/PathTracingSceneGenerator.js +6 -1
  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 +4 -3
  15. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +61 -46
  16. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +2 -11
  17. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +10 -6
  18. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +1 -1
  19. package/src/materials/pathtracing/glsl/traceScene.glsl.js +10 -12
  20. package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
  21. package/src/shader/bsdf/bsdfSampling.glsl.js +7 -7
  22. package/src/shader/common/bvhAnyHit.glsl.js +2 -2
  23. package/src/shader/common/intersectShapes.glsl.js +2 -2
  24. package/src/shader/rand/sobol.glsl.js +3 -3
  25. package/src/shader/sampling/equirectSampling.glsl.js +10 -10
  26. package/src/shader/sampling/lightSampling.glsl.js +30 -37
  27. package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
  28. package/src/shader/structs/lightsStruct.glsl.js +12 -1
  29. package/src/shader/structs/materialStruct.glsl.js +16 -15
  30. package/src/textures/ProceduralEquirectTexture.js +9 -8
  31. package/src/uniforms/EquirectHdrInfoUniform.js +18 -17
  32. package/src/uniforms/IESProfilesTexture.js +2 -2
  33. package/src/uniforms/LightsInfoUniformStruct.js +4 -2
  34. package/src/uniforms/MaterialsTexture.js +3 -1
  35. package/src/utils/BlurredEnvMapGenerator.js +4 -4
  36. package/src/utils/GeometryPreparationUtils.js +8 -2
  37. package/src/utils/IESLoader.js +7 -5
  38. package/src/utils/TextureUtils.js +15 -0
@@ -340,9 +340,9 @@
340
340
 
341
341
  // Seeds
342
342
  uniform sampler2D sobolTexture;
343
- uint sobolPixelIndex;
344
- uint sobolPathIndex;
345
- uint sobolBounceIndex;
343
+ uint sobolPixelIndex = 0u;
344
+ uint sobolPathIndex = 0u;
345
+ uint sobolBounceIndex = 0u;
346
346
 
347
347
  uint sobolGetSeed( uint bounce, uint effect ) {
348
348
 
@@ -704,18 +704,22 @@
704
704
  this._currentTile = 0;
705
705
 
706
706
  this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
707
+
708
+ // will be null if extension not supported
709
+ const floatLinearExtensionSupported = renderer.extensions.get( 'OES_texture_float_linear' );
710
+
707
711
  this._primaryTarget = new three.WebGLRenderTarget( 1, 1, {
708
712
  format: three.RGBAFormat,
709
- type: three.FloatType,
713
+ type: floatLinearExtensionSupported ? three.FloatType : three.HalfFloatType,
710
714
  } );
711
715
  this._blendTargets = [
712
716
  new three.WebGLRenderTarget( 1, 1, {
713
717
  format: three.RGBAFormat,
714
- type: three.FloatType,
718
+ type: floatLinearExtensionSupported ? three.FloatType : three.HalfFloatType,
715
719
  } ),
716
720
  new three.WebGLRenderTarget( 1, 1, {
717
721
  format: three.RGBAFormat,
718
- type: three.FloatType,
722
+ type: floatLinearExtensionSupported ? three.FloatType : three.HalfFloatType,
719
723
  } ),
720
724
  ];
721
725
 
@@ -1196,6 +1200,12 @@
1196
1200
  const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
1197
1201
  geometry.applyMatrix4( mesh.matrixWorld );
1198
1202
 
1203
+ if ( mesh.matrixWorld.determinant() < 0 ) {
1204
+
1205
+ geometry.index.array.reverse();
1206
+
1207
+ }
1208
+
1199
1209
  // ensure our geometry has common attributes
1200
1210
  setCommonAttributes( geometry, {
1201
1211
  attributes: options.attributes,
@@ -1227,7 +1237,7 @@
1227
1237
 
1228
1238
  } );
1229
1239
 
1230
- const geometry = BufferGeometryUtils_js.mergeBufferGeometries( transformedGeometry, false );
1240
+ const geometry = BufferGeometryUtils_js.mergeGeometries( transformedGeometry, false );
1231
1241
  const textures = Array.from( textureSet );
1232
1242
  return { geometry, materials, textures };
1233
1243
 
@@ -1264,7 +1274,12 @@
1264
1274
 
1265
1275
  meshes.push( c );
1266
1276
 
1267
- } else if ( c.isRectAreaLight || c.isSpotLight || c.isDirectionalLight || c.isPointLight ) {
1277
+ } else if (
1278
+ c.isRectAreaLight ||
1279
+ c.isSpotLight ||
1280
+ c.isPointLight ||
1281
+ c.isDirectionalLight
1282
+ ) {
1268
1283
 
1269
1284
  lights.push( c );
1270
1285
 
@@ -1335,7 +1350,7 @@
1335
1350
 
1336
1351
  generate() {
1337
1352
 
1338
- const { objects, staticGeometryGenerator, geometry } = this;
1353
+ const { objects, staticGeometryGenerator, geometry, lights } = this;
1339
1354
  if ( this.bvh === null ) {
1340
1355
 
1341
1356
  const attributes = [ 'position', 'normal', 'tangent', 'uv', 'color' ];
@@ -1349,9 +1364,14 @@
1349
1364
  const normalMapRequired = ! ! c.material.normalMap;
1350
1365
  setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
1351
1366
 
1352
- } else if ( c.isRectAreaLight || c.isSpotLight ) {
1367
+ } else if (
1368
+ c.isRectAreaLight ||
1369
+ c.isSpotLight ||
1370
+ c.isPointLight ||
1371
+ c.isDirectionalLight
1372
+ ) {
1353
1373
 
1354
- this.lights.push( c );
1374
+ lights.push( c );
1355
1375
 
1356
1376
  }
1357
1377
 
@@ -1776,11 +1796,11 @@
1776
1796
  const _color = new three.Color();
1777
1797
  class ProceduralEquirectTexture extends three.DataTexture {
1778
1798
 
1779
- constructor( width, height ) {
1799
+ constructor( width = 512, height = 512 ) {
1780
1800
 
1781
1801
  super(
1782
- new Float32Array( width * height * 4 ),
1783
- width, height, three.RGBAFormat, three.FloatType, three.EquirectangularReflectionMapping,
1802
+ new Uint16Array( width * height * 4 ),
1803
+ width, height, three.RGBAFormat, three.HalfFloatType, three.EquirectangularReflectionMapping,
1784
1804
  three.RepeatWrapping, three.ClampToEdgeWrapping, three.LinearFilter, three.LinearFilter,
1785
1805
  );
1786
1806
 
@@ -1812,10 +1832,10 @@
1812
1832
 
1813
1833
  const i = y * width + x;
1814
1834
  const i4 = 4 * i;
1815
- data[ i4 + 0 ] = _color.r;
1816
- data[ i4 + 1 ] = _color.g;
1817
- data[ i4 + 2 ] = _color.b;
1818
- data[ i4 + 3 ] = 1.0;
1835
+ data[ i4 + 0 ] = three.DataUtils.toHalfFloat( _color.r );
1836
+ data[ i4 + 1 ] = three.DataUtils.toHalfFloat( _color.g );
1837
+ data[ i4 + 2 ] = three.DataUtils.toHalfFloat( _color.b );
1838
+ data[ i4 + 3 ] = three.DataUtils.toHalfFloat( 1.0 );
1819
1839
 
1820
1840
  }
1821
1841
 
@@ -1949,6 +1969,8 @@
1949
1969
  this.type = three.FloatType;
1950
1970
  this.wrapS = three.ClampToEdgeWrapping;
1951
1971
  this.wrapT = three.ClampToEdgeWrapping;
1972
+ this.minFilter = three.NearestFilter;
1973
+ this.magFilter = three.NearestFilter;
1952
1974
  this.generateMipmaps = false;
1953
1975
  this.threeCompatibilityTransforms = false;
1954
1976
  this.features = new MaterialFeatures();
@@ -2483,6 +2505,19 @@
2483
2505
 
2484
2506
  }
2485
2507
 
2508
+ function toHalfFloatArray( f32Array ) {
2509
+
2510
+ const f16Array = new Uint16Array( f32Array.length );
2511
+ for ( let i = 0, n = f32Array.length; i < n; ++ i ) {
2512
+
2513
+ f16Array[ i ] = three.DataUtils.toHalfFloat( f32Array[ i ] );
2514
+
2515
+ }
2516
+
2517
+ return f16Array;
2518
+
2519
+ }
2520
+
2486
2521
  function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
2487
2522
 
2488
2523
  let lower = offset;
@@ -2532,15 +2567,15 @@
2532
2567
  let newData = data;
2533
2568
  if ( map.type === three.HalfFloatType ) {
2534
2569
 
2535
- newData = new Float32Array( data.length );
2570
+ newData = new Uint16Array( data.length );
2536
2571
  for ( const i in data ) {
2537
2572
 
2538
- newData[ i ] = three.DataUtils.fromHalfFloat( data[ i ] );
2573
+ newData[ i ] = data[ i ];
2539
2574
 
2540
2575
  }
2541
2576
 
2542
2577
  map.image.data = newData;
2543
- map.type = three.FloatType;
2578
+ map.type = three.HalfFloatType;
2544
2579
 
2545
2580
  }
2546
2581
 
@@ -2581,8 +2616,8 @@
2581
2616
 
2582
2617
  // Default to a white texture and associated weights so we don't
2583
2618
  // just render black initially.
2584
- const whiteTex = new three.DataTexture( new Float32Array( [ 1, 1, 1, 1 ] ), 1, 1 );
2585
- whiteTex.type = three.FloatType;
2619
+ const whiteTex = new three.DataTexture( toHalfFloatArray( new Float32Array( [ 1, 1, 1, 1 ] ) ), 1, 1 );
2620
+ whiteTex.type = three.HalfFloatType;
2586
2621
  whiteTex.format = three.RGBAFormat;
2587
2622
  whiteTex.minFilter = three.LinearFilter;
2588
2623
  whiteTex.magFilter = three.LinearFilter;
@@ -2593,8 +2628,8 @@
2593
2628
 
2594
2629
  // Stores a map of [0, 1] value -> cumulative importance row & pdf
2595
2630
  // used to sampling a random value to a relevant row to sample from
2596
- const marginalWeights = new three.DataTexture( new Float32Array( [ 0, 1 ] ), 1, 2 );
2597
- marginalWeights.type = three.FloatType;
2631
+ const marginalWeights = new three.DataTexture( toHalfFloatArray( new Float32Array( [ 0, 1 ] ) ), 1, 2 );
2632
+ marginalWeights.type = three.HalfFloatType;
2598
2633
  marginalWeights.format = three.RedFormat;
2599
2634
  marginalWeights.minFilter = three.LinearFilter;
2600
2635
  marginalWeights.magFilter = three.LinearFilter;
@@ -2603,8 +2638,8 @@
2603
2638
 
2604
2639
  // Stores a map of [0, 1] value -> cumulative importance column & pdf
2605
2640
  // used to sampling a random value to a relevant pixel to sample from
2606
- const conditionalWeights = new three.DataTexture( new Float32Array( [ 0, 0, 1, 1 ] ), 2, 2 );
2607
- conditionalWeights.type = three.FloatType;
2641
+ const conditionalWeights = new three.DataTexture( toHalfFloatArray( new Float32Array( [ 0, 0, 1, 1 ] ) ), 2, 2 );
2642
+ conditionalWeights.type = three.HalfFloatType;
2608
2643
  conditionalWeights.format = three.RedFormat;
2609
2644
  conditionalWeights.minFilter = three.LinearFilter;
2610
2645
  conditionalWeights.magFilter = three.LinearFilter;
@@ -2654,9 +2689,9 @@
2654
2689
  for ( let x = 0; x < width; x ++ ) {
2655
2690
 
2656
2691
  const i = y * width + x;
2657
- const r = data[ 4 * i + 0 ];
2658
- const g = data[ 4 * i + 1 ];
2659
- const b = data[ 4 * i + 2 ];
2692
+ const r = three.DataUtils.fromHalfFloat( data[ 4 * i + 0 ] );
2693
+ const g = three.DataUtils.fromHalfFloat( data[ 4 * i + 1 ] );
2694
+ const b = three.DataUtils.fromHalfFloat( data[ 4 * i + 2 ] );
2660
2695
 
2661
2696
  // the probability of the pixel being selected in this row is the
2662
2697
  // scale of the luminance relative to the rest of the pixels.
@@ -2708,8 +2743,8 @@
2708
2743
  // the marginal and conditional data. These will be used to sample with a random number
2709
2744
  // to retrieve a uv value to sample in the environment map.
2710
2745
  // These values continually increase so it's okay to interpolate between them.
2711
- const marginalDataArray = new Float32Array( height );
2712
- const conditionalDataArray = new Float32Array( width * height );
2746
+ const marginalDataArray = new Uint16Array( height );
2747
+ const conditionalDataArray = new Uint16Array( width * height );
2713
2748
 
2714
2749
  // we add a half texel offset so we're sampling the center of the pixel
2715
2750
  for ( let i = 0; i < height; i ++ ) {
@@ -2717,7 +2752,7 @@
2717
2752
  const dist = ( i + 1 ) / height;
2718
2753
  const row = binarySearchFindClosestIndexOf( cdfMarginal, dist );
2719
2754
 
2720
- marginalDataArray[ i ] = ( row + 0.5 ) / height;
2755
+ marginalDataArray[ i ] = three.DataUtils.toHalfFloat( ( row + 0.5 ) / height );
2721
2756
 
2722
2757
  }
2723
2758
 
@@ -2729,7 +2764,7 @@
2729
2764
  const dist = ( x + 1 ) / width;
2730
2765
  const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );
2731
2766
 
2732
- conditionalDataArray[ i ] = ( col + 0.5 ) / width;
2767
+ conditionalDataArray[ i ] = three.DataUtils.toHalfFloat( ( col + 0.5 ) / width );
2733
2768
 
2734
2769
  }
2735
2770
 
@@ -2803,6 +2838,8 @@
2803
2838
  tex.wrapS = three.ClampToEdgeWrapping;
2804
2839
  tex.wrapT = three.ClampToEdgeWrapping;
2805
2840
  tex.generateMipmaps = false;
2841
+ tex.minFilter = three.NearestFilter;
2842
+ tex.magFilter = three.NearestFilter;
2806
2843
 
2807
2844
  this.tex = tex;
2808
2845
  this.count = 0;
@@ -2833,7 +2870,7 @@
2833
2870
  const worldQuaternion = new three.Quaternion();
2834
2871
  const eye = new three.Vector3();
2835
2872
  const target = new three.Vector3();
2836
- const up = new three.Vector3();
2873
+ const up = new three.Vector3( 0, 1, 0 );
2837
2874
 
2838
2875
  for ( let i = 0, l = lights.length; i < l; i ++ ) {
2839
2876
 
@@ -3276,7 +3313,7 @@
3276
3313
  loader.setPath( this.path );
3277
3314
  loader.setRequestHeader( this.requestHeader );
3278
3315
 
3279
- const texture = new three.DataTexture( null, 360, 180, three.RedFormat, three.FloatType );
3316
+ const texture = new three.DataTexture( null, 360, 180, three.RedFormat, three.HalfFloatType );
3280
3317
  texture.minFilter = three.LinearFilter;
3281
3318
  texture.magFilter = three.LinearFilter;
3282
3319
 
@@ -3284,7 +3321,7 @@
3284
3321
 
3285
3322
  const iesLamp = new IESLamp( text );
3286
3323
 
3287
- texture.image.data = this._getIESValues( iesLamp );
3324
+ texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
3288
3325
  texture.needsUpdate = true;
3289
3326
 
3290
3327
  if ( onLoad !== undefined ) {
@@ -3302,10 +3339,10 @@
3302
3339
  parse( text ) {
3303
3340
 
3304
3341
  const iesLamp = new IESLamp( text );
3305
- const texture = new three.DataTexture( null, 360, 180, three.RedFormat, three.FloatType );
3342
+ const texture = new three.DataTexture( null, 360, 180, three.RedFormat, three.HalfFloatType );
3306
3343
  texture.minFilter = three.LinearFilter;
3307
3344
  texture.magFilter = three.LinearFilter;
3308
- texture.image.data = this._getIESValues( iesLamp );
3345
+ texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
3309
3346
  texture.needsUpdate = true;
3310
3347
 
3311
3348
  return texture;
@@ -3323,7 +3360,7 @@
3323
3360
 
3324
3361
  const tex = this.texture;
3325
3362
  tex.format = three.RGBAFormat;
3326
- tex.type = three.FloatType;
3363
+ tex.type = three.HalfFloatType;
3327
3364
  tex.minFilter = three.LinearFilter;
3328
3365
  tex.magFilter = three.LinearFilter;
3329
3366
  tex.wrapS = three.ClampToEdgeWrapping;
@@ -3574,7 +3611,7 @@
3574
3611
  this.renderer = renderer;
3575
3612
  this.pmremGenerator = new three.PMREMGenerator( renderer );
3576
3613
  this.copyQuad = new Pass_js.FullScreenQuad( new PMREMCopyMaterial() );
3577
- this.renderTarget = new three.WebGLRenderTarget( 1, 1, { type: three.FloatType, format: three.RGBAFormat } );
3614
+ this.renderTarget = new three.WebGLRenderTarget( 1, 1, { type: three.HalfFloatType, format: three.RGBAFormat } );
3578
3615
 
3579
3616
  }
3580
3617
 
@@ -3611,10 +3648,10 @@
3611
3648
  renderer.autoClear = prevClear;
3612
3649
 
3613
3650
  // read the data back
3614
- const buffer = new Float32Array( width * height * 4 );
3651
+ const buffer = new Uint16Array( width * height * 4 );
3615
3652
  renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, buffer );
3616
3653
 
3617
- const result = new three.DataTexture( buffer, width, height, three.RGBAFormat, three.FloatType );
3654
+ const result = new three.DataTexture( buffer, width, height, three.RGBAFormat, three.HalfFloatType );
3618
3655
  result.minFilter = texture.minFilter;
3619
3656
  result.magFilter = texture.magFilter;
3620
3657
  result.wrapS = texture.wrapS;
@@ -3756,7 +3793,7 @@
3756
3793
 
3757
3794
  gl_FragColor = smartDeNoise( map, vec2( vUv.x, vUv.y ), sigma, kSigma, threshold );
3758
3795
  #include <tonemapping_fragment>
3759
- #include <encodings_fragment>
3796
+ #include <colorspace_fragment>
3760
3797
  #include <premultiplied_alpha_fragment>
3761
3798
 
3762
3799
  }
@@ -3839,7 +3876,7 @@
3839
3876
  gl_FragColor.rgb = vec3( mix( minColor, maxColor, t ) );
3840
3877
  gl_FragColor.a = 1.0;
3841
3878
 
3842
- #include <encodings_fragment>
3879
+ #include <colorspace_fragment>
3843
3880
 
3844
3881
  }`,
3845
3882
 
@@ -4048,7 +4085,7 @@
4048
4085
 
4049
4086
  }
4050
4087
 
4051
- #include <encodings_fragment>
4088
+ #include <colorspace_fragment>
4052
4089
 
4053
4090
  }
4054
4091
 
@@ -4390,7 +4427,18 @@
4390
4427
 
4391
4428
  l.coneCos = s5.r;
4392
4429
  l.penumbraCos = s5.g;
4393
- l.iesProfile = int( round ( s5.b ) );
4430
+ l.iesProfile = int( round( s5.b ) );
4431
+
4432
+ } else {
4433
+
4434
+ l.radius = 0.0;
4435
+ l.near = 0.0;
4436
+ l.decay = 0.0;
4437
+ l.distance = 0.0;
4438
+
4439
+ l.coneCos = 0.0;
4440
+ l.penumbraCos = 0.0;
4441
+ l.iesProfile = - 1;
4394
4442
 
4395
4443
  }
4396
4444
 
@@ -4586,21 +4634,22 @@
4586
4634
 
4587
4635
  uint firstTextureTransformIdx = i + 15u;
4588
4636
 
4589
- m.mapTransform = m.map == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx );
4590
- m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
4591
- m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
4592
- m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
4593
- m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
4594
- m.normalMapTransform = m.normalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
4595
- m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
4596
- m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
4597
- m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
4598
- m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
4599
- m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
4600
- m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
4601
- m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
4602
- m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
4603
- m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
4637
+ // mat3( 1.0 ) is an identity matrix
4638
+ m.mapTransform = m.map == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx );
4639
+ m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
4640
+ m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
4641
+ m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
4642
+ m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
4643
+ m.normalMapTransform = m.normalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
4644
+ m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
4645
+ m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
4646
+ m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
4647
+ m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
4648
+ m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
4649
+ m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
4650
+ m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
4651
+ m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
4652
+ m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
4604
4653
 
4605
4654
  return m;
4606
4655
 
@@ -4625,9 +4674,9 @@ bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
4625
4674
 
4626
4675
  // returns true if we're within the first fog volume we hit
4627
4676
  bool bvhIntersectFogVolumeHit(
4628
- BVH bvh, vec3 rayOrigin, vec3 rayDirection,
4677
+ vec3 rayOrigin, vec3 rayDirection,
4629
4678
  usampler2D materialIndexAttribute, sampler2D materials,
4630
- out Material material
4679
+ inout Material material
4631
4680
  ) {
4632
4681
 
4633
4682
  material.fogVolume = false;
@@ -5087,7 +5136,7 @@ bool bvhIntersectFogVolumeHit(
5087
5136
  ${ iridescenceGLSL }
5088
5137
 
5089
5138
  // diffuse
5090
- float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
5139
+ float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
5091
5140
 
5092
5141
  // https://schuttejoe.github.io/post/disneybsdf/
5093
5142
  float fl = schlickFresnel( wi.z, 0.0 );
@@ -5118,7 +5167,7 @@ bool bvhIntersectFogVolumeHit(
5118
5167
  }
5119
5168
 
5120
5169
  // specular
5121
- float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
5170
+ float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
5122
5171
 
5123
5172
  // if roughness is set to 0 then D === NaN which results in black pixels
5124
5173
  float metalness = surf.metalness;
@@ -5165,7 +5214,7 @@ bool bvhIntersectFogVolumeHit(
5165
5214
 
5166
5215
  // transmission
5167
5216
  /*
5168
- float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
5217
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
5169
5218
 
5170
5219
  // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
5171
5220
 
@@ -5208,7 +5257,7 @@ bool bvhIntersectFogVolumeHit(
5208
5257
 
5209
5258
  // TODO: This is just using a basic cosine-weighted specular distribution with an
5210
5259
  // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
5211
- float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
5260
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
5212
5261
 
5213
5262
  color = surf.transmission * surf.color;
5214
5263
 
@@ -5299,7 +5348,7 @@ bool bvhIntersectFogVolumeHit(
5299
5348
  // bsdf
5300
5349
  void getLobeWeights(
5301
5350
  vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
5302
- out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight
5351
+ inout float diffuseWeight, inout float specularWeight, inout float transmissionWeight, inout float clearcoatWeight
5303
5352
  ) {
5304
5353
 
5305
5354
  float metalness = surf.metalness;
@@ -5323,7 +5372,7 @@ bool bvhIntersectFogVolumeHit(
5323
5372
 
5324
5373
  float bsdfEval(
5325
5374
  vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
5326
- float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, out float specularPdf, out vec3 color
5375
+ float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, inout float specularPdf, inout vec3 color
5327
5376
  ) {
5328
5377
 
5329
5378
  float metalness = surf.metalness;
@@ -5386,7 +5435,7 @@ bool bvhIntersectFogVolumeHit(
5386
5435
 
5387
5436
  }
5388
5437
 
5389
- float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, out vec3 color ) {
5438
+ float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, inout vec3 color ) {
5390
5439
 
5391
5440
  if ( surf.volumeParticle ) {
5392
5441
 
@@ -5554,14 +5603,14 @@ bool bvhIntersectFogVolumeHit(
5554
5603
  }
5555
5604
 
5556
5605
  // samples the color given env map with CDF and returns the pdf of the direction
5557
- float sampleEquirect( EquirectHdrInfo info, vec3 direction, out vec3 color ) {
5606
+ float sampleEquirect( vec3 direction, inout vec3 color ) {
5558
5607
 
5559
5608
  vec2 uv = equirectDirectionToUv( direction );
5560
- color = texture2D( info.map, uv ).rgb;
5609
+ color = texture2D( envMapInfo.map, uv ).rgb;
5561
5610
 
5562
- float totalSum = info.totalSum;
5611
+ float totalSum = envMapInfo.totalSum;
5563
5612
  float lum = luminance( color );
5564
- ivec2 resolution = textureSize( info.map, 0 );
5613
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
5565
5614
  float pdf = lum / totalSum;
5566
5615
 
5567
5616
  return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
@@ -5569,20 +5618,20 @@ bool bvhIntersectFogVolumeHit(
5569
5618
  }
5570
5619
 
5571
5620
  // samples a direction of the envmap with color and retrieves pdf
5572
- float sampleEquirectProbability( EquirectHdrInfo info, vec2 r, out vec3 color, out vec3 direction ) {
5621
+ float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
5573
5622
 
5574
5623
  // sample env map cdf
5575
- float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
5576
- float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
5624
+ float v = texture2D( envMapInfo.marginalWeights, vec2( r.x, 0.0 ) ).x;
5625
+ float u = texture2D( envMapInfo.conditionalWeights, vec2( r.y, v ) ).x;
5577
5626
  vec2 uv = vec2( u, v );
5578
5627
 
5579
5628
  vec3 derivedDirection = equirectUvToDirection( uv );
5580
5629
  direction = derivedDirection;
5581
- color = texture2D( info.map, uv ).rgb;
5630
+ color = texture2D( envMapInfo.map, uv ).rgb;
5582
5631
 
5583
- float totalSum = info.totalSum;
5632
+ float totalSum = envMapInfo.totalSum;
5584
5633
  float lum = luminance( color );
5585
- ivec2 resolution = textureSize( info.map, 0 );
5634
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
5586
5635
  float pdf = lum / totalSum;
5587
5636
 
5588
5637
  return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
@@ -5635,24 +5684,17 @@ bool bvhIntersectFogVolumeHit(
5635
5684
 
5636
5685
  };
5637
5686
 
5638
- bool lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection, out LightRecord lightRec ) {
5687
+ bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
5639
5688
 
5640
5689
  bool didHit = false;
5641
- uint l;
5642
- for ( l = 0u; l < lightCount; l ++ ) {
5643
-
5644
- Light light = readLightInfo( lights, l );
5645
-
5646
- vec3 u = light.u;
5647
- vec3 v = light.v;
5648
-
5649
- // check for backface
5650
- vec3 normal = normalize( cross( u, v ) );
5651
- if ( dot( normal, rayDirection ) < 0.0 ) {
5690
+ Light light = readLightInfo( lights, l );
5652
5691
 
5653
- continue;
5692
+ vec3 u = light.u;
5693
+ vec3 v = light.v;
5654
5694
 
5655
- }
5695
+ // check for backface
5696
+ vec3 normal = normalize( cross( u, v ) );
5697
+ if ( dot( normal, rayDirection ) > 0.0 ) {
5656
5698
 
5657
5699
  u *= 1.0 / dot( u, u );
5658
5700
  v *= 1.0 / dot( v, v );
@@ -5665,17 +5707,13 @@ bool bvhIntersectFogVolumeHit(
5665
5707
  ( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
5666
5708
  ) {
5667
5709
 
5668
- if ( ! didHit || dist < lightRec.dist ) {
5669
-
5670
- float cosTheta = dot( rayDirection, normal );
5671
- didHit = true;
5672
- lightRec.dist = dist;
5673
- lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
5674
- lightRec.emission = light.color * light.intensity;
5675
- lightRec.direction = rayDirection;
5676
- lightRec.type = light.type;
5677
-
5678
- }
5710
+ float cosTheta = dot( rayDirection, normal );
5711
+ didHit = true;
5712
+ lightRec.dist = dist;
5713
+ lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
5714
+ lightRec.emission = light.color * light.intensity;
5715
+ lightRec.direction = rayDirection;
5716
+ lightRec.type = light.type;
5679
5717
 
5680
5718
  }
5681
5719
 
@@ -5687,11 +5725,6 @@ bool bvhIntersectFogVolumeHit(
5687
5725
 
5688
5726
  LightRecord randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
5689
5727
 
5690
- LightRecord lightRec;
5691
- lightRec.type = light.type;
5692
-
5693
- lightRec.emission = light.color * light.intensity;
5694
-
5695
5728
  vec3 randomPos;
5696
5729
  if( light.type == RECT_AREA_LIGHT_TYPE ) {
5697
5730
 
@@ -5712,12 +5745,17 @@ bool bvhIntersectFogVolumeHit(
5712
5745
 
5713
5746
  vec3 toLight = randomPos - rayOrigin;
5714
5747
  float lightDistSq = dot( toLight, toLight );
5715
- lightRec.dist = sqrt( lightDistSq );
5748
+ float dist = sqrt( lightDistSq );
5749
+ vec3 direction = toLight / dist;
5750
+ vec3 lightNormal = normalize( cross( light.u, light.v ) );
5716
5751
 
5717
- vec3 direction = toLight / lightRec.dist;
5752
+ LightRecord lightRec;
5753
+ lightRec.type = light.type;
5754
+ lightRec.emission = light.color * light.intensity;
5755
+ lightRec.dist = dist;
5718
5756
  lightRec.direction = direction;
5719
5757
 
5720
- vec3 lightNormal = normalize( cross( light.u, light.v ) );
5758
+ // TODO: the denominator is potentially zero
5721
5759
  lightRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
5722
5760
 
5723
5761
  return lightRec;
@@ -5765,13 +5803,15 @@ bool bvhIntersectFogVolumeHit(
5765
5803
 
5766
5804
  LightRecord randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
5767
5805
 
5806
+ LightRecord result;
5807
+
5768
5808
  // pick a random light
5769
5809
  uint l = uint( ruv.x * float( lightCount ) );
5770
5810
  Light light = readLightInfo( lights, l );
5771
5811
 
5772
5812
  if ( light.type == SPOT_LIGHT_TYPE ) {
5773
5813
 
5774
- return randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
5814
+ result = randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
5775
5815
 
5776
5816
  } else if ( light.type == POINT_LIGHT_TYPE ) {
5777
5817
 
@@ -5791,7 +5831,7 @@ bool bvhIntersectFogVolumeHit(
5791
5831
  rec.pdf = 1.0;
5792
5832
  rec.emission = light.color * light.intensity * distanceFalloff;
5793
5833
  rec.type = light.type;
5794
- return rec;
5834
+ result = rec;
5795
5835
 
5796
5836
  } else if ( light.type == DIR_LIGHT_TYPE ) {
5797
5837
 
@@ -5802,15 +5842,17 @@ bool bvhIntersectFogVolumeHit(
5802
5842
  rec.emission = light.color * light.intensity;
5803
5843
  rec.type = light.type;
5804
5844
 
5805
- return rec;
5845
+ result = rec;
5806
5846
 
5807
5847
  } else {
5808
5848
 
5809
5849
  // sample the light
5810
- return randomAreaLightSample( light, rayOrigin, ruv.yz );
5850
+ result = randomAreaLightSample( light, rayOrigin, ruv.yz );
5811
5851
 
5812
5852
  }
5813
5853
 
5854
+ return result;
5855
+
5814
5856
  }
5815
5857
 
5816
5858
  `;
@@ -5907,7 +5949,7 @@ bool bvhIntersectFogVolumeHit(
5907
5949
  // Finds the point where the ray intersects the plane defined by u and v and checks if this point
5908
5950
  // falls in the bounds of the rectangle on that same plane.
5909
5951
  // Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
5910
- bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
5952
+ bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
5911
5953
 
5912
5954
  float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
5913
5955
 
@@ -5938,7 +5980,7 @@ bool bvhIntersectFogVolumeHit(
5938
5980
 
5939
5981
  // Finds the point where the ray intersects the plane defined by u and v and checks if this point
5940
5982
  // falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
5941
- bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
5983
+ bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
5942
5984
 
5943
5985
  float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
5944
5986
 
@@ -6370,7 +6412,7 @@ bool bvhIntersectFogVolumeHit(
6370
6412
  // step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
6371
6413
  // returns true if a solid surface was hit
6372
6414
  bool attenuateHit(
6373
- BVH bvh, RenderState state,
6415
+ RenderState state,
6374
6416
  Ray ray, float rayDist,
6375
6417
  out vec3 color
6376
6418
  ) {
@@ -6384,7 +6426,6 @@ bool bvhIntersectFogVolumeHit(
6384
6426
 
6385
6427
  // hit results
6386
6428
  SurfaceHit surfaceHit;
6387
- LightRecord lightRec;
6388
6429
 
6389
6430
  color = vec3( 1.0 );
6390
6431
 
@@ -6392,20 +6433,12 @@ bool bvhIntersectFogVolumeHit(
6392
6433
  // and then reset.
6393
6434
  for ( int i = 0; i < traversals; i ++ ) {
6394
6435
 
6395
- int hitType = traceScene(
6396
- ray, bvh, lights, fogMaterial,
6397
- surfaceHit, lightRec
6398
- );
6436
+ int hitType = traceScene( ray, fogMaterial, surfaceHit );
6399
6437
 
6400
6438
  if ( hitType == FOG_HIT ) {
6401
6439
 
6402
6440
  return true;
6403
6441
 
6404
- } else if ( hitType == LIGHT_HIT ) {
6405
-
6406
- float totalDist = distance( startPoint, ray.origin + ray.direction * lightRec.dist );
6407
- return totalDist < rayDist - max( totalDist, rayDist ) * 1e-4;
6408
-
6409
6442
  } else if ( hitType == SURFACE_HIT ) {
6410
6443
 
6411
6444
  float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
@@ -6550,22 +6583,26 @@ bool bvhIntersectFogVolumeHit(
6550
6583
  #define LIGHT_HIT 2
6551
6584
  #define FOG_HIT 3
6552
6585
 
6586
+ // Passing the global variable 'lights' into this function caused shader program errors.
6587
+ // So global variables like 'lights' and 'bvh' were moved out of the function parameters.
6588
+ // For more information, refer to: https://github.com/gkjohnson/three-gpu-pathtracer/pull/457
6553
6589
  int traceScene(
6554
6590
 
6555
- Ray ray, BVH bvh, LightsInfo lights, Material fogMaterial,
6556
- out SurfaceHit surfaceHit, out LightRecord lightRec
6591
+ Ray ray, Material fogMaterial, inout SurfaceHit surfaceHit
6557
6592
 
6558
6593
  ) {
6559
6594
 
6595
+ int result = NO_HIT;
6560
6596
  bool hit = bvhIntersectFirstHit( bvh, ray.origin, ray.direction, surfaceHit.faceIndices, surfaceHit.faceNormal, surfaceHit.barycoord, surfaceHit.side, surfaceHit.dist );
6561
- bool lightHit = lightsClosestHit( lights.tex, lights.count, ray.origin, ray.direction, lightRec );
6562
6597
 
6563
6598
  #if FEATURE_FOG
6564
6599
 
6565
6600
  if ( fogMaterial.fogVolume ) {
6566
6601
 
6602
+ // offset the distance so we don't run into issues with particles on the same surface
6603
+ // as other objects
6567
6604
  float particleDist = intersectFogVolume( fogMaterial, sobol( 1 ) );
6568
- if ( particleDist + 1e-4 < surfaceHit.dist && ( particleDist + 1e-4 < lightRec.dist || ! lightHit ) ) {
6605
+ if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
6569
6606
 
6570
6607
  surfaceHit.side = 1.0;
6571
6608
  surfaceHit.faceNormal = normalize( - ray.direction );
@@ -6578,19 +6615,13 @@ bool bvhIntersectFogVolumeHit(
6578
6615
 
6579
6616
  #endif
6580
6617
 
6581
- if ( lightHit && ( lightRec.dist < surfaceHit.dist || ! hit ) ) {
6582
-
6583
- return LIGHT_HIT;
6584
-
6585
- }
6586
-
6587
6618
  if ( hit ) {
6588
6619
 
6589
- return SURFACE_HIT;
6620
+ result = SURFACE_HIT;
6590
6621
 
6591
6622
  }
6592
6623
 
6593
- return NO_HIT;
6624
+ return result;
6594
6625
 
6595
6626
  }
6596
6627
 
@@ -6603,7 +6634,7 @@ bool bvhIntersectFogVolumeHit(
6603
6634
  int getSurfaceRecord(
6604
6635
  Material material, SurfaceHit surfaceHit, sampler2DArray attributesArray,
6605
6636
  float accumulatedRoughness,
6606
- out SurfaceRecord surf
6637
+ inout SurfaceRecord surf
6607
6638
  ) {
6608
6639
 
6609
6640
  if ( material.fogVolume ) {
@@ -6923,6 +6954,8 @@ bool bvhIntersectFogVolumeHit(
6923
6954
 
6924
6955
  vec3 directLightContribution( vec3 worldWo, SurfaceRecord surf, RenderState state, vec3 rayOrigin ) {
6925
6956
 
6957
+ vec3 result = vec3( 0.0 );
6958
+
6926
6959
  // uniformly pick a light or environment map
6927
6960
  if( lightsDenom != 0.0 && sobol( 5 ) < float( lights.count ) / lightsDenom ) {
6928
6961
 
@@ -6944,7 +6977,7 @@ bool bvhIntersectFogVolumeHit(
6944
6977
  if (
6945
6978
  lightRec.pdf > 0.0 &&
6946
6979
  isDirectionValid( lightRec.direction, surf.normal, surf.faceNormal ) &&
6947
- ! attenuateHit( bvh, state, lightRay, lightRec.dist, attenuatedColor )
6980
+ ! attenuateHit( state, lightRay, lightRec.dist, attenuatedColor )
6948
6981
  ) {
6949
6982
 
6950
6983
  // get the material pdf
@@ -6956,7 +6989,7 @@ bool bvhIntersectFogVolumeHit(
6956
6989
  // weight the direct light contribution
6957
6990
  float lightPdf = lightRec.pdf / lightsDenom;
6958
6991
  float misWeight = lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
6959
- return attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
6992
+ result = attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
6960
6993
 
6961
6994
  }
6962
6995
 
@@ -6966,7 +6999,7 @@ bool bvhIntersectFogVolumeHit(
6966
6999
 
6967
7000
  // find a sample in the environment map to include in the contribution
6968
7001
  vec3 envColor, envDirection;
6969
- float envPdf = sampleEquirectProbability( envMapInfo, sobol2( 7 ), envColor, envDirection );
7002
+ float envPdf = sampleEquirectProbability( sobol2( 7 ), envColor, envDirection );
6970
7003
  envDirection = invEnvRotation3x3 * envDirection;
6971
7004
 
6972
7005
  // this env sampling is not set up for transmissive sampling and yields overly bright
@@ -6987,7 +7020,7 @@ bool bvhIntersectFogVolumeHit(
6987
7020
  if (
6988
7021
  envPdf > 0.0 &&
6989
7022
  isDirectionValid( envDirection, surf.normal, surf.faceNormal ) &&
6990
- ! attenuateHit( bvh, state, envRay, INFINITY, attenuatedColor )
7023
+ ! attenuateHit( state, envRay, INFINITY, attenuatedColor )
6991
7024
  ) {
6992
7025
 
6993
7026
  // get the material pdf
@@ -6999,7 +7032,7 @@ bool bvhIntersectFogVolumeHit(
6999
7032
  // weight the direct light contribution
7000
7033
  envPdf /= lightsDenom;
7001
7034
  float misWeight = misHeuristic( envPdf, envMaterialPdf );
7002
- return attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
7035
+ result = attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
7003
7036
 
7004
7037
  }
7005
7038
 
@@ -7007,7 +7040,9 @@ bool bvhIntersectFogVolumeHit(
7007
7040
 
7008
7041
  }
7009
7042
 
7010
- return vec3( 0.0 );
7043
+ // Function changed to have a single return statement to potentially help with crashes on Mac OS.
7044
+ // See issue #470
7045
+ return result;
7011
7046
 
7012
7047
  }
7013
7048
 
@@ -7036,6 +7071,7 @@ bool bvhIntersectFogVolumeHit(
7036
7071
  FEATURE_DOF: 1,
7037
7072
  FEATURE_BACKGROUND_MAP: 0,
7038
7073
  FEATURE_FOG: 1,
7074
+ FEATURE_SOBOL: 0,
7039
7075
  // 0 = Perspective
7040
7076
  // 1 = Orthographic
7041
7077
  // 2 = Equirectangular
@@ -7105,13 +7141,36 @@ bool bvhIntersectFogVolumeHit(
7105
7141
  #include <common>
7106
7142
 
7107
7143
  // bvh intersection
7108
- ${ threeMeshBvh.shaderStructs }
7109
- ${ threeMeshBvh.shaderIntersectFunction }
7144
+ ${ threeMeshBvh.BVHShaderGLSL.common_functions }
7145
+ ${ threeMeshBvh.BVHShaderGLSL.bvh_struct_definitions }
7146
+ ${ threeMeshBvh.BVHShaderGLSL.bvh_ray_functions }
7147
+
7148
+ // uniform structs
7149
+ ${ cameraStructGLSL }
7150
+ ${ lightsStructGLSL }
7151
+ ${ equirectStructGLSL }
7152
+ ${ materialStructGLSL }
7110
7153
 
7111
7154
  // random
7112
7155
  ${ pcgGLSL }
7113
- ${ sobolCommonGLSL }
7114
- ${ sobolSamplingGLSL }
7156
+ #if FEATURE_SOBOL
7157
+
7158
+ ${ sobolCommonGLSL }
7159
+ ${ sobolSamplingGLSL }
7160
+
7161
+ #else
7162
+
7163
+ // Using the sobol functions seems to break the the compiler on MacOS
7164
+ // - specifically the "sobolReverseBits" function.
7165
+ uint sobolPixelIndex = 0u;
7166
+ uint sobolPathIndex = 0u;
7167
+ uint sobolBounceIndex = 0u;
7168
+ float sobol( int v ) { return rand(); }
7169
+ vec2 sobol2( int v ) { return rand2(); }
7170
+ vec3 sobol3( int v ) { return rand3(); }
7171
+ vec4 sobol4( int v ) { return rand4(); }
7172
+
7173
+ #endif
7115
7174
 
7116
7175
  // common
7117
7176
  ${ arraySamplerTexelFetchGLSL }
@@ -7120,20 +7179,6 @@ bool bvhIntersectFogVolumeHit(
7120
7179
  ${ mathGLSL }
7121
7180
  ${ intersectShapesGLSL }
7122
7181
 
7123
- // uniform structs
7124
- ${ cameraStructGLSL }
7125
- ${ lightsStructGLSL }
7126
- ${ equirectStructGLSL }
7127
- ${ materialStructGLSL }
7128
- ${ fogMaterialBvhGLSL }
7129
-
7130
- // sampling
7131
- ${ shapeSamplingGLSL }
7132
- ${ bsdfSamplingGLSL }
7133
- ${ equirectSamplingGLSL }
7134
- ${ lightSamplingGLSL }
7135
- ${ fogGLSL }
7136
-
7137
7182
  // environment
7138
7183
  uniform EquirectHdrInfo envMapInfo;
7139
7184
  uniform mat4 environmentRotation;
@@ -7185,6 +7230,14 @@ bool bvhIntersectFogVolumeHit(
7185
7230
  mat3 invEnvRotation3x3;
7186
7231
  float lightsDenom;
7187
7232
 
7233
+ // sampling
7234
+ ${ fogMaterialBvhGLSL }
7235
+ ${ shapeSamplingGLSL }
7236
+ ${ bsdfSamplingGLSL }
7237
+ ${ equirectSamplingGLSL }
7238
+ ${ lightSamplingGLSL }
7239
+ ${ fogGLSL }
7240
+
7188
7241
  float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
7189
7242
 
7190
7243
  return clamp(
@@ -7224,7 +7277,7 @@ bool bvhIntersectFogVolumeHit(
7224
7277
 
7225
7278
  // init
7226
7279
  rng_initialize( gl_FragCoord.xy, seed );
7227
- sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
7280
+ sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
7228
7281
  sobolPathIndex = uint( seed );
7229
7282
 
7230
7283
  // get camera ray
@@ -7240,7 +7293,6 @@ bool bvhIntersectFogVolumeHit(
7240
7293
 
7241
7294
  // surface results
7242
7295
  SurfaceHit surfaceHit;
7243
- LightRecord lightRec;
7244
7296
  ScatterRecord scatterRec;
7245
7297
 
7246
7298
  // path tracing state
@@ -7249,7 +7301,7 @@ bool bvhIntersectFogVolumeHit(
7249
7301
  #if FEATURE_FOG
7250
7302
 
7251
7303
  state.fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
7252
- bvh, ray.origin, - ray.direction,
7304
+ ray.origin, - ray.direction,
7253
7305
  materialIndexAttribute, materials,
7254
7306
  state.fogMaterial
7255
7307
  );
@@ -7264,44 +7316,42 @@ bool bvhIntersectFogVolumeHit(
7264
7316
  state.traversals = bounces - i;
7265
7317
  state.firstRay = i == 0 && state.transmissiveTraversals == transmissiveBounces;
7266
7318
 
7267
- int hitType = traceScene(
7268
- ray, bvh, lights, state.fogMaterial,
7269
- surfaceHit, lightRec
7270
- );
7319
+ int hitType = traceScene( ray, state.fogMaterial, surfaceHit );
7271
7320
 
7272
- if ( hitType == LIGHT_HIT ) {
7321
+ // check if we intersect any lights and accumulate the light contribution
7322
+ // TODO: we can add support for light surface rendering in the else condition if we
7323
+ // add the ability to toggle visibility of the the light
7324
+ if ( ! state.firstRay && ! state.transmissiveRay ) {
7273
7325
 
7274
- if ( state.firstRay || state.transmissiveRay ) {
7326
+ LightRecord lightRec;
7327
+ float lightDist = hitType == NO_HIT ? INFINITY : surfaceHit.dist;
7328
+ for ( uint i = 0u; i < lights.count; i ++ ) {
7275
7329
 
7276
- gl_FragColor.rgb += lightRec.emission * state.throughputColor;
7330
+ if (
7331
+ intersectLightAtIndex( lights.tex, ray.origin, ray.direction, i, lightRec ) &&
7332
+ lightRec.dist < lightDist
7333
+ ) {
7277
7334
 
7278
- } else {
7279
-
7280
- #if FEATURE_MIS
7281
-
7282
- // NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
7283
- if ( lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ) {
7284
-
7285
- gl_FragColor.rgb += lightRec.emission * state.throughputColor;
7286
-
7287
- } else {
7335
+ #if FEATURE_MIS
7288
7336
 
7289
7337
  // weight the contribution
7338
+ // NOTE: Only area lights are supported for forward sampling and can be hit
7290
7339
  float misWeight = misHeuristic( scatterRec.pdf, lightRec.pdf / lightsDenom );
7291
7340
  gl_FragColor.rgb += lightRec.emission * state.throughputColor * misWeight;
7292
7341
 
7293
- }
7342
+ #else
7294
7343
 
7295
- #else
7344
+ gl_FragColor.rgb += lightRec.emission * state.throughputColor;
7296
7345
 
7297
- gl_FragColor.rgb += lightRec.emission * state.throughputColor;
7346
+ #endif
7298
7347
 
7299
- #endif
7348
+ }
7300
7349
 
7301
7350
  }
7302
- break;
7303
7351
 
7304
- } else if ( hitType == NO_HIT ) {
7352
+ }
7353
+
7354
+ if ( hitType == NO_HIT ) {
7305
7355
 
7306
7356
  if ( state.firstRay || state.transmissiveRay ) {
7307
7357
 
@@ -7314,7 +7364,7 @@ bool bvhIntersectFogVolumeHit(
7314
7364
 
7315
7365
  // get the PDF of the hit envmap point
7316
7366
  vec3 envColor;
7317
- float envPdf = sampleEquirect( envMapInfo, envRotation3x3 * ray.direction, envColor );
7367
+ float envPdf = sampleEquirect( envRotation3x3 * ray.direction, envColor );
7318
7368
  envPdf /= lightsDenom;
7319
7369
 
7320
7370
  // and weight the contribution