rayzee 10.0.0 → 11.0.0

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.
Files changed (173) hide show
  1. package/README.md +252 -44
  2. package/dist/ActiveColor-YvuQq44O.js +13 -0
  3. package/dist/ArchiveImporter-BRQvLggb.js +4300 -0
  4. package/dist/AssetConfig-DSEntE65.js +30 -0
  5. package/dist/BidirectionalIntegrator-9lZQxFVH.js +934 -0
  6. package/dist/BufferLayout-D_IBo2ci.js +150 -0
  7. package/dist/ColorManagement-oeEk7oSu.js +1488 -0
  8. package/dist/EngineEvents-Ci8dVWCP.js +73 -0
  9. package/dist/EngineIssues-CIZnXHsF.js +72 -0
  10. package/dist/FileFormats-sPGJeCRc.js +102 -0
  11. package/dist/Logger-dDDGj9Av.js +97 -0
  12. package/dist/{NeuralRetouch-CYgkQY8Z.js → NeuralRetouch-BCXylThH.js} +8 -6
  13. package/dist/{NeuralSuperRes-PkgDLQcD.js → NeuralSuperRes-sZAeS142.js} +55 -120
  14. package/dist/PathTracer-CQPW06Mm.js +29971 -0
  15. package/dist/PhysicalSky-BvqCw_dy.js +1219 -0
  16. package/dist/RayzeeRenderer-COpwotpU.js +3551 -0
  17. package/dist/SolarLimb-CPaeZVwz.js +721 -0
  18. package/dist/ToneMapGPU-CjU3osDd.js +177 -0
  19. package/dist/ViewTransforms-CYN7bq2w.js +255 -0
  20. package/dist/WorkingMatrix-C6y233hT.js +27 -0
  21. package/dist/ZipReader-gDSWr0I5.js +98 -0
  22. package/dist/addons/archives.es.js +3 -0
  23. package/dist/addons/bidirectional.es.js +2 -0
  24. package/dist/addons/color.es.js +3 -0
  25. package/dist/addons/formats.es.js +2 -0
  26. package/dist/addons/physical-sky.es.js +2 -0
  27. package/dist/addons/storage.es.js +3 -0
  28. package/dist/archiveFormats-BZw7TiDp.js +75 -0
  29. package/dist/identity-DrQsGT2o.js +39 -0
  30. package/dist/openStorage-DwcBRUj7.js +820 -0
  31. package/dist/rayzee-core.es.js +12 -0
  32. package/dist/rayzee.es.js +2551 -44024
  33. package/dist/rayzee.umd.js +528 -487
  34. package/dist/sceneCachePolicy-BSSsKY98.js +443 -0
  35. package/dist/shared-BGBcSxBQ.js +17 -0
  36. package/package.json +23 -2
  37. package/src/Color/ActiveColor.js +34 -0
  38. package/src/Color/BasicColor.js +147 -0
  39. package/src/Color/ColorManagement.js +7 -31
  40. package/src/Color/ViewTransforms.js +5 -1
  41. package/src/EngineDefaults.js +62 -1029
  42. package/src/EngineEvents.js +2 -0
  43. package/src/EngineIssues.js +2 -0
  44. package/src/Headless.js +6 -5
  45. package/src/Passes/AIUpscaler.js +58 -141
  46. package/src/Passes/OIDNDenoiser.js +92 -65
  47. package/src/Passes/OIDNTemporalHistory.js +1 -1
  48. package/src/PathTracerApp.js +620 -4026
  49. package/src/Pipeline/PipelineContext.js +0 -4
  50. package/src/Platform.js +36 -0
  51. package/src/Processor/ArchiveCache.js +1 -1
  52. package/src/Processor/ArchiveImporter.js +1259 -0
  53. package/src/Processor/AssetLoader.js +327 -1763
  54. package/src/Processor/AtmosphereModel.js +1 -2
  55. package/src/Processor/BVHBuilder.js +14 -126
  56. package/src/Processor/BVHLeafFold.js +4 -6
  57. package/src/Processor/BVHRefitter.js +67 -46
  58. package/src/Processor/BufferLayout.js +291 -0
  59. package/src/Processor/EmissiveTriangleBuilder.js +1 -1
  60. package/src/Processor/EnvironmentExactTable.js +187 -0
  61. package/src/Processor/EquirectHDRInfo.js +30 -223
  62. package/src/Processor/FileFormats.js +116 -0
  63. package/src/Processor/GLTFDecoders.js +85 -0
  64. package/src/Processor/GLTFDiffuseTransmission.js +63 -0
  65. package/src/Processor/GeometryExtractor.js +15 -5
  66. package/src/Processor/HostMemory.js +7 -1
  67. package/src/Processor/IESParser.js +47 -35
  68. package/src/Processor/InstanceTable.js +1 -1
  69. package/src/Processor/KernelManager.js +35 -2
  70. package/src/Processor/MaterialPacking.js +10 -8
  71. package/src/Processor/PBRT/PBRTMaterials.js +129 -41
  72. package/src/Processor/PBRT/PBRTParser.js +30 -17
  73. package/src/Processor/PBRT/PBRTSceneBuilder.js +548 -37
  74. package/src/Processor/PBRT/PBRTTextureBake.js +154 -0
  75. package/src/Processor/PBRT/PFM.js +59 -0
  76. package/src/Processor/PackedRayBuffer.js +116 -9
  77. package/src/Processor/ParallelBVHBuilder.js +3 -6
  78. package/src/Processor/PhysicalSky.js +5 -4
  79. package/src/Processor/PlatformImageLoader.js +50 -6
  80. package/src/Processor/QueueManager.js +39 -5
  81. package/src/Processor/ReinsertionOptimizer.js +2 -2
  82. package/src/Processor/SceneProcessor.js +53 -62
  83. package/src/Processor/ShaderBuilder.js +6 -16
  84. package/src/Processor/SimpleSky.js +1 -62
  85. package/src/Processor/SolarLimb.js +3 -0
  86. package/src/Processor/SplitBLAS.js +1 -1
  87. package/src/Processor/StorageTexturePool.js +22 -1
  88. package/src/Processor/TLASBuilder.js +1 -3
  89. package/src/Processor/TextureBuckets.js +237 -0
  90. package/src/Processor/TextureCreator.js +181 -8
  91. package/src/Processor/ToneMapGPU.js +64 -15
  92. package/src/Processor/Workers/BVHRefitWorker.js +5 -2
  93. package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
  94. package/src/Processor/Workers/BVHWorker.js +5 -18
  95. package/src/Processor/Workers/CDFWorker.js +6 -183
  96. package/src/Processor/Workers/TexturesWorker.js +103 -18
  97. package/src/Processor/archiveFormats.js +7 -0
  98. package/src/Processor/utils.js +2 -1
  99. package/src/RayzeeRenderer.js +3899 -0
  100. package/src/RenderSettings.js +68 -62
  101. package/src/Stages/ASVGF.js +13 -14
  102. package/src/Stages/AutoExposure.js +17 -17
  103. package/src/Stages/BilateralFilter.js +2 -1
  104. package/src/Stages/Compositor.js +8 -23
  105. package/src/Stages/DenoiserSettings.js +182 -0
  106. package/src/Stages/EdgeFilter.js +2 -1
  107. package/src/Stages/MotionVector.js +1 -1
  108. package/src/Stages/NRD.js +11 -5
  109. package/src/Stages/NormalDepth.js +12 -8
  110. package/src/Stages/PathTracer.js +338 -43
  111. package/src/Stages/PathTracerStage.js +69 -66
  112. package/src/Stages/Variance.js +1 -1
  113. package/src/Storage/BLASCache.js +1 -1
  114. package/src/Storage/CDFCache.js +18 -17
  115. package/src/Storage/DownloadCache.js +1 -1
  116. package/src/Storage/SceneGraphCodec.js +46 -3
  117. package/src/Storage/SpillStore.js +1 -1
  118. package/src/Storage/StorageManager.js +45 -9
  119. package/src/Storage/areas.js +4 -0
  120. package/src/TSL/BVHTraversal.js +50 -25
  121. package/src/TSL/Bidirectional.js +204 -0
  122. package/src/TSL/BidirectionalLamps.js +132 -0
  123. package/src/TSL/Clearcoat.js +15 -6
  124. package/src/TSL/Common.js +20 -15
  125. package/src/TSL/ConnectKernel.js +147 -0
  126. package/src/TSL/Debugger.js +1 -1
  127. package/src/TSL/Displacement.js +4 -3
  128. package/src/TSL/EmissiveSampling.js +65 -18
  129. package/src/TSL/Environment.js +77 -106
  130. package/src/TSL/EnvironmentCDF.js +85 -39
  131. package/src/TSL/ExtendKernel.js +8 -3
  132. package/src/TSL/FinalWriteKernel.js +4 -4
  133. package/src/TSL/GenerateKernel.js +18 -4
  134. package/src/TSL/HitFacet.js +35 -5
  135. package/src/TSL/LightBVHSampling.js +40 -9
  136. package/src/TSL/LightGenerateKernel.js +293 -0
  137. package/src/TSL/LightGuide.js +198 -0
  138. package/src/TSL/LightSplatKernel.js +165 -0
  139. package/src/TSL/LightVisibility.js +111 -0
  140. package/src/TSL/LightsCore.js +13 -38
  141. package/src/TSL/LightsDirect.js +28 -29
  142. package/src/TSL/LightsIndirect.js +3 -1
  143. package/src/TSL/LightsSampling.js +239 -51
  144. package/src/TSL/MaterialEvaluation.js +113 -31
  145. package/src/TSL/MaterialProperties.js +44 -17
  146. package/src/TSL/MaterialTransmission.js +52 -47
  147. package/src/TSL/MergeKernel.js +218 -0
  148. package/src/TSL/PathTracerCore.js +65 -29
  149. package/src/TSL/Random.js +5 -1
  150. package/src/TSL/SceneResources.js +51 -0
  151. package/src/TSL/ShadeKernel.js +964 -101
  152. package/src/TSL/Struct.js +10 -0
  153. package/src/TSL/Sun.js +1 -1
  154. package/src/TSL/TextureSampling.js +66 -61
  155. package/src/addons/archives.js +14 -0
  156. package/src/addons/bidirectional.js +13 -0
  157. package/src/addons/color.js +32 -0
  158. package/src/addons/formats.js +16 -0
  159. package/src/addons/physicalSky.js +14 -0
  160. package/src/addons/storage.js +19 -0
  161. package/src/core.js +31 -0
  162. package/src/index.js +13 -23
  163. package/src/integrators/BidirectionalIntegrator.js +833 -0
  164. package/src/managers/AnimationManager.js +88 -6
  165. package/src/managers/CameraManager.js +103 -46
  166. package/src/managers/DenoisingManager.js +14 -10
  167. package/src/managers/EnvironmentManager.js +125 -103
  168. package/src/managers/GoboManager.js +9 -0
  169. package/src/managers/MaterialDataManager.js +48 -37
  170. package/src/managers/UniformManager.js +32 -10
  171. package/dist/ToneMapGPU-B6XIe3DU.js +0 -544
  172. package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
  173. package/src/Processor/TreeletOptimizer.js +0 -574
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Where each kind of light gets through, learned from the shadow-ray pick's own rays (LightsSampling lightPick):
3
+ * tries and visible counts per cell of space and facing. Shade reads the learned half, which no kernel writes during
4
+ * a frame, so a render repeats; it adds to the fresh half, folded into the learned one after each frame.
5
+ */
6
+
7
+ import { Fn, float, int, uint, If, Return, instanceIndex, abs, ceil, exp2, floor, length, log2, max, select, sqrt, atomicAdd, atomicLoad, atomicStore } from 'three/tsl';
8
+
9
+ import { VISIBILITY_BASE, VISIBILITY_CELLS, VISIBILITY_KINDS, VISIBILITY_COUNTER_WORDS } from '../Processor/QueueManager.js';
10
+ import { pcgHash } from './Random.js';
11
+
12
+ export const VISIBILITY_KIND = { LAMPS: 0, ENVIRONMENT: 1, SUN: 2, EMITTERS: 3 };
13
+
14
+ const LEARNED = VISIBILITY_BASE;
15
+ const FRESH = VISIBILITY_BASE + VISIBILITY_COUNTER_WORDS;
16
+ const SLOTS = VISIBILITY_CELLS * VISIBILITY_KINDS;
17
+ // A cell is this share of its distance from the camera, rounded up to a power of two.
18
+ const CELL_SHARE = 1 / 32;
19
+ // Below it a kind is still picked now and then, so a light that comes into view is found again.
20
+ const MIN_SHARE = 1 / 64;
21
+ const HALVE_AT = 1 << 16;
22
+
23
+ const hashIn = ( h, v ) => pcgHash( { state: h.bitXor( v ) } );
24
+
25
+ /** The cell of a point and the side its surface faces. */
26
+ export const visibilityCell = ( p, n, cameraPosition ) => {
27
+
28
+ const level = ceil( log2( max( length( p.sub( cameraPosition ) ).mul( CELL_SHARE ), 1e-6 ) ) ).toVar();
29
+ const c = floor( p.div( exp2( level ) ) ).toVar();
30
+ const a = abs( n );
31
+ const facing = select( a.x.greaterThan( max( a.y, a.z ) ), select( n.x.greaterThan( 0.0 ), uint( 0 ), uint( 1 ) ),
32
+ select( a.y.greaterThan( a.z ), select( n.y.greaterThan( 0.0 ), uint( 2 ), uint( 3 ) ), select( n.z.greaterThan( 0.0 ), uint( 4 ), uint( 5 ) ) ) );
33
+
34
+ let h = pcgHash( { state: uint( int( level ).add( 1024 ) ) } );
35
+ h = hashIn( h, uint( int( c.x ) ) );
36
+ h = hashIn( h, uint( int( c.y ) ) );
37
+ h = hashIn( h, uint( int( c.z ) ) );
38
+ h = hashIn( h, facing );
39
+ return h.bitAnd( uint( VISIBILITY_CELLS - 1 ) ).toVar();
40
+
41
+ };
42
+
43
+ const slot = ( cell, kind ) => cell.mul( uint( VISIBILITY_KINDS ) ).add( uint( kind ) ).mul( uint( 2 ) );
44
+
45
+ /** √ of the share of rays that got through: the pick's best weight against a light that is on or off. */
46
+ export const visibilityShare = ( counters, cell, kind ) => {
47
+
48
+ const at = uint( LEARNED ).add( slot( cell, kind ) ).toVar();
49
+ const tries = float( atomicLoad( counters.element( at ) ) );
50
+ const seen = float( atomicLoad( counters.element( at.add( uint( 1 ) ) ) ) );
51
+ return sqrt( max( seen.add( 0.5 ).div( tries.add( 1.0 ) ), MIN_SHARE ) );
52
+
53
+ };
54
+
55
+ export const recordVisibility = ( counters, cell, kind, visible ) => {
56
+
57
+ const at = uint( FRESH ).add( slot( cell, kind ) ).toVar();
58
+ atomicAdd( counters.element( at ), uint( 1 ) );
59
+ If( visible, () => {
60
+
61
+ atomicAdd( counters.element( at.add( uint( 1 ) ) ), uint( 1 ) );
62
+
63
+ } );
64
+
65
+ };
66
+
67
+ /** Adds the frame's counts to the learned ones, halving both past HALVE_AT so the table keeps up with a change. */
68
+ export function buildVisibilityFoldKernel( { counters } ) {
69
+
70
+ return Fn( () => {
71
+
72
+ If( instanceIndex.greaterThanEqual( uint( SLOTS ) ), () => {
73
+
74
+ Return();
75
+
76
+ } );
77
+
78
+ const learned = uint( LEARNED ).add( instanceIndex.mul( uint( 2 ) ) ).toVar();
79
+ const fresh = uint( FRESH ).add( instanceIndex.mul( uint( 2 ) ) ).toVar();
80
+ const tries = atomicLoad( counters.element( learned ) ).add( atomicLoad( counters.element( fresh ) ) ).toVar();
81
+ const seen = atomicLoad( counters.element( learned.add( uint( 1 ) ) ) ).add( atomicLoad( counters.element( fresh.add( uint( 1 ) ) ) ) ).toVar();
82
+ If( tries.greaterThan( uint( HALVE_AT ) ), () => {
83
+
84
+ tries.assign( tries.shiftRight( uint( 1 ) ) );
85
+ seen.assign( seen.shiftRight( uint( 1 ) ) );
86
+
87
+ } );
88
+ atomicStore( counters.element( learned ), tries );
89
+ atomicStore( counters.element( learned.add( uint( 1 ) ) ), seen );
90
+ atomicStore( counters.element( fresh ), uint( 0 ) );
91
+ atomicStore( counters.element( fresh.add( uint( 1 ) ) ), uint( 0 ) );
92
+
93
+ } )().compute( SLOTS, [ 256 ] );
94
+
95
+ }
96
+
97
+ /** Forgets everything learned: a reset may have moved what blocks what. */
98
+ export function buildVisibilityClearKernel( { counters } ) {
99
+
100
+ const words = 2 * VISIBILITY_COUNTER_WORDS;
101
+ return Fn( () => {
102
+
103
+ If( instanceIndex.lessThan( uint( words ) ), () => {
104
+
105
+ atomicStore( counters.element( uint( LEARNED ).add( instanceIndex ) ), uint( 0 ) );
106
+
107
+ } );
108
+
109
+ } )().compute( words, [ 256 ] );
110
+
111
+ }
@@ -1,6 +1,7 @@
1
1
  // Lights Core - Ported from lights_core.fs
2
2
  // Light data structures, access functions, and utility functions
3
3
 
4
+ import { sceneResources } from './SceneResources.js';
4
5
  import {
5
6
  Fn, wgslFn,
6
7
  vec2,
@@ -268,28 +269,14 @@ export const getSpotAttenuation = /*@__PURE__*/ wgslFn( `
268
269
  // SPOT LIGHT GOBO (PROJECTION MASK) SAMPLING
269
270
  // ================================================================================
270
271
 
271
- // Module-level state for spot light gobo masks.
272
- // Set by ShaderBuilder before graph construction.
273
- let _goboMapsTexNode = null;
274
-
275
- /**
276
- * Set the DataArrayTexture node used to sample spot light gobo masks.
277
- * Must be called before the shader graph is constructed.
278
- * @param {TextureNode} node - TSL texture node for the gobo DataArrayTexture
279
- */
280
- export function setGoboMapsTexture( node ) {
281
-
282
- _goboMapsTexNode = node;
283
-
284
- }
285
-
286
272
  // Sample a spot light's gobo mask. Returns 1.0 if no gobo assigned.
287
273
  // Projects the surface direction onto a plane perpendicular to the light's
288
274
  // forward axis at unit distance; cone edge maps to UV ±0.5 around centre.
289
275
  //
290
276
  // `lightDir` = unit direction from surface TO light (matches `LightSample.direction`).
291
- export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
277
+ export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ], builder ) => {
292
278
 
279
+ const { goboMaps } = sceneResources( builder );
293
280
  const mask = float( 1.0 ).toVar();
294
281
 
295
282
  If( light.goboIndex.greaterThanEqual( int( 0 ) ), () => {
@@ -319,13 +306,13 @@ export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
319
306
  const u = clamp( px.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) );
320
307
  const v = clamp( py.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) );
321
308
 
322
- if ( _goboMapsTexNode ) {
309
+ if ( goboMaps ) {
323
310
 
324
311
  // Sample min(.r, .a) so masks encoded in either RGB-luminance
325
312
  // or alpha (Kenney's "Transparent" variants store the shape in alpha
326
313
  // with RGB=white) both produce the expected result.
327
314
  // Sign of goboIntensity encodes inversion: negative = inverted, |value| = strength.
328
- const tex = texture( _goboMapsTexNode, vec2( u, v ) ).depth( light.goboIndex );
315
+ const tex = texture( goboMaps, vec2( u, v ) ).depth( light.goboIndex );
329
316
  const sample = min( tex.r, tex.a );
330
317
  const inverted = light.goboIntensity.lessThan( 0.0 );
331
318
  const effective = select( inverted, float( 1.0 ).sub( sample ), sample );
@@ -353,8 +340,9 @@ export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
353
340
  // can cover any scene size by adjusting the scale.
354
341
  //
355
342
  // `surfacePoint` = world-space position of the surface being shaded.
356
- export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoint ] ) => {
343
+ export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoint ], builder ) => {
357
344
 
345
+ const { goboMaps } = sceneResources( builder );
358
346
  const mask = float( 1.0 ).toVar();
359
347
 
360
348
  If( light.goboIndex.greaterThanEqual( int( 0 ) ), () => {
@@ -379,9 +367,9 @@ export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoi
379
367
  const uTiled = u.sub( u.floor() );
380
368
  const vTiled = v.sub( v.floor() );
381
369
 
382
- if ( _goboMapsTexNode ) {
370
+ if ( goboMaps ) {
383
371
 
384
- const tex = texture( _goboMapsTexNode, vec2( uTiled, vTiled ) ).depth( light.goboIndex );
372
+ const tex = texture( goboMaps, vec2( uTiled, vTiled ) ).depth( light.goboIndex );
385
373
  const sample = min( tex.r, tex.a );
386
374
  const inverted = light.goboIntensity.lessThan( 0.0 );
387
375
  const effective = select( inverted, float( 1.0 ).sub( sample ), sample );
@@ -400,20 +388,6 @@ export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoi
400
388
  // IES PROFILE (PHOTOMETRIC INTENSITY) SAMPLING
401
389
  // ================================================================================
402
390
 
403
- // Module-level texture node for IES profile DataArrayTexture.
404
- // Set by ShaderBuilder before graph construction.
405
- let _iesProfilesTexNode = null;
406
-
407
- /**
408
- * Bind the DataArrayTexture node carrying all loaded IES profiles.
409
- * @param {TextureNode} node
410
- */
411
- export function setIESProfilesTexture( node ) {
412
-
413
- _iesProfilesTexNode = node;
414
-
415
- }
416
-
417
391
  // Sample a spot light's IES profile. Returns a normalized multiplier in [0,1]
418
392
  // (or 1.0 if no profile assigned).
419
393
  //
@@ -421,8 +395,9 @@ export function setIESProfilesTexture( node ) {
421
395
  // where V=0 is along the light's "forward" axis (the spot's direction).
422
396
  //
423
397
  // `lightDir` = unit direction from surface TO light (matches LightSample.direction).
424
- export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
398
+ export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ], builder ) => {
425
399
 
400
+ const { iesProfiles } = sceneResources( builder );
426
401
  const result = float( 1.0 ).toVar();
427
402
 
428
403
  If( light.iesIndex.greaterThanEqual( int( 0 ) ), () => {
@@ -451,9 +426,9 @@ export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
451
426
  const phi = atan( py, px );
452
427
  const u = phi.div( float( 2.0 * Math.PI ) ).add( 0.5 );
453
428
 
454
- if ( _iesProfilesTexNode ) {
429
+ if ( iesProfiles ) {
455
430
 
456
- const sample = texture( _iesProfilesTexNode, vec2( u, v ) ).depth( light.iesIndex ).r;
431
+ const sample = texture( iesProfiles, vec2( u, v ) ).depth( light.iesIndex ).r;
457
432
  // Blend between flat (1.0) and full profile by iesIntensity.
458
433
  const strength = clamp( light.iesIntensity, float( 0.0 ), float( 1.0 ) );
459
434
  result.assign( mix( float( 1.0 ), sample, strength ) );
@@ -2,6 +2,7 @@
2
2
  // Direct lighting calculations including shadow ray tracing
3
3
  // and contribution calculations for all light types.
4
4
 
5
+ import { sceneResources } from './SceneResources.js';
5
6
  import {
6
7
  Fn,
7
8
  float,
@@ -28,43 +29,27 @@ import {
28
29
  import { Ray, ShadowMaterial, HitInfo } from './Struct.js';
29
30
  import {
30
31
  REC709_LUMINANCE_COEFFICIENTS, getShadowMaterial, instanceRows,
31
- instanceFaceNormalToWorld, triangleRow, getAlphaShadowsUniform, shadowFlagsSettle, offsetRayOrigin,
32
+ instanceFaceNormalToWorld, triangleRow, shadowFlagsSettle, offsetRayOrigin,
32
33
  } from './Common.js';
33
34
  import { fresnelDielectric } from './Fresnel.js';
34
35
  import { calculateBeerLawAbsorption } from './MaterialTransmission.js';
35
36
  import { getTransformedUV, sampleBucket } from './TextureSampling.js';
36
37
 
37
- // Module-level state for alpha-cutout shadow testing.
38
- // Set by PathTracer before shade-graph construction.
39
- let _shadowAlbedoMaps = null;
40
-
41
- export { setAlphaShadowsUniform } from './Common.js';
42
-
43
- /**
44
- * Set the sRGB bucket texture node array for alpha-aware shadow rays (albedo alpha).
45
- * Must be called before the shade graph is constructed.
46
- * @param {Array} buckets - K sRGB bucket texture nodes
47
- */
48
- export function setShadowAlbedoMaps( buckets ) {
49
-
50
- _shadowAlbedoMaps = buckets;
51
-
52
- }
53
-
54
38
  // ================================================================================
55
39
  // SHADOW RAY TRACING
56
40
  // ================================================================================
57
41
 
58
42
  // Note: traverseBVH is passed as a parameter to avoid circular dependency
59
- export const traceShadowRay = Fn( ( [
43
+ const makeTraceShadowRay = ( refractiveBlocks ) => Fn( ( [
60
44
  origin, dir, maxDist,
61
45
  // BVH traversal function and textures passed as parameters
62
46
  traverseBVHShadowFn,
63
47
  bvhBuffer,
64
48
  triangleBuffer,
65
49
  materialBuffer,
66
- ] ) => {
50
+ ], builder ) => {
67
51
 
52
+ const { alphaShadows, shadowAlbedoMaps } = sceneResources( builder );
68
53
  const transmittance = float( 1.0 ).toVar();
69
54
  const rayOrigin = origin.toVar();
70
55
  const remainingDist = float( maxDist ).toVar();
@@ -93,7 +78,7 @@ export const traceShadowRay = Fn( ( [
93
78
  // alphaMode/transparent/transmission/opacity all indicate a fully opaque surface.
94
79
  // Short-circuits the 7-slot getShadowMaterial fetch and the whole alpha decision tree.
95
80
  const flags = triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).z;
96
- If( shadowFlagsSettle( flags ), () => {
81
+ If( shadowFlagsSettle( flags, alphaShadows ), () => {
97
82
 
98
83
  transmittance.assign( 0.0 );
99
84
  Break();
@@ -111,14 +96,13 @@ export const traceShadowRay = Fn( ( [
111
96
  // ---------------------------------------------------------------
112
97
  const alphaCutout = tslBool( false ).toVar();
113
98
 
114
- const alphaShadows = getAlphaShadowsUniform();
115
99
  if ( alphaShadows ) If( alphaShadows.equal( int( 1 ) ), () => {
116
100
 
117
101
  // Sample texture alpha once (shared by MASK and BLEND paths).
118
102
  // Deferred UV: barycentrics in shadowHit.uv, triangle index in shadowHit.triangleIndex.
119
103
  const texAlpha = float( 1.0 ).toVar();
120
104
 
121
- if ( _shadowAlbedoMaps ) {
105
+ if ( shadowAlbedoMaps ) {
122
106
 
123
107
  If( shadowMaterial.albedoMapIndex.greaterThanEqual( int( 0 ) ), () => {
124
108
 
@@ -129,7 +113,7 @@ export const traceShadowRay = Fn( ( [
129
113
  const uvData2 = uintBitsToFloat( triangleRow( triangleBuffer, shadowHit.triangleIndex, 4 ).xy );
130
114
  const hitUV = uvData1.xy.mul( baryW ).add( uvData1.zw.mul( baryU ) ).add( uvData2.mul( baryV ) );
131
115
  const albedoUV = getTransformedUV( { uv: hitUV, transform: shadowMaterial.albedoTransform } );
132
- texAlpha.assign( sampleBucket( _shadowAlbedoMaps, shadowMaterial.albedoMapIndex, albedoUV ).a );
116
+ texAlpha.assign( sampleBucket( shadowAlbedoMaps, shadowMaterial.albedoMapIndex, albedoUV ).a );
133
117
 
134
118
  } );
135
119
 
@@ -176,7 +160,12 @@ export const traceShadowRay = Fn( ( [
176
160
  rayOrigin.assign( shadowHit.hitPoint.add( dir.mul( alphaEps ) ) );
177
161
  remainingDist.subAssign( shadowHit.dst.add( alphaEps ) );
178
162
 
179
- } ).ElseIf( shadowMaterial.transmission.greaterThan( 0.0 ), () => {
163
+ } ).ElseIf( shadowMaterial.transmission.greaterThan( 0.0 ), refractiveBlocks ? () => {
164
+
165
+ transmittance.assign( 0.0 );
166
+ Break();
167
+
168
+ } : () => {
180
169
 
181
170
  // Deferred geometric-normal compute — refetch triangle positions and
182
171
  // derive the normal here so opaque/alpha-cutout shadow hits don't pay
@@ -262,6 +251,11 @@ export const traceShadowRay = Fn( ( [
262
251
 
263
252
  } );
264
253
 
254
+ export const traceShadowRay = /*@__PURE__*/ makeTraceShadowRay( false );
255
+
256
+ // For the bidirectional strategies: their light paths carry what refracts, so glass must block.
257
+ export const traceShadowRayRefractiveOpaque = /*@__PURE__*/ makeTraceShadowRay( true );
258
+
265
259
  // ================================================================================
266
260
  // RAY OFFSET CALCULATION
267
261
  // ================================================================================
@@ -273,9 +267,14 @@ export const calculateRayOffset = Fn( ( [ hitPoint, normal ] ) => offsetRayOrigi
273
267
  // LIGHT IMPORTANCE ESTIMATION
274
268
  // ================================================================================
275
269
 
270
+ // A light's cosine for importance: either side for a surface that passes light through diffusely.
271
+ const importanceCosine = ( normal, direction, material ) => select(
272
+ material.diffuseTransmission.greaterThan( 0.0 ), abs( dot( normal, direction ) ), max( float( 0.0 ), dot( normal, direction ) ),
273
+ );
274
+
276
275
  export const calculateDirectionalLightImportance = Fn( ( [ light, normal, material, bounceIndex ] ) => {
277
276
 
278
- const NoL = max( float( 0.0 ), dot( normal, light.direction ) );
277
+ const NoL = importanceCosine( normal, light.direction, material );
279
278
  const result = float( 0.0 ).toVar();
280
279
 
281
280
  If( NoL.greaterThan( 0.0 ), () => {
@@ -312,7 +311,7 @@ export const estimateLightImportance = Fn( ( [ light, hitPoint, normal, material
312
311
  const distSq = dist.mul( dist );
313
312
 
314
313
  const lightDir = toLight.div( dist );
315
- const NoL = max( dot( normal, lightDir ), 0.0 );
314
+ const NoL = importanceCosine( normal, lightDir, material );
316
315
  const result = float( 0.0 ).toVar();
317
316
 
318
317
  If( NoL.greaterThan( 0.0 ), () => {
@@ -366,7 +365,7 @@ export const calculatePointLightImportance = Fn( ( [ light, hitPoint, normal, ma
366
365
 
367
366
  const dist = sqrt( distSq );
368
367
  const lightDir = toLight.div( dist );
369
- const NoL = max( float( 0.0 ), dot( normal, lightDir ) );
368
+ const NoL = importanceCosine( normal, lightDir, material );
370
369
 
371
370
  If( NoL.greaterThan( 0.0 ), () => {
372
371
 
@@ -417,7 +416,7 @@ export const calculateSpotLightImportance = Fn( ( [ light, hitPoint, normal, mat
417
416
  If( distSq.greaterThanEqual( 0.001 ), () => {
418
417
 
419
418
  const lightDir = toLight.div( sqrt( distSq ) );
420
- const NoL = max( float( 0.0 ), dot( normal, lightDir ) );
419
+ const NoL = importanceCosine( normal, lightDir, material );
421
420
 
422
421
  If( NoL.greaterThan( 0.0 ), () => {
423
422
 
@@ -32,6 +32,8 @@ export const calculateIndirectLighting = Fn( ( [
32
32
  brdfSampleDirection, brdfSamplePdf, brdfSampleValue,
33
33
  // DirectionSample carries these so refraction throughput does not need a second sampler run.
34
34
  isTransmission, brdfSampleColorWeight,
35
+ // Drawn through the surface by the diffuse transmission lobe: its cosine is taken below N.
36
+ isDiffuseTransmission,
35
37
  rngState,
36
38
  pixelCoord, resolution, frame, dimBase,
37
39
  ] ) => {
@@ -61,7 +63,7 @@ export const calculateIndirectLighting = Fn( ( [
61
63
  //
62
64
  // brdfSamplePdf is now the true mixture density (calculateBSDFSamplingPDF), so the
63
65
  // estimator is just f·NoL/pdf and the MIS pairs share one function by construction.
64
- const NoL = max( dot( N, brdfSampleDirection ), 0.0 ).toVar();
66
+ const NoL = select( isDiffuseTransmission, dot( N, brdfSampleDirection ).negate(), max( dot( N, brdfSampleDirection ), 0.0 ) ).toVar();
65
67
  const pdf = max( brdfSamplePdf, MIN_PDF ).toVar();
66
68
 
67
69
  r_direction.assign( brdfSampleDirection );