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.
- package/build/index.module.js +411 -44
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +410 -43
- package/build/index.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/core/PathTracingRenderer.js +48 -1
- package/src/core/WebGLPathTracer.js +62 -8
- package/src/core/utils/BakedGeometry.js +23 -0
- package/src/core/utils/BufferAttributeUtils.js +11 -3
- package/src/core/utils/MeshDiff.js +6 -13
- package/src/core/utils/StaticGeometryGenerator.js +19 -4
- package/src/materials/MaterialBase.js +11 -0
- package/src/uniforms/StratifiedSamplesTexture.js +55 -3
- package/src/uniforms/stratified/StratifiedSampler.js +19 -7
- package/src/uniforms/stratified/StratifiedSamplerCombined.js +14 -4
- package/src/utils/CubeToEquirectGenerator.js +159 -0
package/build/index.module.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
452
|
-
|
|
453
|
-
for ( const attr of attributes ) {
|
|
452
|
+
const keys = Object.keys( attributes ).sort();
|
|
453
|
+
for ( const key of keys ) {
|
|
454
454
|
|
|
455
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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 +
|
|
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.
|
|
3619
|
+
this.reshuffle = function () {
|
|
3559
3620
|
|
|
3560
3621
|
for ( const strata of strataObjs ) {
|
|
3561
3622
|
|
|
3562
|
-
strata.
|
|
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
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
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
|
-
}
|
|
8945
|
+
}
|
|
8946
|
+
|
|
8947
|
+
if ( ! this.dynamicLowRes && this.rasterizeScene || this.dynamicLowRes && this.isCompiling ) {
|
|
8581
8948
|
|
|
8582
8949
|
this.rasterizeSceneCallback( this.scene, this.camera );
|
|
8583
8950
|
|