rayzee 5.4.0 → 5.4.2
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/dist/rayzee.es.js +2821 -2784
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +54 -54
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/PathTracerApp.js +1 -2
- package/src/Processor/AssetLoader.js +40 -18
- package/src/Processor/EquirectHDRInfo.js +38 -29
- package/src/Processor/GeometryExtractor.js +19 -2
- package/src/Processor/InstanceTable.js +16 -0
- package/src/Processor/SceneProcessor.js +22 -33
- package/src/Processor/ShaderBuilder.js +14 -17
- package/src/Processor/TLASBuilder.js +9 -4
- package/src/Stages/PathTracer.js +100 -43
- package/src/TSL/BVHTraversal.js +34 -74
- package/src/TSL/Clearcoat.js +1 -1
- package/src/TSL/Displacement.js +1 -1
- package/src/TSL/EmissiveSampling.js +17 -13
- package/src/TSL/Environment.js +12 -9
- package/src/TSL/LightBVHSampling.js +3 -2
- package/src/TSL/LightsCore.js +1 -1
- package/src/TSL/LightsDirect.js +14 -1
- package/src/TSL/LightsIndirect.js +0 -1
- package/src/TSL/LightsSampling.js +2 -2
- package/src/TSL/MaterialTransmission.js +1 -1
- package/src/TSL/PathTracer.js +4 -4
- package/src/TSL/PathTracerCore.js +6 -6
- package/src/TSL/Struct.js +1 -1
- package/src/TSL/TextureSampling.js +1 -1
- package/src/TSL/patches.js +145 -0
- package/src/index.js +1 -1
- package/src/managers/EnvironmentManager.js +32 -56
- package/src/managers/MaterialDataManager.js +93 -3
- package/src/managers/UniformManager.js +3 -0
- package/src/TSL/structProxy.js +0 -87
- package/src/TSL/wgslGlobalVarsPatch.js +0 -60
package/package.json
CHANGED
package/src/PathTracerApp.js
CHANGED
|
@@ -639,8 +639,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
639
639
|
|
|
640
640
|
if ( ! this._sdf.uploadToPathTracer( this.stages.pathTracer, this.lightManager, this.meshScene, environmentTexture ) ) return false;
|
|
641
641
|
|
|
642
|
-
//
|
|
643
|
-
// shader graph captures the storage node during compilation)
|
|
642
|
+
// Patch per-mesh visibility into the TLAS leaves we just uploaded
|
|
644
643
|
this.stages.pathTracer._meshRefs = this.stages.pathTracer._collectMeshRefs( this.meshScene );
|
|
645
644
|
this.stages.pathTracer.setMeshVisibilityData( this.stages.pathTracer._meshRefs );
|
|
646
645
|
|
|
@@ -472,20 +472,28 @@ export class AssetLoader extends EventDispatcher {
|
|
|
472
472
|
const loader = await this.createGLTFLoader();
|
|
473
473
|
loader.manager = manager;
|
|
474
474
|
|
|
475
|
-
|
|
475
|
+
try {
|
|
476
476
|
|
|
477
|
-
|
|
478
|
-
gltf => {
|
|
477
|
+
return await new Promise( ( resolve, reject ) => {
|
|
479
478
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.onModelLoad( this.targetModel ).then( () => resolve( gltf ) );
|
|
479
|
+
loader.parse( gltfContent, '',
|
|
480
|
+
gltf => {
|
|
483
481
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
482
|
+
if ( this.targetModel ) disposeObjectFromMemory( this.targetModel );
|
|
483
|
+
this.targetModel = gltf.scene;
|
|
484
|
+
this.onModelLoad( this.targetModel ).then( () => resolve( gltf ) );
|
|
487
485
|
|
|
488
|
-
|
|
486
|
+
},
|
|
487
|
+
error => reject( error )
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
} );
|
|
491
|
+
|
|
492
|
+
} finally {
|
|
493
|
+
|
|
494
|
+
this._disposeGLTFLoader( loader );
|
|
495
|
+
|
|
496
|
+
}
|
|
489
497
|
|
|
490
498
|
} else {
|
|
491
499
|
|
|
@@ -697,11 +705,10 @@ export class AssetLoader extends EventDispatcher {
|
|
|
697
705
|
|
|
698
706
|
}
|
|
699
707
|
|
|
700
|
-
//
|
|
708
|
+
// Returns a fresh loader each call — DRACOLoader/KTX2Loader hold persistent
|
|
709
|
+
// worker pools. Callers must invoke _disposeGLTFLoader() to terminate them.
|
|
701
710
|
async createGLTFLoader() {
|
|
702
711
|
|
|
703
|
-
if ( this.loaderCache.gltf ) return this.loaderCache.gltf;
|
|
704
|
-
|
|
705
712
|
const dracoLoader = new DRACOLoader();
|
|
706
713
|
dracoLoader.setDecoderConfig( { type: 'js' } );
|
|
707
714
|
dracoLoader.setDecoderPath( 'https://www.gstatic.com/draco/v1/decoders/' );
|
|
@@ -726,18 +733,23 @@ export class AssetLoader extends EventDispatcher {
|
|
|
726
733
|
|
|
727
734
|
}
|
|
728
735
|
|
|
729
|
-
this.loaderCache.ktx2 = ktx2Loader;
|
|
730
|
-
|
|
731
736
|
const loader = new GLTFLoader();
|
|
732
737
|
loader.setDRACOLoader( dracoLoader );
|
|
733
738
|
loader.setKTX2Loader( ktx2Loader );
|
|
734
739
|
loader.setMeshoptDecoder( MeshoptDecoder );
|
|
735
740
|
|
|
736
|
-
this.loaderCache.gltf = loader;
|
|
737
741
|
return loader;
|
|
738
742
|
|
|
739
743
|
}
|
|
740
744
|
|
|
745
|
+
_disposeGLTFLoader( loader ) {
|
|
746
|
+
|
|
747
|
+
if ( ! loader ) return;
|
|
748
|
+
loader.dracoLoader?.dispose();
|
|
749
|
+
loader.ktx2Loader?.dispose();
|
|
750
|
+
|
|
751
|
+
}
|
|
752
|
+
|
|
741
753
|
async loadExampleModels( index, modelFiles ) {
|
|
742
754
|
|
|
743
755
|
if ( ! modelFiles || ! modelFiles[ index ] ) {
|
|
@@ -753,9 +765,10 @@ export class AssetLoader extends EventDispatcher {
|
|
|
753
765
|
|
|
754
766
|
async loadModel( modelUrl ) {
|
|
755
767
|
|
|
768
|
+
const loader = await this.createGLTFLoader();
|
|
769
|
+
|
|
756
770
|
try {
|
|
757
771
|
|
|
758
|
-
const loader = await this.createGLTFLoader();
|
|
759
772
|
updateLoading( { status: "Loading Model...", progress: 2 } );
|
|
760
773
|
const data = await loader.loadAsync( modelUrl );
|
|
761
774
|
updateLoading( { status: "Processing Data...", progress: 10 } );
|
|
@@ -774,15 +787,20 @@ export class AssetLoader extends EventDispatcher {
|
|
|
774
787
|
this.dispatchEvent( { type: 'error', message: error.message, filename: modelUrl } );
|
|
775
788
|
throw error;
|
|
776
789
|
|
|
790
|
+
} finally {
|
|
791
|
+
|
|
792
|
+
this._disposeGLTFLoader( loader );
|
|
793
|
+
|
|
777
794
|
}
|
|
778
795
|
|
|
779
796
|
}
|
|
780
797
|
|
|
781
798
|
async loadGLBFromArrayBuffer( arrayBuffer, filename = 'model.glb' ) {
|
|
782
799
|
|
|
800
|
+
const loader = await this.createGLTFLoader();
|
|
801
|
+
|
|
783
802
|
try {
|
|
784
803
|
|
|
785
|
-
const loader = await this.createGLTFLoader();
|
|
786
804
|
updateLoading( { isLoading: true, status: "Processing GLB Data...", progress: 5 } );
|
|
787
805
|
await new Promise( r => setTimeout( r, 0 ) );
|
|
788
806
|
|
|
@@ -804,6 +822,10 @@ export class AssetLoader extends EventDispatcher {
|
|
|
804
822
|
this.dispatchEvent( { type: 'error', message: error.message, filename } );
|
|
805
823
|
throw error;
|
|
806
824
|
|
|
825
|
+
} finally {
|
|
826
|
+
|
|
827
|
+
this._disposeGLTFLoader( loader );
|
|
828
|
+
|
|
807
829
|
}
|
|
808
830
|
|
|
809
831
|
}
|
|
@@ -220,53 +220,62 @@ export class EquirectHDRInfo {
|
|
|
220
220
|
|
|
221
221
|
const { floatData, width, height } = extractFloatData( hdr );
|
|
222
222
|
|
|
223
|
-
//
|
|
224
|
-
|
|
223
|
+
// Fresh worker per call — terminated in finally to avoid ~30 MB residency.
|
|
224
|
+
try {
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
this._worker = new Worker( CDF_WORKER_URL, { type: 'module' } );
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
} catch ( e ) {
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
231
|
+
this._worker = await fetchAsWorker( CDF_WORKER_URL );
|
|
231
232
|
|
|
232
|
-
|
|
233
|
-
this._worker = await fetchAsWorker( CDF_WORKER_URL );
|
|
233
|
+
}
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
try {
|
|
236
236
|
|
|
237
|
-
|
|
237
|
+
const result = await new Promise( ( resolve, reject ) => {
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
this._worker.onmessage = ( e ) => {
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
if ( e.data.error ) {
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
reject( new Error( e.data.error ) );
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
} else {
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
resolve( e.data );
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
}
|
|
250
250
|
|
|
251
|
-
}
|
|
251
|
+
};
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
this._worker.onerror = reject;
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
// Transfer floatData to worker (zero-copy)
|
|
256
|
+
this._worker.postMessage(
|
|
257
|
+
{ floatData, width, height },
|
|
258
|
+
[ floatData.buffer ]
|
|
259
|
+
);
|
|
256
260
|
|
|
257
|
-
|
|
258
|
-
this._worker.postMessage(
|
|
259
|
-
{ floatData, width, height },
|
|
260
|
-
[ floatData.buffer ]
|
|
261
|
-
);
|
|
261
|
+
} );
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
this.marginalData = result.marginalData;
|
|
264
|
+
this.conditionalData = result.conditionalData;
|
|
265
|
+
this.totalSum = result.totalSum;
|
|
266
|
+
this.width = result.width;
|
|
267
|
+
this.height = result.height;
|
|
264
268
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
} finally {
|
|
270
|
+
|
|
271
|
+
if ( this._worker ) {
|
|
272
|
+
|
|
273
|
+
this._worker.terminate();
|
|
274
|
+
this._worker = null;
|
|
275
|
+
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
}
|
|
270
279
|
|
|
271
280
|
}
|
|
272
281
|
|
|
@@ -618,7 +618,22 @@ export class GeometryExtractor {
|
|
|
618
618
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 0 ] = normalA.x;
|
|
619
619
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 1 ] = normalA.y;
|
|
620
620
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 2 ] = normalA.z;
|
|
621
|
-
|
|
621
|
+
// Repurposed padding: opaque-blocker fast-path flag for shadow rays.
|
|
622
|
+
// 1.0 = surface fully blocks light (no alpha, transmission, or transparency) →
|
|
623
|
+
// traceShadowRay can skip the 7-slot getShadowMaterial fetch.
|
|
624
|
+
// 0.0 = requires full material evaluation.
|
|
625
|
+
{
|
|
626
|
+
|
|
627
|
+
const mat = this.materials[ materialIndex ];
|
|
628
|
+
const isOpaqueBlocker = mat
|
|
629
|
+
&& ( mat.alphaMode | 0 ) === 0
|
|
630
|
+
&& ( mat.transparent | 0 ) === 0
|
|
631
|
+
&& ( mat.transmission || 0 ) === 0
|
|
632
|
+
&& ( mat.opacity ?? 1 ) >= 1
|
|
633
|
+
? 1.0 : 0.0;
|
|
634
|
+
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 3 ] = isOpaqueBlocker;
|
|
635
|
+
|
|
636
|
+
}
|
|
622
637
|
|
|
623
638
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET + 0 ] = normalB.x;
|
|
624
639
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET + 1 ] = normalB.y;
|
|
@@ -628,7 +643,9 @@ export class GeometryExtractor {
|
|
|
628
643
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 0 ] = normalC.x;
|
|
629
644
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 1 ] = normalC.y;
|
|
630
645
|
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 2 ] = normalC.z;
|
|
631
|
-
|
|
646
|
+
// Repurposed padding: per-triangle side flag (0=front, 1=back, 2=double).
|
|
647
|
+
// Lets BVH traversal do side culling without a material-buffer read per hit.
|
|
648
|
+
this.triangleData[ offset + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 3 ] = this.materials[ materialIndex ]?.side ?? 0;
|
|
632
649
|
|
|
633
650
|
// UVs and material index (2 vec4s = 8 floats)
|
|
634
651
|
// First vec4: uvA.x, uvA.y, uvB.x, uvB.y
|
|
@@ -57,10 +57,26 @@ export class InstanceTable {
|
|
|
57
57
|
worldAABB: null, // Computed from triangle data
|
|
58
58
|
originalToBvhMap,
|
|
59
59
|
bvhData,
|
|
60
|
+
visible: true, // Per-mesh visibility (baked into TLAS leaf slot [2])
|
|
61
|
+
tlasLeafIndex: - 1, // Set by TLASBuilder.flatten() — enables in-place visibility patching
|
|
60
62
|
};
|
|
61
63
|
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Set per-mesh visibility flag. Does NOT update the GPU buffer —
|
|
68
|
+
* caller must patch combinedBvhData[tlasLeafIndex*16 + 2] and mark bvh attr dirty.
|
|
69
|
+
*
|
|
70
|
+
* @param {number} meshIndex
|
|
71
|
+
* @param {boolean} visible
|
|
72
|
+
*/
|
|
73
|
+
setVisibility( meshIndex, visible ) {
|
|
74
|
+
|
|
75
|
+
const entry = this.entries[ meshIndex ];
|
|
76
|
+
if ( entry ) entry.visible = visible;
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
64
80
|
/**
|
|
65
81
|
* Compute world-space AABBs for all entries from their BLAS root node data.
|
|
66
82
|
* O(1) per mesh for inner roots; falls back to triangle scan for leaf roots (rare).
|
|
@@ -476,48 +476,36 @@ export class SceneProcessor {
|
|
|
476
476
|
|
|
477
477
|
const validEntries = this.instanceTable.entries.filter( e => e !== null );
|
|
478
478
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
this.instanceTable.assignOffsets( 0 ); // BLAS at offset 0
|
|
486
|
-
this._buildGlobalOriginalToBvhMap();
|
|
487
|
-
entry.originalToBvhMap = null;
|
|
488
|
-
entry.bvhData = null;
|
|
489
|
-
|
|
490
|
-
} else {
|
|
491
|
-
|
|
492
|
-
// Multi-mesh — build TLAS over mesh AABBs
|
|
493
|
-
this.instanceTable.computeAABBs( this.triangleData );
|
|
494
|
-
const { root: tlasRoot, nodeCount: tlasNodeCount } = this.tlasBuilder.build( validEntries );
|
|
479
|
+
// Always build a TLAS — even for a single mesh — so the BLAS-pointer leaf
|
|
480
|
+
// carries packed per-mesh visibility in its slot [2]. The 1-node TLAS
|
|
481
|
+
// overhead (one extra leaf fetch per ray) is negligible and eliminates
|
|
482
|
+
// a dedicated visibility storage buffer binding.
|
|
483
|
+
this.instanceTable.computeAABBs( this.triangleData );
|
|
484
|
+
const { root: tlasRoot, nodeCount: tlasNodeCount } = this.tlasBuilder.build( validEntries );
|
|
495
485
|
|
|
496
|
-
|
|
497
|
-
|
|
486
|
+
this.instanceTable.assignOffsets( tlasNodeCount );
|
|
487
|
+
const totalNodes = this.instanceTable.totalNodeCount;
|
|
498
488
|
|
|
499
|
-
|
|
489
|
+
const tlasData = this.tlasBuilder.flatten( tlasRoot, validEntries );
|
|
500
490
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
491
|
+
// Assemble combined buffer: [TLAS][BLAS_0][BLAS_1]...[BLAS_M]
|
|
492
|
+
this.bvhData = new Float32Array( totalNodes * 16 );
|
|
493
|
+
this.bvhData.set( tlasData );
|
|
504
494
|
|
|
505
|
-
|
|
495
|
+
for ( const entry of validEntries ) {
|
|
506
496
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
497
|
+
const destOffset = entry.blasOffset * 16;
|
|
498
|
+
this.bvhData.set( entry.bvhData, destOffset );
|
|
499
|
+
this._offsetBLASInPlace( destOffset, entry.bvhData.length / 16, entry.blasOffset, entry.triOffset );
|
|
510
500
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
this._buildGlobalOriginalToBvhMap();
|
|
501
|
+
}
|
|
514
502
|
|
|
515
|
-
|
|
503
|
+
this._buildGlobalOriginalToBvhMap();
|
|
516
504
|
|
|
517
|
-
|
|
518
|
-
entry.bvhData = null;
|
|
505
|
+
for ( const entry of validEntries ) {
|
|
519
506
|
|
|
520
|
-
|
|
507
|
+
entry.originalToBvhMap = null;
|
|
508
|
+
entry.bvhData = null;
|
|
521
509
|
|
|
522
510
|
}
|
|
523
511
|
|
|
@@ -1384,6 +1372,7 @@ export class SceneProcessor {
|
|
|
1384
1372
|
}
|
|
1385
1373
|
|
|
1386
1374
|
pathTracer.setBVHData( this.bvhData );
|
|
1375
|
+
pathTracer.setInstanceTable( this.instanceTable );
|
|
1387
1376
|
|
|
1388
1377
|
if ( this.materialData ) {
|
|
1389
1378
|
|
|
@@ -16,7 +16,6 @@ import { Fn, texture, vec2, float, int, uniform, If,
|
|
|
16
16
|
import { TextureNode } from 'three/webgpu';
|
|
17
17
|
import { LinearFilter, DataArrayTexture } from 'three';
|
|
18
18
|
import { pathTracerMain } from '../TSL/PathTracer.js';
|
|
19
|
-
import { setMeshVisibilityBuffer } from '../TSL/BVHTraversal.js';
|
|
20
19
|
import { setShadowAlbedoMaps, setAlphaShadowsUniform } from '../TSL/LightsDirect.js';
|
|
21
20
|
import { BuildTimer } from './BuildTimer.js';
|
|
22
21
|
|
|
@@ -234,11 +233,9 @@ export class ShaderBuilder {
|
|
|
234
233
|
const triStorage = stage.triangleStorageNode;
|
|
235
234
|
const bvhStorage = stage.bvhStorageNode;
|
|
236
235
|
const matStorage = stage.materialData.materialStorageNode;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// Set per-mesh visibility buffer (module-level in BVHTraversal.js, read during graph construction)
|
|
241
|
-
setMeshVisibilityBuffer( stage.meshVisibilityStorageNode );
|
|
236
|
+
// Packed light buffer — [lightBVH | emissive triangles]. One node fed to both
|
|
237
|
+
// TSL params; emissive reads offset by stage.emissiveVec4Offset.
|
|
238
|
+
const lightBufferStorage = stage.lightStorageNode;
|
|
242
239
|
|
|
243
240
|
// Set alpha-shadow uniform (module-level in LightsDirect.js, read at runtime)
|
|
244
241
|
setAlphaShadowsUniform( stage.uniforms.get( 'enableAlphaShadows' ) );
|
|
@@ -249,9 +246,9 @@ export class ShaderBuilder {
|
|
|
249
246
|
const adaptiveSamplingTex = new TextureNode();
|
|
250
247
|
this.adaptiveSamplingTexNode = adaptiveSamplingTex;
|
|
251
248
|
|
|
252
|
-
// Environment importance sampling CDF
|
|
253
|
-
|
|
254
|
-
const
|
|
249
|
+
// Environment importance sampling CDF — packed storage buffer
|
|
250
|
+
// Layout: [marginal (envResolution.y floats) | conditional (envResolution.x * envResolution.y floats)]
|
|
251
|
+
const envCDFStorage = stage.environment.envCDFStorageNode;
|
|
255
252
|
|
|
256
253
|
// Previous-frame texture nodes — initialized from readTarget textures
|
|
257
254
|
const readTextures = storageTextures.getReadTextures();
|
|
@@ -285,8 +282,8 @@ export class ShaderBuilder {
|
|
|
285
282
|
setShadowAlbedoMaps( albedoMapsTex );
|
|
286
283
|
|
|
287
284
|
const result = {
|
|
288
|
-
triStorage, bvhStorage, matStorage,
|
|
289
|
-
envTex, adaptiveSamplingTex,
|
|
285
|
+
triStorage, bvhStorage, matStorage, lightBufferStorage,
|
|
286
|
+
envTex, adaptiveSamplingTex, envCDFStorage,
|
|
290
287
|
albedoMapsTex, normalMapsTex, bumpMapsTex,
|
|
291
288
|
metalnessMapsTex, roughnessMapsTex, emissiveMapsTex, displacementMapsTex,
|
|
292
289
|
};
|
|
@@ -304,8 +301,8 @@ export class ShaderBuilder {
|
|
|
304
301
|
writeColorTex, writeNDTex, writeAlbedoTex ) {
|
|
305
302
|
|
|
306
303
|
const {
|
|
307
|
-
triStorage, bvhStorage, matStorage,
|
|
308
|
-
envTex, adaptiveSamplingTex,
|
|
304
|
+
triStorage, bvhStorage, matStorage, lightBufferStorage,
|
|
305
|
+
envTex, adaptiveSamplingTex, envCDFStorage,
|
|
309
306
|
albedoMapsTex, normalMapsTex, bumpMapsTex,
|
|
310
307
|
metalnessMapsTex, roughnessMapsTex, emissiveMapsTex, displacementMapsTex,
|
|
311
308
|
} = textureNodes;
|
|
@@ -366,8 +363,7 @@ export class ShaderBuilder {
|
|
|
366
363
|
envTexture: envTex,
|
|
367
364
|
environmentIntensity: stage.environmentIntensity,
|
|
368
365
|
envMatrix: stage.environmentMatrix,
|
|
369
|
-
|
|
370
|
-
envConditionalWeights: conditionalCDFStorage,
|
|
366
|
+
envCDFBuffer: envCDFStorage,
|
|
371
367
|
envTotalSum: stage.envTotalSum,
|
|
372
368
|
envResolution: stage.envResolution,
|
|
373
369
|
enableEnvironmentLight: stage.enableEnvironment,
|
|
@@ -381,11 +377,12 @@ export class ShaderBuilder {
|
|
|
381
377
|
globalIlluminationIntensity: stage.globalIlluminationIntensity,
|
|
382
378
|
totalTriangleCount: stage.totalTriangleCount,
|
|
383
379
|
enableEmissiveTriangleSampling: stage.enableEmissiveTriangleSampling,
|
|
384
|
-
emissiveTriangleBuffer:
|
|
380
|
+
emissiveTriangleBuffer: lightBufferStorage,
|
|
385
381
|
emissiveTriangleCount: stage.emissiveTriangleCount,
|
|
386
382
|
emissiveTotalPower: stage.emissiveTotalPower,
|
|
387
383
|
emissiveBoost: stage.emissiveBoost,
|
|
388
|
-
|
|
384
|
+
emissiveVec4Offset: stage.emissiveVec4Offset,
|
|
385
|
+
lightBVHBuffer: lightBufferStorage,
|
|
389
386
|
lightBVHNodeCount: stage.lightBVHNodeCount,
|
|
390
387
|
debugVisScale: stage.debugVisScale,
|
|
391
388
|
enableAccumulation: stage.enableAccumulation,
|
|
@@ -199,10 +199,13 @@ export class TLASBuilder {
|
|
|
199
199
|
/**
|
|
200
200
|
* Flatten TLAS tree into Float32Array.
|
|
201
201
|
* Inner nodes: same format as BVH.
|
|
202
|
-
* Leaf nodes: [blasRootNodeIndex,
|
|
202
|
+
* Leaf nodes: [blasRootNodeIndex, meshIndex, visibility, -2] (BLAS-pointer marker).
|
|
203
|
+
*
|
|
204
|
+
* Side effect: records each entry's flat leaf index on `entry.tlasLeafIndex` so that
|
|
205
|
+
* visibility can later be patched in place (combinedBvhData[tlasLeafIndex*16 + 2]).
|
|
203
206
|
*
|
|
204
207
|
* @param {TLASNode} root
|
|
205
|
-
* @param {Array<{blasOffset: number
|
|
208
|
+
* @param {Array<{blasOffset: number, visible: boolean, tlasLeafIndex: number}>} entries
|
|
206
209
|
* @returns {Float32Array}
|
|
207
210
|
*/
|
|
208
211
|
flatten( root, entries ) {
|
|
@@ -268,10 +271,12 @@ export class TLASBuilder {
|
|
|
268
271
|
// Leaf node — BLAS pointer
|
|
269
272
|
const entry = entries[ n.entryIndex ];
|
|
270
273
|
data[ o ] = entry.blasOffset; // Absolute node index of BLAS root in combined buffer
|
|
271
|
-
data[ o + 1 ] = n.entryIndex; // meshIndex for
|
|
272
|
-
|
|
274
|
+
data[ o + 1 ] = n.entryIndex; // meshIndex (kept for debug/ID — traversal uses slot [2])
|
|
275
|
+
data[ o + 2 ] = entry.visible === false ? 0.0 : 1.0; // Per-mesh visibility (packed — frees a binding)
|
|
273
276
|
data[ o + 3 ] = BVH_LEAF_MARKERS.BLAS_POINTER_LEAF; // -2 marker
|
|
274
277
|
|
|
278
|
+
entry.tlasLeafIndex = i;
|
|
279
|
+
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
}
|