three-gpu-pathtracer 0.0.22 → 0.0.24

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 ) {
@@ -271,7 +279,20 @@
271
279
  const attr = geometry.getAttribute( key );
272
280
  if ( ! skip ) {
273
281
 
274
- copyAttributeContents( attr, targetAttribute, offset );
282
+ if ( key === 'color' && targetAttribute.itemSize !== attr.itemSize ) {
283
+
284
+ // make sure the color attribute is aligned with itemSize 3 to 4
285
+ for ( let index = offset, l = attr.count; index < l; index ++ ) {
286
+
287
+ attr.setXYZW( index, targetAttribute.getX( index ), targetAttribute.getY( index ), targetAttribute.getZ( index ), 1.0 );
288
+
289
+ }
290
+
291
+ } else {
292
+
293
+ copyAttributeContents( attr, targetAttribute, offset );
294
+
295
+ }
275
296
 
276
297
  }
277
298
 
@@ -432,29 +453,22 @@
432
453
 
433
454
  }
434
455
 
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
456
  function getGeometryHash( geometry ) {
444
457
 
445
- let hash = '';
458
+ let hash = geometry.uuid;
446
459
  const attributes = Object.values( geometry.attributes );
447
460
  if ( geometry.index ) {
448
461
 
449
462
  attributes.push( geometry.index );
463
+ hash += `index|${ geometry.index.version }`;
450
464
 
451
465
  }
452
466
 
453
- attributes.sort( attributeSort );
467
+ const keys = Object.keys( attributes ).sort();
468
+ for ( const key of keys ) {
454
469
 
455
- for ( const attr of attributes ) {
456
-
457
- hash += `${ attr.uuid }_${ attr.version }|`;
470
+ const attr = attributes[ key ];
471
+ hash += `${ key }_${ attr.version }|`;
458
472
 
459
473
  }
460
474
 
@@ -885,6 +899,28 @@
885
899
 
886
900
  }
887
901
 
902
+ // returns whether the passed mesh is compatible with this baked geometry
903
+ // such that it can be updated without resizing attributes
904
+ isCompatible( mesh, attributes ) {
905
+
906
+ const geometry = mesh.geometry;
907
+ for ( let i = 0; i < attributes.length; i ++ ) {
908
+
909
+ const key = attributes[ i ];
910
+ const attr1 = geometry.attributes[ key ];
911
+ const attr2 = this.attributes[ key ];
912
+ if ( attr1 && ! validateAttributes( attr1, attr2 ) ) {
913
+
914
+ return false;
915
+
916
+ }
917
+
918
+ }
919
+
920
+ return true;
921
+
922
+ }
923
+
888
924
  updateFrom( mesh, options ) {
889
925
 
890
926
  const diff = this._diff;
@@ -1074,15 +1110,24 @@
1074
1110
  unusedMeshKeys.delete( meshKey );
1075
1111
 
1076
1112
  // initialize the intermediate geometry
1077
- if ( ! _intermediateGeometry.has( meshKey ) ) {
1113
+ // if the mesh and source geometry have changed in such a way that they are no longer
1114
+ // compatible then regenerate the baked geometry from scratch
1115
+ let geom = _intermediateGeometry.get( meshKey );
1116
+ if ( ! geom || ! geom.isCompatible( mesh, this.attributes ) ) {
1117
+
1118
+ if ( geom ) {
1078
1119
 
1079
- _intermediateGeometry.set( meshKey, new BakedGeometry() );
1120
+ geom.dispose();
1121
+
1122
+ }
1123
+
1124
+ geom = new BakedGeometry();
1125
+ _intermediateGeometry.set( meshKey, geom );
1080
1126
 
1081
1127
  }
1082
1128
 
1083
1129
  // transform the geometry into the intermediate buffer geometry, saving whether
1084
1130
  // or not it changed.
1085
- const geom = _intermediateGeometry.get( meshKey );
1086
1131
  if ( geom.updateFrom( mesh, convertOptions ) ) {
1087
1132
 
1088
1133
  // TODO: provide option for only generating the set of attributes that are present
@@ -1134,6 +1179,12 @@
1134
1179
 
1135
1180
  // get the list of geometries to merge
1136
1181
  let forceUpdate = false;
1182
+ if ( meshes.length !== previousMergeInfo.length ) {
1183
+
1184
+ forceUpdate = true;
1185
+
1186
+ }
1187
+
1137
1188
  for ( let i = 0, l = meshes.length; i < l; i ++ ) {
1138
1189
 
1139
1190
  const mesh = meshes[ i ];
@@ -1279,6 +1330,7 @@
1279
1330
  this._bvhWorker = null;
1280
1331
  this._pendingGenerate = null;
1281
1332
  this._buildAsync = false;
1333
+ this._materialUuids = null;
1282
1334
 
1283
1335
  }
1284
1336
 
@@ -1360,12 +1412,29 @@
1360
1412
  // generate the geometry
1361
1413
  const result = staticGeometryGenerator.generate( geometry );
1362
1414
  const materials = result.materials;
1415
+ let needsMaterialIndexUpdate = result.changeType !== NO_CHANGE || this._materialUuids === null || this._materialUuids.length !== length;
1416
+ if ( ! needsMaterialIndexUpdate ) {
1417
+
1418
+ for ( let i = 0, length = materials.length; i < length; i ++ ) {
1419
+
1420
+ const material = materials[ i ];
1421
+ if ( material.uuid !== this._materialUuids[ i ] ) {
1422
+
1423
+ needsMaterialIndexUpdate = true;
1424
+ break;
1425
+
1426
+ }
1427
+
1428
+ }
1429
+
1430
+ }
1431
+
1363
1432
  const textures = getTextures$1( materials );
1364
1433
  const { lights, iesTextures } = getLights$1( objects );
1365
-
1366
- if ( result.changeType !== NO_CHANGE ) {
1434
+ if ( needsMaterialIndexUpdate ) {
1367
1435
 
1368
1436
  updateMaterialIndexAttribute( geometry, materials, materials );
1437
+ this._materialUuids = materials.map( material => material.uuid );
1369
1438
 
1370
1439
  }
1371
1440
 
@@ -1409,6 +1478,7 @@
1409
1478
  return {
1410
1479
  bvhChanged: result.changeType !== NO_CHANGE,
1411
1480
  bvh: this.bvh,
1481
+ needsMaterialIndexUpdate,
1412
1482
  lights,
1413
1483
  iesTextures,
1414
1484
  geometry,
@@ -1445,6 +1515,17 @@
1445
1515
 
1446
1516
  class MaterialBase extends three.ShaderMaterial {
1447
1517
 
1518
+ set needsUpdate( v ) {
1519
+
1520
+ super.needsUpdate = true;
1521
+ this.dispatchEvent( {
1522
+
1523
+ type: 'recompilation',
1524
+
1525
+ } );
1526
+
1527
+ }
1528
+
1448
1529
  constructor( shader ) {
1449
1530
 
1450
1531
  super( shader );
@@ -2825,7 +2906,7 @@
2825
2906
 
2826
2907
  }
2827
2908
 
2828
- const MATERIAL_PIXELS = 45;
2909
+ const MATERIAL_PIXELS = 47;
2829
2910
  const MATERIAL_STRIDE = MATERIAL_PIXELS * 4;
2830
2911
 
2831
2912
  class MaterialFeatures {
@@ -3250,6 +3331,9 @@
3250
3331
  // specularIntensityMap transform 43
3251
3332
  index += writeTextureMatrixToArray( m, 'specularIntensityMap', floatArray, index );
3252
3333
 
3334
+ // alphaMap transform 45
3335
+ index += writeTextureMatrixToArray( m, 'alphaMap', floatArray, index );
3336
+
3253
3337
  }
3254
3338
 
3255
3339
  // check if the contents have changed
@@ -3445,11 +3529,11 @@
3445
3529
  // - https://github.com/hoverinc/ray-tracing-renderer
3446
3530
  // - http://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Stratified_Sampling.html
3447
3531
 
3448
- function shuffle( arr ) {
3532
+ function shuffle( arr, random = Math.random() ) {
3449
3533
 
3450
3534
  for ( let i = arr.length - 1; i > 0; i -- ) {
3451
3535
 
3452
- const j = Math.floor( Math.random() * ( i + 1 ) );
3536
+ const j = Math.floor( random() * ( i + 1 ) );
3453
3537
  const x = arr[ i ];
3454
3538
  arr[ i ] = arr[ j ];
3455
3539
  arr[ j ] = x;
@@ -3464,7 +3548,7 @@
3464
3548
  // dimensions : The number of dimensions to generate stratified values for
3465
3549
  class StratifiedSampler {
3466
3550
 
3467
- constructor( strataCount, dimensions ) {
3551
+ constructor( strataCount, dimensions, random = Math.random ) {
3468
3552
 
3469
3553
  const l = strataCount ** dimensions;
3470
3554
  const strata = new Uint16Array( l );
@@ -3481,7 +3565,19 @@
3481
3565
 
3482
3566
  this.strataCount = strataCount;
3483
3567
 
3484
- this.restart = function () {
3568
+ this.reset = function () {
3569
+
3570
+ for ( let i = 0; i < l; i ++ ) {
3571
+
3572
+ strata[ i ] = i;
3573
+
3574
+ }
3575
+
3576
+ index = 0;
3577
+
3578
+ };
3579
+
3580
+ this.reshuffle = function () {
3485
3581
 
3486
3582
  index = 0;
3487
3583
 
@@ -3493,8 +3589,8 @@
3493
3589
 
3494
3590
  if ( index >= strata.length ) {
3495
3591
 
3496
- shuffle( strata );
3497
- this.restart();
3592
+ shuffle( strata, random );
3593
+ this.reshuffle();
3498
3594
 
3499
3595
  }
3500
3596
 
@@ -3502,7 +3598,7 @@
3502
3598
 
3503
3599
  for ( let i = 0; i < dimensions; i ++ ) {
3504
3600
 
3505
- samples[ i ] = ( stratum % strataCount + Math.random() ) / strataCount;
3601
+ samples[ i ] = ( stratum % strataCount + random() ) / strataCount;
3506
3602
  stratum = Math.floor( stratum / strataCount );
3507
3603
 
3508
3604
  }
@@ -3520,7 +3616,7 @@
3520
3616
  // Stratified set of data with each tuple stratified separately and combined
3521
3617
  class StratifiedSamplerCombined {
3522
3618
 
3523
- constructor( strataCount, listOfDimensions ) {
3619
+ constructor( strataCount, listOfDimensions, random = Math.random ) {
3524
3620
 
3525
3621
  let totalDim = 0;
3526
3622
  for ( const dim of listOfDimensions ) {
@@ -3534,7 +3630,7 @@
3534
3630
  let offset = 0;
3535
3631
  for ( const dim of listOfDimensions ) {
3536
3632
 
3537
- const sampler = new StratifiedSampler( strataCount, dim );
3633
+ const sampler = new StratifiedSampler( strataCount, dim, random );
3538
3634
  sampler.samples = new Float32Array( combined.buffer, offset, sampler.samples.length );
3539
3635
  offset += sampler.samples.length * 4;
3540
3636
  strataObjs.push( sampler );
@@ -3557,11 +3653,21 @@
3557
3653
 
3558
3654
  };
3559
3655
 
3560
- this.restart = function () {
3656
+ this.reshuffle = function () {
3561
3657
 
3562
3658
  for ( const strata of strataObjs ) {
3563
3659
 
3564
- strata.restart();
3660
+ strata.reshuffle();
3661
+
3662
+ }
3663
+
3664
+ };
3665
+
3666
+ this.reset = function () {
3667
+
3668
+ for ( const strata of strataObjs ) {
3669
+
3670
+ strata.reset();
3565
3671
 
3566
3672
  }
3567
3673
 
@@ -3571,6 +3677,36 @@
3571
3677
 
3572
3678
  }
3573
3679
 
3680
+ // https://stackoverflow.com/questions/424292/seedable-javascript-random-number-generator
3681
+ class RandomGenerator {
3682
+
3683
+ constructor( seed = 0 ) {
3684
+
3685
+ // LCG using GCC's constants
3686
+ this.m = 0x80000000; // 2**31;
3687
+ this.a = 1103515245;
3688
+ this.c = 12345;
3689
+
3690
+ this.seed = seed;
3691
+
3692
+ }
3693
+
3694
+ nextInt() {
3695
+
3696
+ this.seed = ( this.a * this.seed + this.c ) % this.m;
3697
+ return this.seed;
3698
+
3699
+ }
3700
+
3701
+ nextFloat() {
3702
+
3703
+ // returns in range [0,1]
3704
+ return this.nextInt() / ( this.m - 1 );
3705
+
3706
+ }
3707
+
3708
+ }
3709
+
3574
3710
  class StratifiedSamplesTexture extends three.DataTexture {
3575
3711
 
3576
3712
  constructor( count = 1, depth = 1, strata = 8 ) {
@@ -3581,22 +3717,37 @@
3581
3717
 
3582
3718
  this.strata = strata;
3583
3719
  this.sampler = null;
3720
+ this.generator = new RandomGenerator();
3721
+ this.stableNoise = false;
3722
+ this.random = () => {
3723
+
3724
+ if ( this.stableNoise ) {
3725
+
3726
+ return this.generator.nextFloat();
3727
+
3728
+ } else {
3729
+
3730
+ return Math.random();
3731
+
3732
+ }
3733
+
3734
+ };
3584
3735
 
3585
3736
  this.init( count, depth, strata );
3586
3737
 
3587
3738
  }
3588
3739
 
3589
- init( count, depth, strata = this.strata ) {
3740
+ init( count = this.image.height, depth = this.image.width, strata = this.strata ) {
3590
3741
 
3591
3742
  const { image } = this;
3592
- if ( image.width === depth && image.height === count ) {
3743
+ if ( image.width === depth && image.height === count && this.sampler !== null ) {
3593
3744
 
3594
3745
  return;
3595
3746
 
3596
3747
  }
3597
3748
 
3598
3749
  const dimensions = new Array( count * depth ).fill( 4 );
3599
- const sampler = new StratifiedSamplerCombined( strata, dimensions );
3750
+ const sampler = new StratifiedSamplerCombined( strata, dimensions, this.random );
3600
3751
 
3601
3752
  image.width = depth;
3602
3753
  image.height = count;
@@ -3616,6 +3767,13 @@
3616
3767
 
3617
3768
  }
3618
3769
 
3770
+ reset() {
3771
+
3772
+ this.sampler.reset();
3773
+ this.generator.seed = 0;
3774
+
3775
+ }
3776
+
3619
3777
  }
3620
3778
 
3621
3779
  function shuffleArray( array, random = Math.random ) {
@@ -4255,6 +4413,7 @@
4255
4413
  mat3 iridescenceThicknessMapTransform;
4256
4414
  mat3 specularColorMapTransform;
4257
4415
  mat3 specularIntensityMapTransform;
4416
+ mat3 alphaMapTransform;
4258
4417
 
4259
4418
  };
4260
4419
 
@@ -4275,7 +4434,7 @@
4275
4434
 
4276
4435
  Material readMaterialInfo( sampler2D tex, uint index ) {
4277
4436
 
4278
- uint i = index * 45u;
4437
+ uint i = index * uint( MATERIAL_PIXELS );
4279
4438
 
4280
4439
  vec4 s0 = texelFetch1D( tex, i + 0u );
4281
4440
  vec4 s1 = texelFetch1D( tex, i + 1u );
@@ -4374,6 +4533,7 @@
4374
4533
  m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
4375
4534
  m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
4376
4535
  m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
4536
+ m.alphaMapTransform = m.alphaMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 30u );
4377
4537
 
4378
4538
  return m;
4379
4539
 
@@ -6140,7 +6300,7 @@
6140
6300
  // returns whether the given material is a fog material or not
6141
6301
  bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
6142
6302
 
6143
- uint i = materialIndex * 45u;
6303
+ uint i = materialIndex * uint( MATERIAL_PIXELS );
6144
6304
  vec4 s14 = texelFetch1D( materials, i + 14u );
6145
6305
  return bool( int( s14.b ) & 4 );
6146
6306
 
@@ -6370,7 +6530,8 @@ bool bvhIntersectFogVolumeHit(
6370
6530
  // alphaMap
6371
6531
  if ( material.alphaMap != - 1 ) {
6372
6532
 
6373
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
6533
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
6534
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
6374
6535
 
6375
6536
  }
6376
6537
 
@@ -6512,7 +6673,7 @@ bool bvhIntersectFogVolumeHit(
6512
6673
  vec3 shapeUVW= rand3( 1 );
6513
6674
  int blades = physicalCamera.apertureBlades;
6514
6675
  float anamorphicRatio = physicalCamera.anamorphicRatio;
6515
- vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
6676
+ vec2 apertureSample = sampleAperture( blades, shapeUVW );
6516
6677
  apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
6517
6678
 
6518
6679
  // rotate the aperture shape
@@ -6682,7 +6843,8 @@ bool bvhIntersectFogVolumeHit(
6682
6843
  // alphaMap
6683
6844
  if ( material.alphaMap != - 1 ) {
6684
6845
 
6685
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
6846
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
6847
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
6686
6848
 
6687
6849
  }
6688
6850
 
@@ -7097,6 +7259,7 @@ bool bvhIntersectFogVolumeHit(
7097
7259
  ATTR_TANGENT: 1,
7098
7260
  ATTR_UV: 2,
7099
7261
  ATTR_COLOR: 3,
7262
+ MATERIAL_PIXELS: MATERIAL_PIXELS,
7100
7263
  },
7101
7264
 
7102
7265
  uniforms: {
@@ -7779,6 +7942,9 @@ bool bvhIntersectFogVolumeHit(
7779
7942
 
7780
7943
  set material( v ) {
7781
7944
 
7945
+ this._fsQuad.material.removeEventListener( 'recompilation', this._compileFunction );
7946
+ v.addEventListener( 'recompilation', this._compileFunction );
7947
+
7782
7948
  this._fsQuad.material = v;
7783
7949
 
7784
7950
  }
@@ -7815,6 +7981,12 @@ bool bvhIntersectFogVolumeHit(
7815
7981
 
7816
7982
  }
7817
7983
 
7984
+ get isCompiling() {
7985
+
7986
+ return Boolean( this._compilePromise );
7987
+
7988
+ }
7989
+
7818
7990
  constructor( renderer ) {
7819
7991
 
7820
7992
  this.camera = null;
@@ -7832,6 +8004,7 @@ bool bvhIntersectFogVolumeHit(
7832
8004
  this._blendQuad = new Pass_js.FullScreenQuad( new BlendMaterial() );
7833
8005
  this._task = null;
7834
8006
  this._currentTile = 0;
8007
+ this._compilePromise = null;
7835
8008
 
7836
8009
  this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
7837
8010
 
@@ -7856,6 +8029,33 @@ bool bvhIntersectFogVolumeHit(
7856
8029
  } ),
7857
8030
  ];
7858
8031
 
8032
+ // function for listening to for triggered compilation so we can wait for compilation to finish
8033
+ // before starting to render
8034
+ this._compileFunction = () => {
8035
+
8036
+ const promise = this.compileMaterial( this._fsQuad._mesh );
8037
+ promise.then( () => {
8038
+
8039
+ if ( this._compilePromise === promise ) {
8040
+
8041
+ this._compilePromise = null;
8042
+
8043
+ }
8044
+
8045
+ } );
8046
+
8047
+ this._compilePromise = promise;
8048
+
8049
+ };
8050
+
8051
+ this.material.addEventListener( 'recompilation', this._compileFunction );
8052
+
8053
+ }
8054
+
8055
+ compileMaterial() {
8056
+
8057
+ return this._renderer.compileAsync( this._fsQuad._mesh );
8058
+
7859
8059
  }
7860
8060
 
7861
8061
  setCamera( camera ) {
@@ -7887,7 +8087,6 @@ bool bvhIntersectFogVolumeHit(
7887
8087
  material.setDefine( 'CAMERA_TYPE', cameraType );
7888
8088
 
7889
8089
  this.camera = camera;
7890
- // this.reset();
7891
8090
 
7892
8091
  }
7893
8092
 
@@ -7954,9 +8153,11 @@ bool bvhIntersectFogVolumeHit(
7954
8153
  this.samples = 0;
7955
8154
  this._task = null;
7956
8155
 
8156
+ this.material.stratifiedTexture.stableNoise = this.stableNoise;
7957
8157
  if ( this.stableNoise ) {
7958
8158
 
7959
8159
  this.material.seed = 0;
8160
+ this.material.stratifiedTexture.reset();
7960
8161
 
7961
8162
  }
7962
8163
 
@@ -7964,6 +8165,15 @@ bool bvhIntersectFogVolumeHit(
7964
8165
 
7965
8166
  update() {
7966
8167
 
8168
+ // ensure we've updated our defines before rendering so we can ensure we
8169
+ // can wait for compilation to finish
8170
+ this.material.onBeforeRender();
8171
+ if ( this.isCompiling ) {
8172
+
8173
+ return;
8174
+
8175
+ }
8176
+
7967
8177
  if ( ! this._task ) {
7968
8178
 
7969
8179
  this._task = renderTask.call( this );
@@ -8183,6 +8393,150 @@ bool bvhIntersectFogVolumeHit(
8183
8393
 
8184
8394
  }
8185
8395
 
8396
+ class CubeToEquirectMaterial extends three.ShaderMaterial {
8397
+
8398
+ constructor() {
8399
+
8400
+ super( {
8401
+
8402
+ uniforms: {
8403
+
8404
+ envMap: { value: null },
8405
+ flipEnvMap: { value: - 1 },
8406
+
8407
+ },
8408
+
8409
+ vertexShader: /* glsl */`
8410
+ varying vec2 vUv;
8411
+ void main() {
8412
+
8413
+ vUv = uv;
8414
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
8415
+
8416
+ }`,
8417
+
8418
+ fragmentShader: /* glsl */`
8419
+ #define ENVMAP_TYPE_CUBE_UV
8420
+
8421
+ uniform samplerCube envMap;
8422
+ uniform float flipEnvMap;
8423
+ varying vec2 vUv;
8424
+
8425
+ #include <common>
8426
+ #include <cube_uv_reflection_fragment>
8427
+
8428
+ ${ util_functions }
8429
+
8430
+ void main() {
8431
+
8432
+ vec3 rayDirection = equirectUvToDirection( vUv );
8433
+ rayDirection.x *= flipEnvMap;
8434
+ gl_FragColor = textureCube( envMap, rayDirection );
8435
+
8436
+ }`
8437
+ } );
8438
+
8439
+ this.depthWrite = false;
8440
+ this.depthTest = false;
8441
+
8442
+ }
8443
+
8444
+ }
8445
+
8446
+ class CubeToEquirectGenerator {
8447
+
8448
+ constructor( renderer ) {
8449
+
8450
+ this._renderer = renderer;
8451
+ this._quad = new Pass_js.FullScreenQuad( new CubeToEquirectMaterial() );
8452
+
8453
+ }
8454
+
8455
+ generate( source, width = null, height = null ) {
8456
+
8457
+ if ( ! source.isCubeTexture ) {
8458
+
8459
+ throw new Error( 'CubeToEquirectMaterial: Source can only be cube textures.' );
8460
+
8461
+ }
8462
+
8463
+ const image = source.images[ 0 ];
8464
+ const renderer = this._renderer;
8465
+ const quad = this._quad;
8466
+
8467
+ // determine the dimensions if not provided
8468
+ if ( width === null ) {
8469
+
8470
+ width = 4 * image.height;
8471
+
8472
+ }
8473
+
8474
+ if ( height === null ) {
8475
+
8476
+ height = 2 * image.height;
8477
+
8478
+ }
8479
+
8480
+ const target = new three.WebGLRenderTarget( width, height, {
8481
+ type: three.FloatType,
8482
+ colorSpace: image.colorSpace,
8483
+ } );
8484
+
8485
+ // prep the cube map data
8486
+ const imageHeight = image.height;
8487
+ const maxMip = Math.log2( imageHeight ) - 2;
8488
+ const texelHeight = 1.0 / imageHeight;
8489
+ const texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) );
8490
+
8491
+ quad.material.defines.CUBEUV_MAX_MIP = `${ maxMip }.0`;
8492
+ quad.material.defines.CUBEUV_TEXEL_WIDTH = texelWidth;
8493
+ quad.material.defines.CUBEUV_TEXEL_HEIGHT = texelHeight;
8494
+ quad.material.uniforms.envMap.value = source;
8495
+ quad.material.uniforms.flipEnvMap.value = source.isRenderTargetTexture ? 1 : - 1;
8496
+ quad.material.needsUpdate = true;
8497
+
8498
+ // save state and render the contents
8499
+ const currentTarget = renderer.getRenderTarget();
8500
+ const currentAutoClear = renderer.autoClear;
8501
+ renderer.autoClear = true;
8502
+ renderer.setRenderTarget( target );
8503
+ quad.render( renderer );
8504
+ renderer.setRenderTarget( currentTarget );
8505
+ renderer.autoClear = currentAutoClear;
8506
+
8507
+ // read the data back
8508
+ const buffer = new Uint16Array( width * height * 4 );
8509
+ const readBuffer = new Float32Array( width * height * 4 );
8510
+ renderer.readRenderTargetPixels( target, 0, 0, width, height, readBuffer );
8511
+ target.dispose();
8512
+
8513
+ for ( let i = 0, l = readBuffer.length; i < l; i ++ ) {
8514
+
8515
+ buffer[ i ] = three.DataUtils.toHalfFloat( readBuffer[ i ] );
8516
+
8517
+ }
8518
+
8519
+ // produce the data texture
8520
+ const result = new three.DataTexture( buffer, width, height, three.RGBAFormat, three.HalfFloatType );
8521
+ result.minFilter = three.LinearMipMapLinearFilter;
8522
+ result.magFilter = three.LinearFilter;
8523
+ result.wrapS = three.RepeatWrapping;
8524
+ result.wrapT = three.RepeatWrapping;
8525
+ result.mapping = three.EquirectangularReflectionMapping;
8526
+ result.needsUpdate = true;
8527
+
8528
+ return result;
8529
+
8530
+ }
8531
+
8532
+ dispose() {
8533
+
8534
+ this._quad.dispose();
8535
+
8536
+ }
8537
+
8538
+ }
8539
+
8186
8540
  function supportsFloatBlending( renderer ) {
8187
8541
 
8188
8542
  return renderer.extensions.get( 'EXT_float_blend' );
@@ -8258,6 +8612,24 @@ bool bvhIntersectFogVolumeHit(
8258
8612
 
8259
8613
  }
8260
8614
 
8615
+ get stableNoise() {
8616
+
8617
+ return this._pathTracer.stableNoise;
8618
+
8619
+ }
8620
+
8621
+ set stableNoise( v ) {
8622
+
8623
+ this._pathTracer.stableNoise = v;
8624
+
8625
+ }
8626
+
8627
+ get isCompiling() {
8628
+
8629
+ return Boolean( this._pathTracer.isCompiling );
8630
+
8631
+ }
8632
+
8261
8633
  constructor( renderer ) {
8262
8634
 
8263
8635
  // members
@@ -8266,6 +8638,7 @@ bool bvhIntersectFogVolumeHit(
8266
8638
  this._pathTracer = new PathTracingRenderer( renderer );
8267
8639
  this._queueReset = false;
8268
8640
  this._clock = new three.Clock();
8641
+ this._compilePromise = null;
8269
8642
 
8270
8643
  this._lowResPathTracer = new PathTracingRenderer( renderer );
8271
8644
  this._lowResPathTracer.tiles.set( 1, 1 );
@@ -8278,6 +8651,10 @@ bool bvhIntersectFogVolumeHit(
8278
8651
  } ) );
8279
8652
  this._materials = null;
8280
8653
 
8654
+ this._previousEnvironment = null;
8655
+ this._previousBackground = null;
8656
+ this._internalBackground = null;
8657
+
8281
8658
  // options
8282
8659
  this.renderDelay = 100;
8283
8660
  this.minSamples = 5;
@@ -8406,6 +8783,13 @@ bool bvhIntersectFogVolumeHit(
8406
8783
  const scene = this.scene;
8407
8784
  const material = this._pathTracer.material;
8408
8785
 
8786
+ if ( this._internalBackground ) {
8787
+
8788
+ this._internalBackground.dispose();
8789
+ this._internalBackground = null;
8790
+
8791
+ }
8792
+
8409
8793
  // update scene background
8410
8794
  material.backgroundBlur = scene.backgroundBlurriness;
8411
8795
  material.backgroundIntensity = scene.backgroundIntensity ?? 1;
@@ -8433,6 +8817,17 @@ bool bvhIntersectFogVolumeHit(
8433
8817
  material.backgroundMap = colorBackground;
8434
8818
  material.backgroundAlpha = 1;
8435
8819
 
8820
+ } else if ( scene.background.isCubeTexture ) {
8821
+
8822
+ if ( scene.background !== this._previousBackground ) {
8823
+
8824
+ const background = new CubeToEquirectGenerator( this._renderer ).generate( scene.background );
8825
+ this._internalBackground = background;
8826
+ material.backgroundMap = background;
8827
+ material.backgroundAlpha = 1;
8828
+
8829
+ }
8830
+
8436
8831
  } else {
8437
8832
 
8438
8833
  material.backgroundMap = scene.background;
@@ -8441,26 +8836,32 @@ bool bvhIntersectFogVolumeHit(
8441
8836
  }
8442
8837
 
8443
8838
  // update scene environment
8444
- material.environmentIntensity = scene.environmentIntensity ?? 1;
8839
+ material.environmentIntensity = scene.environment !== null ? ( scene.environmentIntensity ?? 1 ) : 0;
8445
8840
  material.environmentRotation.makeRotationFromEuler( scene.environmentRotation ).invert();
8446
8841
  if ( this._previousEnvironment !== scene.environment ) {
8447
8842
 
8448
- if ( scene.environment ) {
8843
+ if ( scene.environment !== null ) {
8449
8844
 
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 );
8845
+ if ( scene.environment.isCubeTexture ) {
8454
8846
 
8455
- } else {
8847
+ const environment = new CubeToEquirectGenerator( this._renderer ).generate( scene.environment );
8848
+ material.envMapInfo.updateFrom( environment );
8849
+
8850
+ } else {
8851
+
8852
+ // TODO: Consider setting this to the highest supported bit depth by checking for
8853
+ // OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
8854
+ // the equirect uniform
8855
+ material.envMapInfo.updateFrom( scene.environment );
8456
8856
 
8457
- material.environmentIntensity = 0;
8857
+ }
8458
8858
 
8459
8859
  }
8460
8860
 
8461
8861
  }
8462
8862
 
8463
8863
  this._previousEnvironment = scene.environment;
8864
+ this._previousBackground = scene.background;
8464
8865
  this.reset();
8465
8866
 
8466
8867
  }
@@ -8472,6 +8873,7 @@ bool bvhIntersectFogVolumeHit(
8472
8873
  geometry,
8473
8874
  bvh,
8474
8875
  bvhChanged,
8876
+ needsMaterialIndexUpdate,
8475
8877
  } = results;
8476
8878
 
8477
8879
  this._materials = materials;
@@ -8489,6 +8891,10 @@ bool bvhIntersectFogVolumeHit(
8489
8891
  geometry.attributes.color,
8490
8892
  );
8491
8893
 
8894
+ }
8895
+
8896
+ if ( needsMaterialIndexUpdate ) {
8897
+
8492
8898
  material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
8493
8899
 
8494
8900
  }
@@ -8531,7 +8937,7 @@ bool bvhIntersectFogVolumeHit(
8531
8937
  // render the path tracing sample after enough time has passed
8532
8938
  const delta = clock.getDelta() * 1e3;
8533
8939
  const elapsedTime = clock.getElapsedTime() * 1e3;
8534
- if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime ) {
8940
+ if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime && ! this.isCompiling ) {
8535
8941
 
8536
8942
  pathTracer.update();
8537
8943
 
@@ -8564,7 +8970,7 @@ bool bvhIntersectFogVolumeHit(
8564
8970
  // render the fallback if we haven't rendered enough samples, are paused, or are occluded
8565
8971
  if ( ! this.enablePathTracing || this.samples < minSamples || quad.material.opacity < 1 ) {
8566
8972
 
8567
- if ( this.dynamicLowRes ) {
8973
+ if ( this.dynamicLowRes && ! this.isCompiling ) {
8568
8974
 
8569
8975
  if ( lowResPathTracer.samples < 1 ) {
8570
8976
 
@@ -8579,7 +8985,9 @@ bool bvhIntersectFogVolumeHit(
8579
8985
  quad.render( renderer );
8580
8986
  quad.material.opacity = currentOpacity;
8581
8987
 
8582
- } else if ( this.rasterizeScene ) {
8988
+ }
8989
+
8990
+ if ( ! this.dynamicLowRes && this.rasterizeScene || this.dynamicLowRes && this.isCompiling ) {
8583
8991
 
8584
8992
  this.rasterizeSceneCallback( this.scene, this.camera );
8585
8993
 
@@ -8617,8 +9025,8 @@ bool bvhIntersectFogVolumeHit(
8617
9025
 
8618
9026
  dispose() {
8619
9027
 
8620
- this._renderQuad.dispose();
8621
- this._renderQuad.material.dispose();
9028
+ this._quad.dispose();
9029
+ this._quad.material.dispose();
8622
9030
  this._pathTracer.dispose();
8623
9031
 
8624
9032
  }