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/src/Stages/PathTracer.js
CHANGED
|
@@ -114,6 +114,19 @@ export class PathTracer extends RenderStage {
|
|
|
114
114
|
// Initialize material data manager
|
|
115
115
|
this.materialData = new MaterialDataManager( this.sdfs );
|
|
116
116
|
this.materialData.callbacks.onReset = () => this.reset();
|
|
117
|
+
// Triangle data carries the per-triangle `side` flag (NORMAL_C.w). The
|
|
118
|
+
// authoritative CPU array is triangleStorageAttr.array (not sdfs.triangleData,
|
|
119
|
+
// which isn't populated on the PathTracerApp build path). The patch mutates
|
|
120
|
+
// the array in place — only a dirty flag is needed for GPU re-upload.
|
|
121
|
+
this.materialData.callbacks.getTriangleData = () => ( {
|
|
122
|
+
array: this.triangleStorageAttr?.array,
|
|
123
|
+
count: this.triangleCount,
|
|
124
|
+
} );
|
|
125
|
+
this.materialData.callbacks.onTriangleDataChanged = () => {
|
|
126
|
+
|
|
127
|
+
if ( this.triangleStorageAttr ) this.triangleStorageAttr.needsUpdate = true;
|
|
128
|
+
|
|
129
|
+
};
|
|
117
130
|
|
|
118
131
|
// Initialize environment manager
|
|
119
132
|
this.environment = new EnvironmentManager( this.scene, this.uniforms );
|
|
@@ -180,17 +193,20 @@ export class PathTracer extends RenderStage {
|
|
|
180
193
|
// Blue noise
|
|
181
194
|
this.blueNoiseTexture = null;
|
|
182
195
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
// Packed light buffer — [lightBVH nodes (4 vec4s each) | emissive triangles (2 vec4s each)]
|
|
197
|
+
// emissiveVec4Offset uniform tracks the vec4-count offset where emissive data starts.
|
|
198
|
+
// Initialized with dummy data so TSL compilation never sees null.
|
|
199
|
+
this.lightStorageAttr = new StorageInstancedBufferAttribute( new Float32Array( 16 ), 4 );
|
|
200
|
+
this.lightStorageNode = storage( this.lightStorageAttr, 'vec4', 1 ).toReadOnly();
|
|
186
201
|
|
|
187
|
-
//
|
|
188
|
-
this.
|
|
189
|
-
this.
|
|
202
|
+
// Cached CPU-side data — rebuilt into the packed buffer whenever either source changes.
|
|
203
|
+
this._lbvhDataCache = null;
|
|
204
|
+
this._emissiveDataCache = null;
|
|
190
205
|
|
|
191
|
-
// Per-mesh visibility
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
// Per-mesh visibility is packed into the TLAS BLAS-pointer leaf's slot [2]
|
|
207
|
+
// (see TLASBuilder.flatten + BVHTraversal.js). The InstanceTable holds the
|
|
208
|
+
// tlasLeafIndex for each mesh so we can patch visibility in place.
|
|
209
|
+
this._instanceTable = null;
|
|
194
210
|
|
|
195
211
|
// Adaptive sampling
|
|
196
212
|
this.adaptiveSamplingTexture = null;
|
|
@@ -454,6 +470,7 @@ export class PathTracer extends RenderStage {
|
|
|
454
470
|
// Set data references
|
|
455
471
|
this.setTriangleData( this.sdfs.triangleData, this.sdfs.triangleCount );
|
|
456
472
|
this.setBVHData( this.sdfs.bvhData );
|
|
473
|
+
this.setInstanceTable( this.sdfs.instanceTable );
|
|
457
474
|
this.materialData.setMaterialData( this.sdfs.materialData );
|
|
458
475
|
|
|
459
476
|
// Update triangle count
|
|
@@ -768,61 +785,78 @@ export class PathTracer extends RenderStage {
|
|
|
768
785
|
}
|
|
769
786
|
|
|
770
787
|
/**
|
|
771
|
-
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
* @param {Array} meshes - Array of Three.js mesh objects
|
|
788
|
+
* Bind the InstanceTable used to locate each mesh's TLAS leaf for in-place
|
|
789
|
+
* visibility patching. Called by SceneProcessor during upload.
|
|
790
|
+
* @param {import('../Processor/InstanceTable.js').InstanceTable} instanceTable
|
|
775
791
|
*/
|
|
776
|
-
|
|
792
|
+
setInstanceTable( instanceTable ) {
|
|
777
793
|
|
|
778
|
-
|
|
794
|
+
this._instanceTable = instanceTable;
|
|
779
795
|
|
|
780
|
-
|
|
781
|
-
// One vec4 per mesh — visibility stored in .x (simple indexing on GPU)
|
|
782
|
-
const data = new Float32Array( meshCount * 4 );
|
|
796
|
+
}
|
|
783
797
|
|
|
784
|
-
|
|
798
|
+
/**
|
|
799
|
+
* Initialize packed visibility for each mesh from current world-visibility.
|
|
800
|
+
* Patches the TLAS leaf slots in the combined BVH buffer that was just uploaded.
|
|
801
|
+
* @param {Array} meshes - Array of Three.js mesh objects, ordered by meshIndex
|
|
802
|
+
*/
|
|
803
|
+
setMeshVisibilityData( meshes ) {
|
|
785
804
|
|
|
786
|
-
|
|
805
|
+
if ( ! meshes || meshes.length === 0 || ! this._instanceTable ) return;
|
|
806
|
+
|
|
807
|
+
for ( let i = 0; i < meshes.length; i ++ ) {
|
|
808
|
+
|
|
809
|
+
this._patchTLASLeafVisibility( i, this._isWorldVisible( meshes[ i ] ) );
|
|
787
810
|
|
|
788
811
|
}
|
|
789
812
|
|
|
790
|
-
this.
|
|
791
|
-
this.meshVisibilityStorageNode.value = this.meshVisibilityStorageAttr;
|
|
792
|
-
this.meshVisibilityStorageNode.bufferCount = meshCount;
|
|
813
|
+
if ( this.bvhStorageAttr ) this.bvhStorageAttr.needsUpdate = true;
|
|
793
814
|
|
|
794
815
|
}
|
|
795
816
|
|
|
796
817
|
/**
|
|
797
|
-
* Update visibility for a single mesh
|
|
818
|
+
* Update visibility for a single mesh by patching its TLAS leaf slot [2].
|
|
798
819
|
* @param {number} meshIndex
|
|
799
820
|
* @param {boolean} visible
|
|
800
821
|
*/
|
|
801
822
|
updateMeshVisibility( meshIndex, visible ) {
|
|
802
823
|
|
|
803
|
-
if ( ! this.
|
|
804
|
-
|
|
805
|
-
this.meshVisibilityStorageAttr.array[ meshIndex * 4 ] = visible ? 1.0 : 0.0;
|
|
806
|
-
this.meshVisibilityStorageAttr.needsUpdate = true;
|
|
824
|
+
if ( ! this._patchTLASLeafVisibility( meshIndex, visible ) ) return;
|
|
825
|
+
if ( this.bvhStorageAttr ) this.bvhStorageAttr.needsUpdate = true;
|
|
807
826
|
|
|
808
827
|
}
|
|
809
828
|
|
|
810
829
|
/**
|
|
811
|
-
* Recompute world-visibility for all meshes and
|
|
830
|
+
* Recompute world-visibility for all meshes and patch TLAS leaves in place.
|
|
812
831
|
* Call this when group visibility changes at runtime.
|
|
813
832
|
*/
|
|
814
833
|
updateAllMeshVisibility() {
|
|
815
834
|
|
|
816
|
-
if ( ! this._meshRefs || ! this.
|
|
835
|
+
if ( ! this._meshRefs || ! this._instanceTable ) return;
|
|
817
836
|
|
|
818
|
-
const data = this.meshVisibilityStorageAttr.array;
|
|
819
837
|
for ( let i = 0; i < this._meshRefs.length; i ++ ) {
|
|
820
838
|
|
|
821
|
-
|
|
839
|
+
this._patchTLASLeafVisibility( i, this._isWorldVisible( this._meshRefs[ i ] ) );
|
|
822
840
|
|
|
823
841
|
}
|
|
824
842
|
|
|
825
|
-
this.
|
|
843
|
+
if ( this.bvhStorageAttr ) this.bvhStorageAttr.needsUpdate = true;
|
|
844
|
+
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Patch a single TLAS leaf's visibility flag in the combined BVH buffer.
|
|
849
|
+
* Returns true if the patch was applied.
|
|
850
|
+
* @private
|
|
851
|
+
*/
|
|
852
|
+
_patchTLASLeafVisibility( meshIndex, visible ) {
|
|
853
|
+
|
|
854
|
+
const entry = this._instanceTable?.entries?.[ meshIndex ];
|
|
855
|
+
if ( ! entry || entry.tlasLeafIndex < 0 || ! this.bvhStorageAttr ) return false;
|
|
856
|
+
|
|
857
|
+
entry.visible = visible;
|
|
858
|
+
this.bvhStorageAttr.array[ entry.tlasLeafIndex * 16 + 2 ] = visible ? 1.0 : 0.0;
|
|
859
|
+
return true;
|
|
826
860
|
|
|
827
861
|
}
|
|
828
862
|
|
|
@@ -1493,18 +1527,43 @@ export class PathTracer extends RenderStage {
|
|
|
1493
1527
|
|
|
1494
1528
|
}
|
|
1495
1529
|
|
|
1496
|
-
|
|
1530
|
+
/**
|
|
1531
|
+
* Rebuild the packed light buffer from cached lightBVH + emissive data.
|
|
1532
|
+
* Layout: [ lightBVH (LBVH_STRIDE vec4s per node) | emissive (EMISSIVE_STRIDE vec4s per entry) ].
|
|
1533
|
+
* Also updates `emissiveVec4Offset` uniform (in vec4 elements).
|
|
1534
|
+
* @private
|
|
1535
|
+
*/
|
|
1536
|
+
_rebuildLightBuffer() {
|
|
1497
1537
|
|
|
1498
|
-
|
|
1538
|
+
const LBVH_STRIDE = 4; // vec4s per LBVH node — must match LightBVHSampling.js
|
|
1539
|
+
const lbvh = this._lbvhDataCache;
|
|
1540
|
+
const emis = this._emissiveDataCache;
|
|
1541
|
+
const lbvhLen = lbvh ? lbvh.length : 0;
|
|
1542
|
+
const emisLen = emis ? emis.length : 0;
|
|
1543
|
+
|
|
1544
|
+
// Ensure at least a minimal non-empty buffer so GPU allocation remains valid.
|
|
1545
|
+
const totalLen = Math.max( lbvhLen + emisLen, 4 );
|
|
1546
|
+
const combined = new Float32Array( totalLen );
|
|
1547
|
+
if ( lbvh ) combined.set( lbvh, 0 );
|
|
1548
|
+
if ( emis ) combined.set( emis, lbvhLen );
|
|
1549
|
+
|
|
1550
|
+
this.lightStorageAttr = new StorageInstancedBufferAttribute( combined, 4 );
|
|
1551
|
+
this.lightStorageNode.value = this.lightStorageAttr;
|
|
1552
|
+
this.lightStorageNode.bufferCount = combined.length / 4;
|
|
1553
|
+
|
|
1554
|
+
// Offset (in vec4 elements) where emissive data starts.
|
|
1555
|
+
this.emissiveVec4Offset.value = ( this.lightBVHNodeCount.value || 0 ) * LBVH_STRIDE;
|
|
1499
1556
|
|
|
1500
|
-
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
setEmissiveTriangleData( emissiveData, count, totalPower = 0 ) {
|
|
1501
1560
|
|
|
1502
|
-
|
|
1503
|
-
this.emissiveTriangleStorageNode.value = this.emissiveTriangleStorageAttr;
|
|
1504
|
-
this.emissiveTriangleStorageNode.bufferCount = vec4Count;
|
|
1561
|
+
if ( ! emissiveData ) return;
|
|
1505
1562
|
|
|
1563
|
+
this._emissiveDataCache = emissiveData;
|
|
1506
1564
|
this.emissiveTriangleCount.value = count;
|
|
1507
1565
|
this.emissiveTotalPower.value = totalPower;
|
|
1566
|
+
this._rebuildLightBuffer();
|
|
1508
1567
|
console.log( `PathTracer: ${count} emissive triangles, totalPower=${totalPower.toFixed( 4 )} (storage buffer)` );
|
|
1509
1568
|
|
|
1510
1569
|
}
|
|
@@ -1513,11 +1572,9 @@ export class PathTracer extends RenderStage {
|
|
|
1513
1572
|
|
|
1514
1573
|
if ( ! nodeData ) return;
|
|
1515
1574
|
|
|
1516
|
-
|
|
1517
|
-
this.lightBVHStorageAttr = new StorageInstancedBufferAttribute( nodeData, 4 );
|
|
1518
|
-
this.lightBVHStorageNode.value = this.lightBVHStorageAttr;
|
|
1519
|
-
this.lightBVHStorageNode.bufferCount = vec4Count;
|
|
1575
|
+
this._lbvhDataCache = nodeData;
|
|
1520
1576
|
this.lightBVHNodeCount.value = nodeCount;
|
|
1577
|
+
this._rebuildLightBuffer();
|
|
1521
1578
|
console.log( `PathTracer: Light BVH ${nodeCount} nodes` );
|
|
1522
1579
|
|
|
1523
1580
|
}
|
package/src/TSL/BVHTraversal.js
CHANGED
|
@@ -43,19 +43,8 @@ const BVH_STRIDE = 4;
|
|
|
43
43
|
const TRI_STRIDE = 8;
|
|
44
44
|
const HUGE_VAL = 1e8;
|
|
45
45
|
|
|
46
|
-
// Per-mesh visibility
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Set the per-mesh visibility storage buffer node.
|
|
51
|
-
* Must be called before the shader graph is constructed (i.e., before setupCompute).
|
|
52
|
-
* @param {StorageNode} buffer - TSL storage node indexed by meshIndex
|
|
53
|
-
*/
|
|
54
|
-
export function setMeshVisibilityBuffer( buffer ) {
|
|
55
|
-
|
|
56
|
-
_meshVisibilityBuffer = buffer;
|
|
57
|
-
|
|
58
|
-
}
|
|
46
|
+
// Per-mesh visibility is now packed into the TLAS BLAS-pointer leaf's slot [2]
|
|
47
|
+
// by TLASBuilder.flatten() — eliminates the dedicated meshVisibility storage buffer.
|
|
59
48
|
|
|
60
49
|
// ================================================================================
|
|
61
50
|
// STACK HELPERS (Native WGSL array via TSL ArrayNode)
|
|
@@ -235,28 +224,34 @@ export const traverseBVH = Fn( ( [
|
|
|
235
224
|
const u = triResult.y;
|
|
236
225
|
const v = triResult.z;
|
|
237
226
|
|
|
238
|
-
// Fetch normals
|
|
227
|
+
// Fetch normals for side-culling (3 reads). Slot 7 (uvData2,
|
|
228
|
+
// carries matIdx + meshIndex) is deferred to post-traversal —
|
|
229
|
+
// it's only needed for the one winning triangle, not per candidate.
|
|
230
|
+
// normalCData.w carries the per-triangle side flag (0/1/2).
|
|
239
231
|
const nA = getDatafromStorageBuffer( triangleBuffer, triIndex, int( 3 ), int( TRI_STRIDE ) ).xyz;
|
|
240
232
|
const nB = getDatafromStorageBuffer( triangleBuffer, triIndex, int( 4 ), int( TRI_STRIDE ) ).xyz;
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
const matIdx = int( uvData2.z );
|
|
233
|
+
const normalCData = getDatafromStorageBuffer( triangleBuffer, triIndex, int( 5 ), int( TRI_STRIDE ) );
|
|
234
|
+
const nC = normalCData.xyz;
|
|
235
|
+
const side = int( normalCData.w ).toVar();
|
|
245
236
|
|
|
246
237
|
// Interpolate normal
|
|
247
238
|
const w = float( 1.0 ).sub( u ).sub( v );
|
|
248
239
|
const normal = normalize( nA.mul( w ).add( nB.mul( u ) ).add( nC.mul( v ) ) ).toVar();
|
|
249
240
|
|
|
250
|
-
// Side culling
|
|
251
|
-
|
|
241
|
+
// Side culling (inline; per-mesh visibility is at the BLAS-pointer level).
|
|
242
|
+
// 0=front (reject back-facing), 1=back (reject front-facing), 2=double (pass).
|
|
243
|
+
const rayDotNormal = rayDirection.dot( normal );
|
|
244
|
+
const sidePass = side.equal( int( 2 ) )
|
|
245
|
+
.or( side.equal( int( 0 ) ).and( rayDotNormal.lessThan( - 0.0001 ) ) )
|
|
246
|
+
.or( side.equal( int( 1 ) ).and( rayDotNormal.greaterThan( 0.0001 ) ) );
|
|
247
|
+
If( sidePass, () => {
|
|
252
248
|
|
|
253
249
|
closestHit.didHit.assign( true );
|
|
254
250
|
closestHit.dst.assign( t );
|
|
255
251
|
closestHit.normal.assign( normal );
|
|
256
|
-
closestHit.materialIndex.assign( matIdx );
|
|
257
|
-
closestHit.meshIndex.assign( int( uvData2.w ) );
|
|
258
252
|
|
|
259
|
-
// Defer hitPoint
|
|
253
|
+
// Defer materialIndex/meshIndex/hitPoint/UV to post-traversal
|
|
254
|
+
// (all re-derived from closestTriIdx with a single uvData2 fetch below).
|
|
260
255
|
closestTriIdx.assign( triIndex );
|
|
261
256
|
closestU.assign( u );
|
|
262
257
|
closestV.assign( v );
|
|
@@ -276,35 +271,17 @@ export const traverseBVH = Fn( ( [
|
|
|
276
271
|
|
|
277
272
|
} ).Else( () => {
|
|
278
273
|
|
|
279
|
-
// BLAS-pointer leaf (marker -2) — push BLAS root
|
|
280
|
-
// nodeData0: [blasRootNodeIndex, meshIndex,
|
|
274
|
+
// BLAS-pointer leaf (marker -2) — push BLAS root onto stack if mesh is visible
|
|
275
|
+
// nodeData0: [blasRootNodeIndex, meshIndex, visibility, -2]
|
|
276
|
+
// Visibility is free-fetched with the leaf — no extra storage read.
|
|
281
277
|
const blasRoot = int( nodeData0.x ).toVar();
|
|
282
278
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// Per-mesh visibility check — skip entire BLAS if mesh is hidden
|
|
286
|
-
// getDatafromStorageBuffer( buffer, stride=1, sampleIndex=meshIdx, dataOffset=0 )
|
|
287
|
-
const meshIdx = int( nodeData0.y ).toVar();
|
|
288
|
-
const meshVis = getDatafromStorageBuffer( _meshVisibilityBuffer, int( 1 ), meshIdx, int( 0 ) ).x;
|
|
289
|
-
|
|
290
|
-
If( meshVis.greaterThan( 0.5 ).and( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ) ), () => {
|
|
291
|
-
|
|
292
|
-
stack.element( stackPtr ).assign( blasRoot );
|
|
293
|
-
stackPtr.addAssign( 1 );
|
|
279
|
+
If( nodeData0.z.greaterThan( 0.5 ).and( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ) ), () => {
|
|
294
280
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
} else {
|
|
298
|
-
|
|
299
|
-
// No visibility buffer — push unconditionally (original behavior)
|
|
300
|
-
If( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ), () => {
|
|
301
|
-
|
|
302
|
-
stack.element( stackPtr ).assign( blasRoot );
|
|
303
|
-
stackPtr.addAssign( 1 );
|
|
304
|
-
|
|
305
|
-
} );
|
|
281
|
+
stack.element( stackPtr ).assign( blasRoot );
|
|
282
|
+
stackPtr.addAssign( 1 );
|
|
306
283
|
|
|
307
|
-
}
|
|
284
|
+
} );
|
|
308
285
|
|
|
309
286
|
} );
|
|
310
287
|
|
|
@@ -353,7 +330,7 @@ export const traverseBVH = Fn( ( [
|
|
|
353
330
|
|
|
354
331
|
} );
|
|
355
332
|
|
|
356
|
-
// Deferred: compute hitPoint and
|
|
333
|
+
// Deferred: compute hitPoint, UVs, and fetch matIdx/meshIndex once for the final closest hit
|
|
357
334
|
If( closestHit.didHit, () => {
|
|
358
335
|
|
|
359
336
|
closestHit.hitPoint.assign( ray.origin.add( ray.direction.mul( closestHit.dst ) ) );
|
|
@@ -364,6 +341,8 @@ export const traverseBVH = Fn( ( [
|
|
|
364
341
|
closestHit.uv.assign(
|
|
365
342
|
uvData1.xy.mul( w ).add( uvData1.zw.mul( closestU ) ).add( uvData2.xy.mul( closestV ) )
|
|
366
343
|
);
|
|
344
|
+
closestHit.materialIndex.assign( int( uvData2.z ) );
|
|
345
|
+
closestHit.meshIndex.assign( int( uvData2.w ) );
|
|
367
346
|
closestHit.triangleIndex.assign( closestTriIdx );
|
|
368
347
|
|
|
369
348
|
} );
|
|
@@ -466,35 +445,16 @@ export const traverseBVHShadow = Fn( ( [
|
|
|
466
445
|
|
|
467
446
|
} ).Else( () => {
|
|
468
447
|
|
|
469
|
-
// BLAS-pointer leaf (marker -2) — push BLAS root
|
|
470
|
-
// nodeData0: [blasRootNodeIndex, meshIndex,
|
|
448
|
+
// BLAS-pointer leaf (marker -2) — push BLAS root onto stack if mesh is visible
|
|
449
|
+
// nodeData0: [blasRootNodeIndex, meshIndex, visibility, -2]
|
|
471
450
|
const blasRoot = int( nodeData0.x ).toVar();
|
|
472
451
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
// Per-mesh visibility check — skip entire BLAS if mesh is hidden
|
|
476
|
-
// getDatafromStorageBuffer( buffer, stride=1, sampleIndex=meshIdx, dataOffset=0 )
|
|
477
|
-
const meshIdx = int( nodeData0.y ).toVar();
|
|
478
|
-
const meshVis = getDatafromStorageBuffer( _meshVisibilityBuffer, int( 1 ), meshIdx, int( 0 ) ).x;
|
|
479
|
-
|
|
480
|
-
If( meshVis.greaterThan( 0.5 ).and( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ) ), () => {
|
|
481
|
-
|
|
482
|
-
stack.element( stackPtr ).assign( blasRoot );
|
|
483
|
-
stackPtr.addAssign( 1 );
|
|
484
|
-
|
|
485
|
-
} );
|
|
486
|
-
|
|
487
|
-
} else {
|
|
488
|
-
|
|
489
|
-
// No visibility buffer — push unconditionally (original behavior)
|
|
490
|
-
If( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ), () => {
|
|
452
|
+
If( nodeData0.z.greaterThan( 0.5 ).and( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ) ), () => {
|
|
491
453
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
} );
|
|
454
|
+
stack.element( stackPtr ).assign( blasRoot );
|
|
455
|
+
stackPtr.addAssign( 1 );
|
|
496
456
|
|
|
497
|
-
}
|
|
457
|
+
} );
|
|
498
458
|
|
|
499
459
|
} );
|
|
500
460
|
|
package/src/TSL/Clearcoat.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
If,
|
|
14
14
|
} from 'three/tsl';
|
|
15
15
|
|
|
16
|
-
import { struct } from './
|
|
16
|
+
import { struct } from './patches.js';
|
|
17
17
|
|
|
18
18
|
import { Ray, HitInfo, RayTracingMaterial, DotProducts } from './Struct.js';
|
|
19
19
|
import { PI, MIN_CLEARCOAT_ROUGHNESS, computeDotProducts } from './Common.js';
|
package/src/TSL/Displacement.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fn, float, vec2, int, If, Loop, abs, normalize, dot, max } from 'three/tsl';
|
|
2
2
|
|
|
3
|
-
import { struct } from './
|
|
3
|
+
import { struct } from './patches.js';
|
|
4
4
|
import { getDatafromStorageBuffer } from './Common.js';
|
|
5
5
|
import { sampleDisplacementMap } from './TextureSampling.js';
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
atan,
|
|
28
28
|
} from 'three/tsl';
|
|
29
29
|
|
|
30
|
-
import { struct } from './
|
|
30
|
+
import { struct } from './patches.js';
|
|
31
31
|
import { MIN_PDF, getDatafromStorageBuffer, powerHeuristic, MATERIAL_SLOTS, MATERIAL_SLOT } from './Common.js';
|
|
32
32
|
import { RandomValue } from './Random.js';
|
|
33
33
|
import { calculateMaterialPDF } from './LightsSampling.js';
|
|
@@ -361,8 +361,10 @@ export const calculateEmissiveLightPdf = Fn( ( [
|
|
|
361
361
|
// ================================================================================
|
|
362
362
|
|
|
363
363
|
// Binary search in CDF for importance-weighted triangle selection
|
|
364
|
-
// CDF values are stored in the .b channel of the emissive buffer
|
|
365
|
-
|
|
364
|
+
// CDF values are stored in the .b channel of the emissive buffer.
|
|
365
|
+
// `emissiveOffset` is the vec4-element offset into the packed light buffer
|
|
366
|
+
// where emissive entries start (0 if using a non-packed buffer).
|
|
367
|
+
const binarySearchCDF = Fn( ( [ emissiveTriangleBuffer, emissiveOffset, emissiveTriangleCount, rand ] ) => {
|
|
366
368
|
|
|
367
369
|
const lo = int( 0 ).toVar();
|
|
368
370
|
const hi = emissiveTriangleCount.sub( 1 ).toVar();
|
|
@@ -370,7 +372,7 @@ const binarySearchCDF = Fn( ( [ emissiveTriangleBuffer, emissiveTriangleCount, r
|
|
|
370
372
|
Loop( lo.lessThan( hi ), () => {
|
|
371
373
|
|
|
372
374
|
const mid = lo.add( hi ).div( 2 ).toVar();
|
|
373
|
-
const cdfVal = emissiveTriangleBuffer.element( mid.mul( EMISSIVE_STRIDE ) ).b;
|
|
375
|
+
const cdfVal = emissiveTriangleBuffer.element( emissiveOffset.add( mid.mul( int( EMISSIVE_STRIDE ) ) ) ).b;
|
|
374
376
|
|
|
375
377
|
If( cdfVal.lessThan( rand ), () => {
|
|
376
378
|
|
|
@@ -388,11 +390,13 @@ const binarySearchCDF = Fn( ( [ emissiveTriangleBuffer, emissiveTriangleCount, r
|
|
|
388
390
|
|
|
389
391
|
} );
|
|
390
392
|
|
|
391
|
-
// Sample from emissive triangle index using CDF importance sampling
|
|
393
|
+
// Sample from emissive triangle index using CDF importance sampling.
|
|
394
|
+
// `emissiveTriangleBuffer` may be the shared packed light buffer; `emissiveVec4Offset`
|
|
395
|
+
// gives the vec4 offset where emissive entries begin.
|
|
392
396
|
export const sampleEmissiveTriangle = Fn( ( [
|
|
393
397
|
hitPoint, surfaceNormal, totalTriangleCount,
|
|
394
398
|
rngState,
|
|
395
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower,
|
|
399
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
396
400
|
triangleBuffer,
|
|
397
401
|
] ) => {
|
|
398
402
|
|
|
@@ -413,12 +417,12 @@ export const sampleEmissiveTriangle = Fn( ( [
|
|
|
413
417
|
|
|
414
418
|
// CDF importance-weighted triangle selection (brighter triangles sampled more)
|
|
415
419
|
const randEmissive = RandomValue( rngState );
|
|
416
|
-
const emissiveIndex = binarySearchCDF( emissiveTriangleBuffer, emissiveTriangleCount, randEmissive ).toVar();
|
|
420
|
+
const emissiveIndex = binarySearchCDF( emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, randEmissive ).toVar();
|
|
417
421
|
|
|
418
|
-
// Fetch emissive triangle data from
|
|
422
|
+
// Fetch emissive triangle data from packed light buffer (2 vec4s per entry)
|
|
419
423
|
// vec4[0] = (triangleIndex, power, cdf, selectionPdf)
|
|
420
424
|
// vec4[1] = (emission.r, emission.g, emission.b, area)
|
|
421
|
-
const baseIdx = emissiveIndex.mul( EMISSIVE_STRIDE );
|
|
425
|
+
const baseIdx = emissiveVec4Offset.add( emissiveIndex.mul( int( EMISSIVE_STRIDE ) ) );
|
|
422
426
|
const emissiveData0 = emissiveTriangleBuffer.element( baseIdx );
|
|
423
427
|
const emissiveData1 = emissiveTriangleBuffer.element( baseIdx.add( 1 ) );
|
|
424
428
|
const triangleIndex = int( emissiveData0.r );
|
|
@@ -534,7 +538,7 @@ export const calculateEmissiveTriangleContributionDebug = Fn( ( [
|
|
|
534
538
|
hitPoint, normal, viewDir, material,
|
|
535
539
|
totalTriangleCount, bounceIndex, rngState,
|
|
536
540
|
emissiveBoost,
|
|
537
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower,
|
|
541
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
538
542
|
triangleBuffer,
|
|
539
543
|
// Callback functions to avoid circular deps
|
|
540
544
|
traceShadowRayFn,
|
|
@@ -557,7 +561,7 @@ export const calculateEmissiveTriangleContributionDebug = Fn( ( [
|
|
|
557
561
|
// Sample emissive triangle (CDF importance-weighted)
|
|
558
562
|
const emissiveSample = EmissiveSample.wrap( sampleEmissiveTriangle(
|
|
559
563
|
hitPoint, normal, totalTriangleCount, rngState,
|
|
560
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower,
|
|
564
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
561
565
|
triangleBuffer,
|
|
562
566
|
) );
|
|
563
567
|
|
|
@@ -619,7 +623,7 @@ export const calculateEmissiveTriangleContribution = Fn( ( [
|
|
|
619
623
|
hitPoint, normal, viewDir, material,
|
|
620
624
|
totalTriangleCount, bounceIndex, rngState,
|
|
621
625
|
emissiveBoost,
|
|
622
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower,
|
|
626
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
623
627
|
triangleBuffer,
|
|
624
628
|
traceShadowRayFn,
|
|
625
629
|
evaluateMaterialResponseFn,
|
|
@@ -630,7 +634,7 @@ export const calculateEmissiveTriangleContribution = Fn( ( [
|
|
|
630
634
|
hitPoint, normal, viewDir, material,
|
|
631
635
|
totalTriangleCount, bounceIndex, rngState,
|
|
632
636
|
emissiveBoost,
|
|
633
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower,
|
|
637
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
634
638
|
triangleBuffer,
|
|
635
639
|
traceShadowRayFn,
|
|
636
640
|
evaluateMaterialResponseFn,
|
package/src/TSL/Environment.js
CHANGED
|
@@ -82,8 +82,7 @@ export const sampleEquirect = Fn( ( [ environment, direction, environmentMatrix,
|
|
|
82
82
|
// Exact implementation from three-gpu-pathtracer
|
|
83
83
|
export const sampleEquirectProbability = Fn( ( [
|
|
84
84
|
environment,
|
|
85
|
-
|
|
86
|
-
envConditionalWeights,
|
|
85
|
+
envCDFBuffer,
|
|
87
86
|
environmentMatrix,
|
|
88
87
|
environmentIntensity,
|
|
89
88
|
envTotalSum,
|
|
@@ -92,15 +91,19 @@ export const sampleEquirectProbability = Fn( ( [
|
|
|
92
91
|
colorOutput
|
|
93
92
|
] ) => {
|
|
94
93
|
|
|
95
|
-
//
|
|
94
|
+
// Packed CDF layout: [marginal (envResolution.y floats) | conditional (envResolution.x * envResolution.y floats)]
|
|
95
|
+
// The conditional offset equals the marginal length, which is envResolution.y.
|
|
96
|
+
const condOffset = int( envResolution.y ).toVar();
|
|
97
|
+
|
|
98
|
+
// Sample marginal CDF for V coordinate (1D, linear interpolation)
|
|
96
99
|
const marginalSize = envResolution.y;
|
|
97
100
|
const mIdx = clamp( r.x.mul( marginalSize.sub( 1.0 ) ), 0.0, marginalSize.sub( 1.0 ) );
|
|
98
101
|
const mI0 = int( floor( mIdx ) );
|
|
99
102
|
const mI1 = min( mI0.add( 1 ), int( marginalSize ).sub( 1 ) );
|
|
100
103
|
const mFrac = fract( mIdx );
|
|
101
|
-
const v = mix(
|
|
104
|
+
const v = mix( envCDFBuffer.element( mI0 ), envCDFBuffer.element( mI1 ), mFrac ).toVar();
|
|
102
105
|
|
|
103
|
-
// Sample conditional CDF for U coordinate (2D
|
|
106
|
+
// Sample conditional CDF for U coordinate (2D grid, bilinear interpolation)
|
|
104
107
|
const condW = envResolution.x;
|
|
105
108
|
const condH = envResolution.y;
|
|
106
109
|
const cxf = clamp( r.y.mul( condW.sub( 1.0 ) ), 0.0, condW.sub( 1.0 ) );
|
|
@@ -112,10 +115,10 @@ export const sampleEquirectProbability = Fn( ( [
|
|
|
112
115
|
const fx = fract( cxf );
|
|
113
116
|
const fy = fract( cyf );
|
|
114
117
|
const condWi = int( condW );
|
|
115
|
-
const v00 =
|
|
116
|
-
const v10 =
|
|
117
|
-
const v01 =
|
|
118
|
-
const v11 =
|
|
118
|
+
const v00 = envCDFBuffer.element( condOffset.add( cy0.mul( condWi ).add( cx0 ) ) );
|
|
119
|
+
const v10 = envCDFBuffer.element( condOffset.add( cy0.mul( condWi ).add( cx1 ) ) );
|
|
120
|
+
const v01 = envCDFBuffer.element( condOffset.add( cy1.mul( condWi ).add( cx0 ) ) );
|
|
121
|
+
const v11 = envCDFBuffer.element( condOffset.add( cy1.mul( condWi ).add( cx1 ) ) );
|
|
119
122
|
const u = mix( mix( v00, v10, fx ), mix( v01, v11, fx ), fy ).toVar();
|
|
120
123
|
|
|
121
124
|
const uv = vec2( u, v ).toVar();
|
|
@@ -53,6 +53,7 @@ export const sampleLightBVHTriangle = Fn( ( [
|
|
|
53
53
|
rngState,
|
|
54
54
|
lbvhBuffer,
|
|
55
55
|
emissiveTriangleBuffer,
|
|
56
|
+
emissiveVec4Offset,
|
|
56
57
|
triangleBuffer,
|
|
57
58
|
] ) => {
|
|
58
59
|
|
|
@@ -185,7 +186,7 @@ export const sampleLightBVHTriangle = Fn( ( [
|
|
|
185
186
|
Loop( { start: int( 0 ), end: emissiveCount }, ( { i } ) => {
|
|
186
187
|
|
|
187
188
|
const entryIdx = emissiveStart.add( i );
|
|
188
|
-
const baseIdx = entryIdx.mul( int( EMISSIVE_STRIDE ) );
|
|
189
|
+
const baseIdx = emissiveVec4Offset.add( entryIdx.mul( int( EMISSIVE_STRIDE ) ) );
|
|
189
190
|
const emData0 = emissiveTriangleBuffer.element( baseIdx );
|
|
190
191
|
const triPower = max( emData0.g, float( 0.0 ) );
|
|
191
192
|
cumPower.addAssign( triPower );
|
|
@@ -204,7 +205,7 @@ export const sampleLightBVHTriangle = Fn( ( [
|
|
|
204
205
|
selectionPdf.mulAssign( selectedPower.div( leafTotalPower ) );
|
|
205
206
|
|
|
206
207
|
// Now sample the selected triangle (same path as flat CDF sampling)
|
|
207
|
-
const baseIdx = selectedEmissiveIndex.mul( int( EMISSIVE_STRIDE ) );
|
|
208
|
+
const baseIdx = emissiveVec4Offset.add( selectedEmissiveIndex.mul( int( EMISSIVE_STRIDE ) ) );
|
|
208
209
|
const emissiveData0 = emissiveTriangleBuffer.element( baseIdx );
|
|
209
210
|
const emissiveData1 = emissiveTriangleBuffer.element( baseIdx.add( int( 1 ) ) );
|
|
210
211
|
|
package/src/TSL/LightsCore.js
CHANGED
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -41,7 +41,7 @@ import { RandomValue } from './Random.js';
|
|
|
41
41
|
import { getTransformedUV } from './TextureSampling.js';
|
|
42
42
|
|
|
43
43
|
// Module-level state for alpha-cutout shadow testing.
|
|
44
|
-
// Set by ShaderBuilder before graph construction
|
|
44
|
+
// Set by ShaderBuilder before graph construction.
|
|
45
45
|
let _shadowAlbedoMaps = null;
|
|
46
46
|
let _enableAlphaShadows = null;
|
|
47
47
|
|
|
@@ -130,6 +130,19 @@ export const traceShadowRay = Fn( ( [
|
|
|
130
130
|
|
|
131
131
|
} );
|
|
132
132
|
|
|
133
|
+
// Opaque fast-path: check the per-triangle blocker flag (NORMAL_A.w, set at
|
|
134
|
+
// extraction time when alphaMode/transparent/transmission/opacity all indicate
|
|
135
|
+
// a fully opaque surface). Short-circuits the 7-slot getShadowMaterial fetch
|
|
136
|
+
// and the entire alpha/transmission/transparent decision tree below.
|
|
137
|
+
const TRI_STRIDE_SR = int( 8 );
|
|
138
|
+
const blocker = getDatafromStorageBuffer( triangleBuffer, shadowHit.triangleIndex, int( 3 ), TRI_STRIDE_SR ).w;
|
|
139
|
+
If( blocker.greaterThan( 0.5 ), () => {
|
|
140
|
+
|
|
141
|
+
transmittance.assign( 0.0 );
|
|
142
|
+
Break();
|
|
143
|
+
|
|
144
|
+
} );
|
|
145
|
+
|
|
133
146
|
// Fetch material for the hit surface (thin reader: 7 slots instead of 27)
|
|
134
147
|
const shadowMaterial = ShadowMaterial.wrap( getShadowMaterial( shadowHit.materialIndex, materialBuffer ) );
|
|
135
148
|
|
|
@@ -273,7 +273,6 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
273
273
|
samplingInfo,
|
|
274
274
|
// Environment resources
|
|
275
275
|
envTexture, environmentIntensity, envMatrix,
|
|
276
|
-
envMarginalWeights, envConditionalWeights,
|
|
277
276
|
envTotalSum, envResolution,
|
|
278
277
|
enableEnvironmentLight, useEnvMapIS,
|
|
279
278
|
] ) => {
|
|
@@ -934,7 +934,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
934
934
|
materialBuffer,
|
|
935
935
|
// Environment resources
|
|
936
936
|
envTexture, environmentIntensity, envMatrix,
|
|
937
|
-
|
|
937
|
+
envCDFBuffer,
|
|
938
938
|
envTotalSum, envResolution,
|
|
939
939
|
enableEnvironmentLight,
|
|
940
940
|
] ) => {
|
|
@@ -1203,7 +1203,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1203
1203
|
|
|
1204
1204
|
// Sample direction + PDF + color from importance-sampled environment
|
|
1205
1205
|
const envSampleResult = sampleEquirectProbability(
|
|
1206
|
-
envTexture,
|
|
1206
|
+
envTexture, envCDFBuffer,
|
|
1207
1207
|
envMatrix, environmentIntensity, envTotalSum, envResolution, envRandom, envColor
|
|
1208
1208
|
).toVar();
|
|
1209
1209
|
|