rayzee 5.4.0 → 5.4.1
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 +2723 -2726
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +53 -53
- 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/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 +87 -43
- package/src/TSL/BVHTraversal.js +15 -63
- 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 +1 -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/patches.js +145 -0
- package/src/index.js +1 -1
- package/src/managers/EnvironmentManager.js +32 -56
- package/src/managers/UniformManager.js +3 -0
- package/src/TSL/structProxy.js +0 -87
- package/src/TSL/wgslGlobalVarsPatch.js +0 -60
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)
|
|
@@ -276,35 +265,17 @@ export const traverseBVH = Fn( ( [
|
|
|
276
265
|
|
|
277
266
|
} ).Else( () => {
|
|
278
267
|
|
|
279
|
-
// BLAS-pointer leaf (marker -2) — push BLAS root
|
|
280
|
-
// nodeData0: [blasRootNodeIndex, meshIndex,
|
|
268
|
+
// BLAS-pointer leaf (marker -2) — push BLAS root onto stack if mesh is visible
|
|
269
|
+
// nodeData0: [blasRootNodeIndex, meshIndex, visibility, -2]
|
|
270
|
+
// Visibility is free-fetched with the leaf — no extra storage read.
|
|
281
271
|
const blasRoot = int( nodeData0.x ).toVar();
|
|
282
272
|
|
|
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 );
|
|
294
|
-
|
|
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 );
|
|
273
|
+
If( nodeData0.z.greaterThan( 0.5 ).and( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ) ), () => {
|
|
304
274
|
|
|
305
|
-
|
|
275
|
+
stack.element( stackPtr ).assign( blasRoot );
|
|
276
|
+
stackPtr.addAssign( 1 );
|
|
306
277
|
|
|
307
|
-
}
|
|
278
|
+
} );
|
|
308
279
|
|
|
309
280
|
} );
|
|
310
281
|
|
|
@@ -466,35 +437,16 @@ export const traverseBVHShadow = Fn( ( [
|
|
|
466
437
|
|
|
467
438
|
} ).Else( () => {
|
|
468
439
|
|
|
469
|
-
// BLAS-pointer leaf (marker -2) — push BLAS root
|
|
470
|
-
// nodeData0: [blasRootNodeIndex, meshIndex,
|
|
440
|
+
// BLAS-pointer leaf (marker -2) — push BLAS root onto stack if mesh is visible
|
|
441
|
+
// nodeData0: [blasRootNodeIndex, meshIndex, visibility, -2]
|
|
471
442
|
const blasRoot = int( nodeData0.x ).toVar();
|
|
472
443
|
|
|
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 {
|
|
444
|
+
If( nodeData0.z.greaterThan( 0.5 ).and( stackPtr.lessThan( int( MAX_STACK_DEPTH ) ) ), () => {
|
|
488
445
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
stack.element( stackPtr ).assign( blasRoot );
|
|
493
|
-
stackPtr.addAssign( 1 );
|
|
494
|
-
|
|
495
|
-
} );
|
|
446
|
+
stack.element( stackPtr ).assign( blasRoot );
|
|
447
|
+
stackPtr.addAssign( 1 );
|
|
496
448
|
|
|
497
|
-
}
|
|
449
|
+
} );
|
|
498
450
|
|
|
499
451
|
} );
|
|
500
452
|
|
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
|
|
|
@@ -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
|
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
fract,
|
|
31
31
|
} from 'three/tsl';
|
|
32
32
|
|
|
33
|
-
import { struct } from './
|
|
33
|
+
import { struct } from './patches.js';
|
|
34
34
|
import { Ray, RayTracingMaterial, RenderState, HitInfo, DotProducts, DirectionSample } from './Struct.js';
|
|
35
35
|
import { PI, EPSILON, MIN_ROUGHNESS, MIN_CLEARCOAT_ROUGHNESS, computeDotProducts } from './Common.js';
|
|
36
36
|
import { iorToFresnel0, fresnelSchlickFloat } from './Fresnel.js';
|
package/src/TSL/PathTracer.js
CHANGED
|
@@ -137,14 +137,14 @@ export const pathTracerMain = ( params ) => {
|
|
|
137
137
|
pointLightsBuffer, numPointLights,
|
|
138
138
|
spotLightsBuffer, numSpotLights,
|
|
139
139
|
envTexture, environmentIntensity, envMatrix,
|
|
140
|
-
|
|
140
|
+
envCDFBuffer,
|
|
141
141
|
envTotalSum, envResolution,
|
|
142
142
|
enableEnvironmentLight, useEnvMapIS,
|
|
143
143
|
maxBounceCount, transmissiveBounces,
|
|
144
144
|
showBackground, transparentBackground, backgroundIntensity,
|
|
145
145
|
fireflyThreshold, globalIlluminationIntensity,
|
|
146
146
|
totalTriangleCount, enableEmissiveTriangleSampling,
|
|
147
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
147
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
148
148
|
lightBVHBuffer, lightBVHNodeCount,
|
|
149
149
|
debugVisScale,
|
|
150
150
|
enableAccumulation, hasPreviousAccumulated,
|
|
@@ -284,14 +284,14 @@ export const pathTracerMain = ( params ) => {
|
|
|
284
284
|
pointLightsBuffer, numPointLights,
|
|
285
285
|
spotLightsBuffer, numSpotLights,
|
|
286
286
|
envTexture, environmentIntensity, envMatrix,
|
|
287
|
-
|
|
287
|
+
envCDFBuffer,
|
|
288
288
|
envTotalSum, envResolution,
|
|
289
289
|
enableEnvironmentLight, useEnvMapIS,
|
|
290
290
|
maxBounceCount, transmissiveBounces,
|
|
291
291
|
backgroundIntensity, showBackground, transparentBackground,
|
|
292
292
|
fireflyThreshold, globalIlluminationIntensity,
|
|
293
293
|
totalTriangleCount, enableEmissiveTriangleSampling,
|
|
294
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
294
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
295
295
|
lightBVHBuffer, lightBVHNodeCount,
|
|
296
296
|
pixelCoord, resolution, frame,
|
|
297
297
|
) );
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
sampler,
|
|
42
42
|
} from 'three/tsl';
|
|
43
43
|
|
|
44
|
-
import { struct } from './
|
|
44
|
+
import { struct } from './patches.js';
|
|
45
45
|
|
|
46
46
|
import {
|
|
47
47
|
PI_INV,
|
|
@@ -590,7 +590,7 @@ export const Trace = Fn( ( [
|
|
|
590
590
|
spotLightsBuffer, numSpotLights,
|
|
591
591
|
// Environment
|
|
592
592
|
envTexture, environmentIntensity, envMatrix,
|
|
593
|
-
|
|
593
|
+
envCDFBuffer,
|
|
594
594
|
envTotalSum, envResolution,
|
|
595
595
|
enableEnvironmentLight, useEnvMapIS,
|
|
596
596
|
// Rendering parameters
|
|
@@ -598,7 +598,7 @@ export const Trace = Fn( ( [
|
|
|
598
598
|
backgroundIntensity, showBackground, transparentBackground,
|
|
599
599
|
fireflyThreshold, globalIlluminationIntensity,
|
|
600
600
|
totalTriangleCount, enableEmissiveTriangleSampling,
|
|
601
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
601
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
602
602
|
lightBVHBuffer, lightBVHNodeCount,
|
|
603
603
|
// Per-pixel info
|
|
604
604
|
pixelCoord, resolution, frame,
|
|
@@ -1013,7 +1013,7 @@ export const Trace = Fn( ( [
|
|
|
1013
1013
|
triangleBuffer,
|
|
1014
1014
|
materialBuffer,
|
|
1015
1015
|
envTexture, environmentIntensity, envMatrix,
|
|
1016
|
-
|
|
1016
|
+
envCDFBuffer,
|
|
1017
1017
|
envTotalSum, envResolution,
|
|
1018
1018
|
enableEnvironmentLight,
|
|
1019
1019
|
);
|
|
@@ -1040,6 +1040,7 @@ export const Trace = Fn( ( [
|
|
|
1040
1040
|
rngState,
|
|
1041
1041
|
lightBVHBuffer,
|
|
1042
1042
|
emissiveTriangleBuffer,
|
|
1043
|
+
emissiveVec4Offset,
|
|
1043
1044
|
triangleBuffer,
|
|
1044
1045
|
) );
|
|
1045
1046
|
|
|
@@ -1091,7 +1092,7 @@ export const Trace = Fn( ( [
|
|
|
1091
1092
|
hitInfo.hitPoint, N, V, material,
|
|
1092
1093
|
totalTriangleCount, bounceIndex, rngState,
|
|
1093
1094
|
emissiveBoost,
|
|
1094
|
-
emissiveTriangleBuffer, emissiveTriangleCount, emissiveTotalPower,
|
|
1095
|
+
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
|
|
1095
1096
|
triangleBuffer,
|
|
1096
1097
|
traceShadowRayWrapped,
|
|
1097
1098
|
evaluateMaterialResponse,
|
|
@@ -1132,7 +1133,6 @@ export const Trace = Fn( ( [
|
|
|
1132
1133
|
rngState,
|
|
1133
1134
|
samplingInfo,
|
|
1134
1135
|
envTexture, environmentIntensity, envMatrix,
|
|
1135
|
-
envMarginalWeights, envConditionalWeights,
|
|
1136
1136
|
envTotalSum, envResolution,
|
|
1137
1137
|
enableEnvironmentLight, useEnvMapIS,
|
|
1138
1138
|
) );
|
package/src/TSL/Struct.js
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rayzee patches for Three.js / TSL.
|
|
3
|
+
*
|
|
4
|
+
* Side-effect on import: installs `WebGPUBackend.createNodeBuilder` override
|
|
5
|
+
* (restores r183 function-scoped `var` emission for compute shaders — prevents
|
|
6
|
+
* a register-allocation regression in the path tracer's hot loop).
|
|
7
|
+
*
|
|
8
|
+
* Export: `struct()` — drop-in replacement for TSL's `struct()` returning
|
|
9
|
+
* a proxy factory that supports GLSL-style dot-notation field access.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { WebGPUBackend } from 'three/webgpu';
|
|
13
|
+
import { struct as _struct } from 'three/tsl';
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// 1. WGSL global-variable promotion patch (compute-only)
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Three.js r184 introduced `WGSLNodeBuilder.allowGlobalVariables = true`, which
|
|
19
|
+
// emits `.toVar()` declarations at WGSL module scope as `var<private> name : T`
|
|
20
|
+
// instead of function-local `var name : T` inside `fn main()` (as r183 did).
|
|
21
|
+
//
|
|
22
|
+
// For compute shaders with hundreds of `.toVar()` calls in loops (e.g. the BVH
|
|
23
|
+
// traversal + BRDF path tracer), `var<private>` increases GPU register pressure
|
|
24
|
+
// because the Dawn/Chromium WGSL compiler cannot aggressively register-allocate
|
|
25
|
+
// variables with a stable per-invocation memory address. We measured a ~8% fps
|
|
26
|
+
// regression (120 → 110) on the path tracer after upgrading r183 → r184 that
|
|
27
|
+
// traced entirely to GPU execution, not CPU.
|
|
28
|
+
//
|
|
29
|
+
// `allowGlobalVariables` is ONLY consumed by the compute template
|
|
30
|
+
// (`_getWGSLComputeCode`). The vertex/fragment templates always emit
|
|
31
|
+
// `shaderData.vars` at module scope and REQUIRE `allowGlobalVariables=true`
|
|
32
|
+
// (emitting function-local `var` at module scope is invalid WGSL and crashes
|
|
33
|
+
// pipeline creation with "Invalid ShaderModule"). We install a per-instance
|
|
34
|
+
// accessor that returns `false` only when the builder is for a compute node
|
|
35
|
+
// (material === null) and `true` otherwise, so render pipelines keep r184
|
|
36
|
+
// behavior untouched.
|
|
37
|
+
//
|
|
38
|
+
// Relevant upstream lines:
|
|
39
|
+
// - `node_modules/three/src/renderers/webgpu/nodes/WGSLNodeBuilder.js:247`
|
|
40
|
+
// (`this.allowGlobalVariables = true`)
|
|
41
|
+
// - `...WGSLNodeBuilder.js:2458` (module-scope vars block)
|
|
42
|
+
// - `...WGSLNodeBuilder.js:2467` (function-body vars block)
|
|
43
|
+
//
|
|
44
|
+
// Revisit if upstream adds an official opt-out or fixes register pressure.
|
|
45
|
+
|
|
46
|
+
const _origCreateNodeBuilder = WebGPUBackend.prototype.createNodeBuilder;
|
|
47
|
+
|
|
48
|
+
WebGPUBackend.prototype.createNodeBuilder = function ( object, renderer ) {
|
|
49
|
+
|
|
50
|
+
const builder = _origCreateNodeBuilder.call( this, object, renderer );
|
|
51
|
+
|
|
52
|
+
Object.defineProperty( builder, 'allowGlobalVariables', {
|
|
53
|
+
get() {
|
|
54
|
+
|
|
55
|
+
return this.material !== null;
|
|
56
|
+
|
|
57
|
+
},
|
|
58
|
+
set() { /* ignore — the value is derived from material presence */ },
|
|
59
|
+
configurable: true,
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
return builder;
|
|
63
|
+
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// 2. TSL struct proxy — enables GLSL-style dot-notation field access
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// TSL structs require `.get('fieldName')` for member access, but GLSL-style
|
|
70
|
+
// dot notation (`.fieldName`) is more natural and matches ported code.
|
|
71
|
+
//
|
|
72
|
+
// This wraps TSL's `struct()` so that:
|
|
73
|
+
// - Direct construction: `MyStruct({...}).toVar('x')` → `.fieldName` works
|
|
74
|
+
// - Fn return values: `MyStruct.wrap(someFn(...))` → `.fieldName` works
|
|
75
|
+
//
|
|
76
|
+
// Property access for known struct member names is redirected to `.get('name')`.
|
|
77
|
+
// Swizzle properties (x, y, z, w, etc.), Node methods (.add, .assign, etc.), and
|
|
78
|
+
// other standard properties pass through to the underlying node unmodified.
|
|
79
|
+
|
|
80
|
+
function createStructProxy( node, memberSet ) {
|
|
81
|
+
|
|
82
|
+
return new Proxy( node, {
|
|
83
|
+
|
|
84
|
+
get( target, prop, receiver ) {
|
|
85
|
+
|
|
86
|
+
// Intercept known struct member names
|
|
87
|
+
if ( typeof prop === 'string' && memberSet.has( prop ) ) {
|
|
88
|
+
|
|
89
|
+
return target.get( prop );
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const val = Reflect.get( target, prop, receiver );
|
|
94
|
+
|
|
95
|
+
// Intercept .toVar() to proxy-wrap the result
|
|
96
|
+
if ( prop === 'toVar' && typeof val === 'function' ) {
|
|
97
|
+
|
|
98
|
+
return ( ...args ) => createStructProxy( val.apply( target, args ), memberSet );
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return val;
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} );
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Drop-in replacement for TSL's `struct()` that returns a proxy-enhanced factory.
|
|
112
|
+
*
|
|
113
|
+
* The returned factory:
|
|
114
|
+
* - Creates struct nodes where `.toVar()` results support dot-notation field access
|
|
115
|
+
* - Has `.wrap(node)` method to proxy-wrap Fn return values for field access
|
|
116
|
+
* - Has `.layout` and `.isStruct` matching the original TSL struct API
|
|
117
|
+
*
|
|
118
|
+
* @param {Object} members - Struct member layout (e.g., { didHit: 'bool', dst: 'float' })
|
|
119
|
+
* @param {string|null} name - Optional struct name
|
|
120
|
+
* @returns {Function} Enhanced struct factory
|
|
121
|
+
*/
|
|
122
|
+
export function struct( members, name = null ) {
|
|
123
|
+
|
|
124
|
+
const factory = _struct( members, name );
|
|
125
|
+
const memberSet = new Set( Object.keys( members ) );
|
|
126
|
+
|
|
127
|
+
const wrappedFactory = ( ...args ) => {
|
|
128
|
+
|
|
129
|
+
const node = factory( ...args );
|
|
130
|
+
return createStructProxy( node, memberSet );
|
|
131
|
+
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
wrappedFactory.layout = factory.layout;
|
|
135
|
+
wrappedFactory.isStruct = true;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Wrap an existing node (e.g., Fn return value) with struct field access proxy.
|
|
139
|
+
* Usage: `const hit = HitInfo.wrap(traverseBVH(...).toVar('hit'));`
|
|
140
|
+
*/
|
|
141
|
+
wrappedFactory.wrap = ( node ) => createStructProxy( node, memberSet );
|
|
142
|
+
|
|
143
|
+
return wrappedFactory;
|
|
144
|
+
|
|
145
|
+
}
|