three-gpu-pathtracer 0.0.21 → 0.0.23

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.
@@ -1,4 +1,4 @@
1
- import { BufferAttribute, BufferGeometry, Matrix4, Vector3, Vector4, Matrix3, MeshBasicMaterial, Mesh, ShaderMaterial, NoBlending, Vector2, WebGLRenderTarget, FloatType, RGBAFormat, NearestFilter, PerspectiveCamera, DataUtils, HalfFloatType, Source, DataTexture, LinearFilter, RepeatWrapping, RedFormat, ClampToEdgeWrapping, Quaternion, DataArrayTexture, DoubleSide, BackSide, FrontSide, Color, WebGLArrayRenderTarget, UnsignedByteType, NoToneMapping, RGFormat, NormalBlending, Spherical, EquirectangularReflectionMapping, Clock, Scene, AdditiveBlending, Camera, SpotLight, RectAreaLight, PMREMGenerator, MeshStandardMaterial } from 'three';
1
+ import { BufferAttribute, BufferGeometry, Matrix4, Vector3, Vector4, Matrix3, MeshBasicMaterial, Mesh, ShaderMaterial, NoBlending, Vector2, WebGLRenderTarget, FloatType, RGBAFormat, NearestFilter, PerspectiveCamera, DataUtils, HalfFloatType, Source, DataTexture, LinearFilter, RepeatWrapping, RedFormat, ClampToEdgeWrapping, Quaternion, DataArrayTexture, DoubleSide, BackSide, FrontSide, Color, WebGLArrayRenderTarget, UnsignedByteType, NoToneMapping, RGFormat, NormalBlending, Spherical, EquirectangularReflectionMapping, LinearMipMapLinearFilter, Clock, Scene, AdditiveBlending, Camera, SpotLight, RectAreaLight, PMREMGenerator, MeshStandardMaterial } from 'three';
2
2
  import { SAH, MeshBVH, FloatVertexAttributeTexture, MeshBVHUniformStruct, UIntVertexAttributeTexture, BVHShaderGLSL } from 'three-mesh-bvh';
3
3
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
4
4
 
@@ -43,12 +43,18 @@ function createAttributeClone( attr, countOverride = null ) {
43
43
 
44
44
  }
45
45
 
46
- // Confirms that the two provided attributes are compatible
46
+ // Confirms that the two provided attributes are compatible. Returns false if they are not.
47
47
  function validateAttributes( attr1, attr2 ) {
48
48
 
49
49
  if ( ! attr1 && ! attr2 ) {
50
50
 
51
- return;
51
+ return true;
52
+
53
+ }
54
+
55
+ if ( Boolean( attr1 ) !== Boolean( attr2 ) ) {
56
+
57
+ return false;
52
58
 
53
59
  }
54
60
 
@@ -59,10 +65,12 @@ function validateAttributes( attr1, attr2 ) {
59
65
 
60
66
  if ( ! sameCount || ! sameNormalized || ! sameType || ! sameItemSize ) {
61
67
 
62
- throw new Error();
68
+ return false;
63
69
 
64
70
  }
65
71
 
72
+ return true;
73
+
66
74
  }
67
75
 
68
76
  function validateMergeability( geometries ) {
@@ -430,29 +438,22 @@ function bufferToHash( buffer ) {
430
438
 
431
439
  }
432
440
 
433
- function attributeSort( a, b ) {
434
-
435
- if ( a.uuid > b.uuid ) return 1;
436
- if ( a.uuid < b.uuid ) return - 1;
437
- return 0;
438
-
439
- }
440
-
441
441
  function getGeometryHash( geometry ) {
442
442
 
443
- let hash = '';
443
+ let hash = geometry.uuid;
444
444
  const attributes = Object.values( geometry.attributes );
445
445
  if ( geometry.index ) {
446
446
 
447
447
  attributes.push( geometry.index );
448
+ hash += `index|${ geometry.index.version }`;
448
449
 
449
450
  }
450
451
 
451
- attributes.sort( attributeSort );
452
-
453
- for ( const attr of attributes ) {
452
+ const keys = Object.keys( attributes ).sort();
453
+ for ( const key of keys ) {
454
454
 
455
- hash += `${ attr.uuid }_${ attr.version }|`;
455
+ const attr = attributes[ key ];
456
+ hash += `${ key }_${ attr.version }|`;
456
457
 
457
458
  }
458
459
 
@@ -883,6 +884,28 @@ class BakedGeometry extends BufferGeometry {
883
884
 
884
885
  }
885
886
 
887
+ // returns whether the passed mesh is compatible with this baked geometry
888
+ // such that it can be updated without resizing attributes
889
+ isCompatible( mesh, attributes ) {
890
+
891
+ const geometry = mesh.geometry;
892
+ for ( let i = 0; i < attributes.length; i ++ ) {
893
+
894
+ const key = attributes[ i ];
895
+ const attr1 = geometry.attributes[ key ];
896
+ const attr2 = this.attributes[ key ];
897
+ if ( attr1 && ! validateAttributes( attr1, attr2 ) ) {
898
+
899
+ return false;
900
+
901
+ }
902
+
903
+ }
904
+
905
+ return true;
906
+
907
+ }
908
+
886
909
  updateFrom( mesh, options ) {
887
910
 
888
911
  const diff = this._diff;
@@ -914,7 +937,7 @@ function flatTraverseMeshes( objects, cb ) {
914
937
  for ( let i = 0, l = objects.length; i < l; i ++ ) {
915
938
 
916
939
  const object = objects[ i ];
917
- object.traverse( o => {
940
+ object.traverseVisible( o => {
918
941
 
919
942
  if ( o.isMesh ) {
920
943
 
@@ -1072,15 +1095,24 @@ class StaticGeometryGenerator {
1072
1095
  unusedMeshKeys.delete( meshKey );
1073
1096
 
1074
1097
  // initialize the intermediate geometry
1075
- if ( ! _intermediateGeometry.has( meshKey ) ) {
1098
+ // if the mesh and source geometry have changed in such a way that they are no longer
1099
+ // compatible then regenerate the baked geometry from scratch
1100
+ let geom = _intermediateGeometry.get( meshKey );
1101
+ if ( ! geom || ! geom.isCompatible( mesh, this.attributes ) ) {
1102
+
1103
+ if ( geom ) {
1104
+
1105
+ geom.dispose();
1106
+
1107
+ }
1076
1108
 
1077
- _intermediateGeometry.set( meshKey, new BakedGeometry() );
1109
+ geom = new BakedGeometry();
1110
+ _intermediateGeometry.set( meshKey, geom );
1078
1111
 
1079
1112
  }
1080
1113
 
1081
1114
  // transform the geometry into the intermediate buffer geometry, saving whether
1082
1115
  // or not it changed.
1083
- const geom = _intermediateGeometry.get( meshKey );
1084
1116
  if ( geom.updateFrom( mesh, convertOptions ) ) {
1085
1117
 
1086
1118
  // TODO: provide option for only generating the set of attributes that are present
@@ -1132,6 +1164,12 @@ class StaticGeometryGenerator {
1132
1164
 
1133
1165
  // get the list of geometries to merge
1134
1166
  let forceUpdate = false;
1167
+ if ( meshes.length !== previousMergeInfo.length ) {
1168
+
1169
+ forceUpdate = true;
1170
+
1171
+ }
1172
+
1135
1173
  for ( let i = 0, l = meshes.length; i < l; i ++ ) {
1136
1174
 
1137
1175
  const mesh = meshes[ i ];
@@ -1443,6 +1481,17 @@ class PathTracingSceneWorker extends PathTracingSceneGenerator {
1443
1481
 
1444
1482
  class MaterialBase extends ShaderMaterial {
1445
1483
 
1484
+ set needsUpdate( v ) {
1485
+
1486
+ super.needsUpdate = true;
1487
+ this.dispatchEvent( {
1488
+
1489
+ type: 'recompilation',
1490
+
1491
+ } );
1492
+
1493
+ }
1494
+
1446
1495
  constructor( shader ) {
1447
1496
 
1448
1497
  super( shader );
@@ -3443,11 +3492,11 @@ class CopyMaterial extends ShaderMaterial {
3443
3492
  // - https://github.com/hoverinc/ray-tracing-renderer
3444
3493
  // - http://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Stratified_Sampling.html
3445
3494
 
3446
- function shuffle( arr ) {
3495
+ function shuffle( arr, random = Math.random() ) {
3447
3496
 
3448
3497
  for ( let i = arr.length - 1; i > 0; i -- ) {
3449
3498
 
3450
- const j = Math.floor( Math.random() * ( i + 1 ) );
3499
+ const j = Math.floor( random() * ( i + 1 ) );
3451
3500
  const x = arr[ i ];
3452
3501
  arr[ i ] = arr[ j ];
3453
3502
  arr[ j ] = x;
@@ -3462,7 +3511,7 @@ function shuffle( arr ) {
3462
3511
  // dimensions : The number of dimensions to generate stratified values for
3463
3512
  class StratifiedSampler {
3464
3513
 
3465
- constructor( strataCount, dimensions ) {
3514
+ constructor( strataCount, dimensions, random = Math.random ) {
3466
3515
 
3467
3516
  const l = strataCount ** dimensions;
3468
3517
  const strata = new Uint16Array( l );
@@ -3479,7 +3528,19 @@ class StratifiedSampler {
3479
3528
 
3480
3529
  this.strataCount = strataCount;
3481
3530
 
3482
- this.restart = function () {
3531
+ this.reset = function () {
3532
+
3533
+ for ( let i = 0; i < l; i ++ ) {
3534
+
3535
+ strata[ i ] = i;
3536
+
3537
+ }
3538
+
3539
+ index = 0;
3540
+
3541
+ };
3542
+
3543
+ this.reshuffle = function () {
3483
3544
 
3484
3545
  index = 0;
3485
3546
 
@@ -3491,8 +3552,8 @@ class StratifiedSampler {
3491
3552
 
3492
3553
  if ( index >= strata.length ) {
3493
3554
 
3494
- shuffle( strata );
3495
- this.restart();
3555
+ shuffle( strata, random );
3556
+ this.reshuffle();
3496
3557
 
3497
3558
  }
3498
3559
 
@@ -3500,7 +3561,7 @@ class StratifiedSampler {
3500
3561
 
3501
3562
  for ( let i = 0; i < dimensions; i ++ ) {
3502
3563
 
3503
- samples[ i ] = ( stratum % strataCount + Math.random() ) / strataCount;
3564
+ samples[ i ] = ( stratum % strataCount + random() ) / strataCount;
3504
3565
  stratum = Math.floor( stratum / strataCount );
3505
3566
 
3506
3567
  }
@@ -3518,7 +3579,7 @@ class StratifiedSampler {
3518
3579
  // Stratified set of data with each tuple stratified separately and combined
3519
3580
  class StratifiedSamplerCombined {
3520
3581
 
3521
- constructor( strataCount, listOfDimensions ) {
3582
+ constructor( strataCount, listOfDimensions, random = Math.random ) {
3522
3583
 
3523
3584
  let totalDim = 0;
3524
3585
  for ( const dim of listOfDimensions ) {
@@ -3532,7 +3593,7 @@ class StratifiedSamplerCombined {
3532
3593
  let offset = 0;
3533
3594
  for ( const dim of listOfDimensions ) {
3534
3595
 
3535
- const sampler = new StratifiedSampler( strataCount, dim );
3596
+ const sampler = new StratifiedSampler( strataCount, dim, random );
3536
3597
  sampler.samples = new Float32Array( combined.buffer, offset, sampler.samples.length );
3537
3598
  offset += sampler.samples.length * 4;
3538
3599
  strataObjs.push( sampler );
@@ -3555,11 +3616,21 @@ class StratifiedSamplerCombined {
3555
3616
 
3556
3617
  };
3557
3618
 
3558
- this.restart = function () {
3619
+ this.reshuffle = function () {
3559
3620
 
3560
3621
  for ( const strata of strataObjs ) {
3561
3622
 
3562
- strata.restart();
3623
+ strata.reshuffle();
3624
+
3625
+ }
3626
+
3627
+ };
3628
+
3629
+ this.reset = function () {
3630
+
3631
+ for ( const strata of strataObjs ) {
3632
+
3633
+ strata.reset();
3563
3634
 
3564
3635
  }
3565
3636
 
@@ -3569,6 +3640,36 @@ class StratifiedSamplerCombined {
3569
3640
 
3570
3641
  }
3571
3642
 
3643
+ // https://stackoverflow.com/questions/424292/seedable-javascript-random-number-generator
3644
+ class RandomGenerator {
3645
+
3646
+ constructor( seed = 0 ) {
3647
+
3648
+ // LCG using GCC's constants
3649
+ this.m = 0x80000000; // 2**31;
3650
+ this.a = 1103515245;
3651
+ this.c = 12345;
3652
+
3653
+ this.seed = seed;
3654
+
3655
+ }
3656
+
3657
+ nextInt() {
3658
+
3659
+ this.seed = ( this.a * this.seed + this.c ) % this.m;
3660
+ return this.seed;
3661
+
3662
+ }
3663
+
3664
+ nextFloat() {
3665
+
3666
+ // returns in range [0,1]
3667
+ return this.nextInt() / ( this.m - 1 );
3668
+
3669
+ }
3670
+
3671
+ }
3672
+
3572
3673
  class StratifiedSamplesTexture extends DataTexture {
3573
3674
 
3574
3675
  constructor( count = 1, depth = 1, strata = 8 ) {
@@ -3579,22 +3680,37 @@ class StratifiedSamplesTexture extends DataTexture {
3579
3680
 
3580
3681
  this.strata = strata;
3581
3682
  this.sampler = null;
3683
+ this.generator = new RandomGenerator();
3684
+ this.stableNoise = false;
3685
+ this.random = () => {
3686
+
3687
+ if ( this.stableNoise ) {
3688
+
3689
+ return this.generator.nextFloat();
3690
+
3691
+ } else {
3692
+
3693
+ return Math.random();
3694
+
3695
+ }
3696
+
3697
+ };
3582
3698
 
3583
3699
  this.init( count, depth, strata );
3584
3700
 
3585
3701
  }
3586
3702
 
3587
- init( count, depth, strata = this.strata ) {
3703
+ init( count = this.image.height, depth = this.image.width, strata = this.strata ) {
3588
3704
 
3589
3705
  const { image } = this;
3590
- if ( image.width === depth && image.height === count ) {
3706
+ if ( image.width === depth && image.height === count && this.sampler !== null ) {
3591
3707
 
3592
3708
  return;
3593
3709
 
3594
3710
  }
3595
3711
 
3596
3712
  const dimensions = new Array( count * depth ).fill( 4 );
3597
- const sampler = new StratifiedSamplerCombined( strata, dimensions );
3713
+ const sampler = new StratifiedSamplerCombined( strata, dimensions, this.random );
3598
3714
 
3599
3715
  image.width = depth;
3600
3716
  image.height = count;
@@ -3614,6 +3730,13 @@ class StratifiedSamplesTexture extends DataTexture {
3614
3730
 
3615
3731
  }
3616
3732
 
3733
+ reset() {
3734
+
3735
+ this.sampler.reset();
3736
+ this.generator.seed = 0;
3737
+
3738
+ }
3739
+
3617
3740
  }
3618
3741
 
3619
3742
  function shuffleArray( array, random = Math.random ) {
@@ -7777,6 +7900,9 @@ class PathTracingRenderer {
7777
7900
 
7778
7901
  set material( v ) {
7779
7902
 
7903
+ this._fsQuad.material.removeEventListener( 'recompilation', this._compileFunction );
7904
+ v.addEventListener( 'recompilation', this._compileFunction );
7905
+
7780
7906
  this._fsQuad.material = v;
7781
7907
 
7782
7908
  }
@@ -7813,6 +7939,12 @@ class PathTracingRenderer {
7813
7939
 
7814
7940
  }
7815
7941
 
7942
+ get isCompiling() {
7943
+
7944
+ return Boolean( this._compilePromise );
7945
+
7946
+ }
7947
+
7816
7948
  constructor( renderer ) {
7817
7949
 
7818
7950
  this.camera = null;
@@ -7830,6 +7962,7 @@ class PathTracingRenderer {
7830
7962
  this._blendQuad = new FullScreenQuad( new BlendMaterial() );
7831
7963
  this._task = null;
7832
7964
  this._currentTile = 0;
7965
+ this._compilePromise = null;
7833
7966
 
7834
7967
  this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
7835
7968
 
@@ -7854,6 +7987,33 @@ class PathTracingRenderer {
7854
7987
  } ),
7855
7988
  ];
7856
7989
 
7990
+ // function for listening to for triggered compilation so we can wait for compilation to finish
7991
+ // before starting to render
7992
+ this._compileFunction = () => {
7993
+
7994
+ const promise = this.compileMaterial( this._fsQuad._mesh );
7995
+ promise.then( () => {
7996
+
7997
+ if ( this._compilePromise === promise ) {
7998
+
7999
+ this._compilePromise = null;
8000
+
8001
+ }
8002
+
8003
+ } );
8004
+
8005
+ this._compilePromise = promise;
8006
+
8007
+ };
8008
+
8009
+ this.material.addEventListener( 'recompilation', this._compileFunction );
8010
+
8011
+ }
8012
+
8013
+ compileMaterial() {
8014
+
8015
+ return this._renderer.compileAsync( this._fsQuad._mesh );
8016
+
7857
8017
  }
7858
8018
 
7859
8019
  setCamera( camera ) {
@@ -7885,7 +8045,6 @@ class PathTracingRenderer {
7885
8045
  material.setDefine( 'CAMERA_TYPE', cameraType );
7886
8046
 
7887
8047
  this.camera = camera;
7888
- // this.reset();
7889
8048
 
7890
8049
  }
7891
8050
 
@@ -7952,9 +8111,11 @@ class PathTracingRenderer {
7952
8111
  this.samples = 0;
7953
8112
  this._task = null;
7954
8113
 
8114
+ this.material.stratifiedTexture.stableNoise = this.stableNoise;
7955
8115
  if ( this.stableNoise ) {
7956
8116
 
7957
8117
  this.material.seed = 0;
8118
+ this.material.stratifiedTexture.reset();
7958
8119
 
7959
8120
  }
7960
8121
 
@@ -7962,6 +8123,15 @@ class PathTracingRenderer {
7962
8123
 
7963
8124
  update() {
7964
8125
 
8126
+ // ensure we've updated our defines before rendering so we can ensure we
8127
+ // can wait for compilation to finish
8128
+ this.material.onBeforeRender();
8129
+ if ( this.isCompiling ) {
8130
+
8131
+ return;
8132
+
8133
+ }
8134
+
7965
8135
  if ( ! this._task ) {
7966
8136
 
7967
8137
  this._task = renderTask.call( this );
@@ -8181,6 +8351,150 @@ class ClampedInterpolationMaterial extends ShaderMaterial {
8181
8351
 
8182
8352
  }
8183
8353
 
8354
+ class CubeToEquirectMaterial extends ShaderMaterial {
8355
+
8356
+ constructor() {
8357
+
8358
+ super( {
8359
+
8360
+ uniforms: {
8361
+
8362
+ envMap: { value: null },
8363
+ flipEnvMap: { value: - 1 },
8364
+
8365
+ },
8366
+
8367
+ vertexShader: /* glsl */`
8368
+ varying vec2 vUv;
8369
+ void main() {
8370
+
8371
+ vUv = uv;
8372
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
8373
+
8374
+ }`,
8375
+
8376
+ fragmentShader: /* glsl */`
8377
+ #define ENVMAP_TYPE_CUBE_UV
8378
+
8379
+ uniform samplerCube envMap;
8380
+ uniform float flipEnvMap;
8381
+ varying vec2 vUv;
8382
+
8383
+ #include <common>
8384
+ #include <cube_uv_reflection_fragment>
8385
+
8386
+ ${ util_functions }
8387
+
8388
+ void main() {
8389
+
8390
+ vec3 rayDirection = equirectUvToDirection( vUv );
8391
+ rayDirection.x *= flipEnvMap;
8392
+ gl_FragColor = textureCube( envMap, rayDirection );
8393
+
8394
+ }`
8395
+ } );
8396
+
8397
+ this.depthWrite = false;
8398
+ this.depthTest = false;
8399
+
8400
+ }
8401
+
8402
+ }
8403
+
8404
+ class CubeToEquirectGenerator {
8405
+
8406
+ constructor( renderer ) {
8407
+
8408
+ this._renderer = renderer;
8409
+ this._quad = new FullScreenQuad( new CubeToEquirectMaterial() );
8410
+
8411
+ }
8412
+
8413
+ generate( source, width = null, height = null ) {
8414
+
8415
+ if ( ! source.isCubeTexture ) {
8416
+
8417
+ throw new Error( 'CubeToEquirectMaterial: Source can only be cube textures.' );
8418
+
8419
+ }
8420
+
8421
+ const image = source.images[ 0 ];
8422
+ const renderer = this._renderer;
8423
+ const quad = this._quad;
8424
+
8425
+ // determine the dimensions if not provided
8426
+ if ( width === null ) {
8427
+
8428
+ width = 4 * image.height;
8429
+
8430
+ }
8431
+
8432
+ if ( height === null ) {
8433
+
8434
+ height = 2 * image.height;
8435
+
8436
+ }
8437
+
8438
+ const target = new WebGLRenderTarget( width, height, {
8439
+ type: FloatType,
8440
+ colorSpace: image.colorSpace,
8441
+ } );
8442
+
8443
+ // prep the cube map data
8444
+ const imageHeight = image.height;
8445
+ const maxMip = Math.log2( imageHeight ) - 2;
8446
+ const texelHeight = 1.0 / imageHeight;
8447
+ const texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) );
8448
+
8449
+ quad.material.defines.CUBEUV_MAX_MIP = `${ maxMip }.0`;
8450
+ quad.material.defines.CUBEUV_TEXEL_WIDTH = texelWidth;
8451
+ quad.material.defines.CUBEUV_TEXEL_HEIGHT = texelHeight;
8452
+ quad.material.uniforms.envMap.value = source;
8453
+ quad.material.uniforms.flipEnvMap.value = source.isRenderTargetTexture ? 1 : - 1;
8454
+ quad.material.needsUpdate = true;
8455
+
8456
+ // save state and render the contents
8457
+ const currentTarget = renderer.getRenderTarget();
8458
+ const currentAutoClear = renderer.autoClear;
8459
+ renderer.autoClear = true;
8460
+ renderer.setRenderTarget( target );
8461
+ quad.render( renderer );
8462
+ renderer.setRenderTarget( currentTarget );
8463
+ renderer.autoClear = currentAutoClear;
8464
+
8465
+ // read the data back
8466
+ const buffer = new Uint16Array( width * height * 4 );
8467
+ const readBuffer = new Float32Array( width * height * 4 );
8468
+ renderer.readRenderTargetPixels( target, 0, 0, width, height, readBuffer );
8469
+ target.dispose();
8470
+
8471
+ for ( let i = 0, l = readBuffer.length; i < l; i ++ ) {
8472
+
8473
+ buffer[ i ] = DataUtils.toHalfFloat( readBuffer[ i ] );
8474
+
8475
+ }
8476
+
8477
+ // produce the data texture
8478
+ const result = new DataTexture( buffer, width, height, RGBAFormat, HalfFloatType );
8479
+ result.minFilter = LinearMipMapLinearFilter;
8480
+ result.magFilter = LinearFilter;
8481
+ result.wrapS = RepeatWrapping;
8482
+ result.wrapT = RepeatWrapping;
8483
+ result.mapping = EquirectangularReflectionMapping;
8484
+ result.needsUpdate = true;
8485
+
8486
+ return result;
8487
+
8488
+ }
8489
+
8490
+ dispose() {
8491
+
8492
+ this._quad.dispose();
8493
+
8494
+ }
8495
+
8496
+ }
8497
+
8184
8498
  function supportsFloatBlending( renderer ) {
8185
8499
 
8186
8500
  return renderer.extensions.get( 'EXT_float_blend' );
@@ -8256,6 +8570,24 @@ class WebGLPathTracer {
8256
8570
 
8257
8571
  }
8258
8572
 
8573
+ get stableNoise() {
8574
+
8575
+ return this._pathTracer.stableNoise;
8576
+
8577
+ }
8578
+
8579
+ set stableNoise( v ) {
8580
+
8581
+ this._pathTracer.stableNoise = v;
8582
+
8583
+ }
8584
+
8585
+ get isCompiling() {
8586
+
8587
+ return Boolean( this._pathTracer.isCompiling );
8588
+
8589
+ }
8590
+
8259
8591
  constructor( renderer ) {
8260
8592
 
8261
8593
  // members
@@ -8264,6 +8596,7 @@ class WebGLPathTracer {
8264
8596
  this._pathTracer = new PathTracingRenderer( renderer );
8265
8597
  this._queueReset = false;
8266
8598
  this._clock = new Clock();
8599
+ this._compilePromise = null;
8267
8600
 
8268
8601
  this._lowResPathTracer = new PathTracingRenderer( renderer );
8269
8602
  this._lowResPathTracer.tiles.set( 1, 1 );
@@ -8276,6 +8609,10 @@ class WebGLPathTracer {
8276
8609
  } ) );
8277
8610
  this._materials = null;
8278
8611
 
8612
+ this._previousEnvironment = null;
8613
+ this._previousBackground = null;
8614
+ this._internalBackground = null;
8615
+
8279
8616
  // options
8280
8617
  this.renderDelay = 100;
8281
8618
  this.minSamples = 5;
@@ -8404,6 +8741,13 @@ class WebGLPathTracer {
8404
8741
  const scene = this.scene;
8405
8742
  const material = this._pathTracer.material;
8406
8743
 
8744
+ if ( this._internalBackground ) {
8745
+
8746
+ this._internalBackground.dispose();
8747
+ this._internalBackground = null;
8748
+
8749
+ }
8750
+
8407
8751
  // update scene background
8408
8752
  material.backgroundBlur = scene.backgroundBlurriness;
8409
8753
  material.backgroundIntensity = scene.backgroundIntensity ?? 1;
@@ -8431,6 +8775,17 @@ class WebGLPathTracer {
8431
8775
  material.backgroundMap = colorBackground;
8432
8776
  material.backgroundAlpha = 1;
8433
8777
 
8778
+ } else if ( scene.background.isCubeTexture ) {
8779
+
8780
+ if ( scene.background !== this._previousBackground ) {
8781
+
8782
+ const background = new CubeToEquirectGenerator( this._renderer ).generate( scene.background );
8783
+ this._internalBackground = background;
8784
+ material.backgroundMap = background;
8785
+ material.backgroundAlpha = 1;
8786
+
8787
+ }
8788
+
8434
8789
  } else {
8435
8790
 
8436
8791
  material.backgroundMap = scene.background;
@@ -8443,12 +8798,21 @@ class WebGLPathTracer {
8443
8798
  material.environmentRotation.makeRotationFromEuler( scene.environmentRotation ).invert();
8444
8799
  if ( this._previousEnvironment !== scene.environment ) {
8445
8800
 
8446
- if ( scene.environment ) {
8801
+ if ( scene.environment !== null ) {
8802
+
8803
+ if ( scene.environment.isCubeTexture ) {
8447
8804
 
8448
- // TODO: Consider setting this to the highest supported bit depth by checking for
8449
- // OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
8450
- // the equirect uniform
8451
- material.envMapInfo.updateFrom( scene.environment );
8805
+ const environment = new CubeToEquirectGenerator( this._renderer ).generate( scene.environment );
8806
+ material.envMapInfo.updateFrom( environment );
8807
+
8808
+ } else {
8809
+
8810
+ // TODO: Consider setting this to the highest supported bit depth by checking for
8811
+ // OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
8812
+ // the equirect uniform
8813
+ material.envMapInfo.updateFrom( scene.environment );
8814
+
8815
+ }
8452
8816
 
8453
8817
  } else {
8454
8818
 
@@ -8459,6 +8823,7 @@ class WebGLPathTracer {
8459
8823
  }
8460
8824
 
8461
8825
  this._previousEnvironment = scene.environment;
8826
+ this._previousBackground = scene.background;
8462
8827
  this.reset();
8463
8828
 
8464
8829
  }
@@ -8529,7 +8894,7 @@ class WebGLPathTracer {
8529
8894
  // render the path tracing sample after enough time has passed
8530
8895
  const delta = clock.getDelta() * 1e3;
8531
8896
  const elapsedTime = clock.getElapsedTime() * 1e3;
8532
- if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime ) {
8897
+ if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime && ! this.isCompiling ) {
8533
8898
 
8534
8899
  pathTracer.update();
8535
8900
 
@@ -8562,7 +8927,7 @@ class WebGLPathTracer {
8562
8927
  // render the fallback if we haven't rendered enough samples, are paused, or are occluded
8563
8928
  if ( ! this.enablePathTracing || this.samples < minSamples || quad.material.opacity < 1 ) {
8564
8929
 
8565
- if ( this.dynamicLowRes ) {
8930
+ if ( this.dynamicLowRes && ! this.isCompiling ) {
8566
8931
 
8567
8932
  if ( lowResPathTracer.samples < 1 ) {
8568
8933
 
@@ -8577,7 +8942,9 @@ class WebGLPathTracer {
8577
8942
  quad.render( renderer );
8578
8943
  quad.material.opacity = currentOpacity;
8579
8944
 
8580
- } else if ( this.rasterizeScene ) {
8945
+ }
8946
+
8947
+ if ( ! this.dynamicLowRes && this.rasterizeScene || this.dynamicLowRes && this.isCompiling ) {
8581
8948
 
8582
8949
  this.rasterizeSceneCallback( this.scene, this.camera );
8583
8950