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