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