rayzee 10.0.0 → 11.1.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 (176) hide show
  1. package/README.md +252 -44
  2. package/dist/ActiveColor-YvuQq44O.js +13 -0
  3. package/dist/ArchiveImporter-Hn35o6Z5.js +5145 -0
  4. package/dist/AssetConfig-DSEntE65.js +30 -0
  5. package/dist/BidirectionalIntegrator-CBw8hkeu.js +946 -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-BzNpiVHZ.js} +8 -6
  13. package/dist/{NeuralSuperRes-PkgDLQcD.js → NeuralSuperRes-Bk7bHg2B.js} +55 -120
  14. package/dist/PathTracer-72XufKAr.js +29976 -0
  15. package/dist/PhysicalSky-BgjdV88k.js +1219 -0
  16. package/dist/RayzeeRenderer-1QRO97eq.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-tnAoUL1j.js +94 -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-C67jbci-.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 +20 -7
  52. package/src/Processor/ArchiveImporter.js +1265 -0
  53. package/src/Processor/ArchiveReader.js +22 -4
  54. package/src/Processor/AssetLoader.js +327 -1763
  55. package/src/Processor/AtmosphereModel.js +1 -2
  56. package/src/Processor/BVHBuilder.js +14 -126
  57. package/src/Processor/BVHLeafFold.js +4 -6
  58. package/src/Processor/BVHRefitter.js +67 -46
  59. package/src/Processor/BufferLayout.js +291 -0
  60. package/src/Processor/EmissiveTriangleBuilder.js +1 -1
  61. package/src/Processor/EnvironmentExactTable.js +187 -0
  62. package/src/Processor/EquirectHDRInfo.js +30 -223
  63. package/src/Processor/FileFormats.js +116 -0
  64. package/src/Processor/GLTFDecoders.js +85 -0
  65. package/src/Processor/GLTFDiffuseTransmission.js +63 -0
  66. package/src/Processor/GeometryExtractor.js +15 -5
  67. package/src/Processor/HostMemory.js +7 -1
  68. package/src/Processor/IESParser.js +47 -35
  69. package/src/Processor/InstanceTable.js +1 -1
  70. package/src/Processor/KernelManager.js +35 -2
  71. package/src/Processor/MaterialPacking.js +10 -8
  72. package/src/Processor/PBRT/PBRTMaterials.js +254 -42
  73. package/src/Processor/PBRT/PBRTParser.js +49 -19
  74. package/src/Processor/PBRT/PBRTScattering.js +93 -0
  75. package/src/Processor/PBRT/PBRTSceneBuilder.js +1050 -75
  76. package/src/Processor/PBRT/PBRTTextureBake.js +179 -0
  77. package/src/Processor/PBRT/PFM.js +59 -0
  78. package/src/Processor/PackedRayBuffer.js +116 -9
  79. package/src/Processor/ParallelBVHBuilder.js +3 -6
  80. package/src/Processor/PhysicalSky.js +5 -4
  81. package/src/Processor/PlatformImageLoader.js +50 -6
  82. package/src/Processor/QueueManager.js +39 -5
  83. package/src/Processor/ReinsertionOptimizer.js +2 -2
  84. package/src/Processor/SceneMetadata.js +30 -5
  85. package/src/Processor/SceneProcessor.js +53 -62
  86. package/src/Processor/ShaderBuilder.js +6 -16
  87. package/src/Processor/SimpleSky.js +1 -62
  88. package/src/Processor/SolarLimb.js +3 -0
  89. package/src/Processor/SplitBLAS.js +1 -1
  90. package/src/Processor/StorageTexturePool.js +22 -1
  91. package/src/Processor/TLASBuilder.js +1 -3
  92. package/src/Processor/TextureBuckets.js +237 -0
  93. package/src/Processor/TextureCreator.js +181 -8
  94. package/src/Processor/ToneMapGPU.js +64 -15
  95. package/src/Processor/Workers/BVHRefitWorker.js +5 -2
  96. package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
  97. package/src/Processor/Workers/BVHWorker.js +5 -18
  98. package/src/Processor/Workers/CDFWorker.js +6 -183
  99. package/src/Processor/Workers/TexturesWorker.js +103 -18
  100. package/src/Processor/archiveFormats.js +7 -0
  101. package/src/Processor/utils.js +2 -1
  102. package/src/RayzeeRenderer.js +3900 -0
  103. package/src/RenderSettings.js +68 -62
  104. package/src/Stages/ASVGF.js +13 -14
  105. package/src/Stages/AutoExposure.js +17 -17
  106. package/src/Stages/BilateralFilter.js +2 -1
  107. package/src/Stages/Compositor.js +8 -23
  108. package/src/Stages/DenoiserSettings.js +182 -0
  109. package/src/Stages/EdgeFilter.js +2 -1
  110. package/src/Stages/MotionVector.js +1 -1
  111. package/src/Stages/NRD.js +11 -5
  112. package/src/Stages/NormalDepth.js +12 -8
  113. package/src/Stages/PathTracer.js +336 -43
  114. package/src/Stages/PathTracerStage.js +69 -66
  115. package/src/Stages/Variance.js +1 -1
  116. package/src/Storage/BLASCache.js +1 -1
  117. package/src/Storage/CDFCache.js +18 -17
  118. package/src/Storage/DownloadCache.js +1 -1
  119. package/src/Storage/SceneGraphCodec.js +46 -3
  120. package/src/Storage/SpillStore.js +1 -1
  121. package/src/Storage/StorageManager.js +45 -9
  122. package/src/Storage/areas.js +4 -0
  123. package/src/TSL/BVHTraversal.js +50 -25
  124. package/src/TSL/Bidirectional.js +208 -0
  125. package/src/TSL/BidirectionalLamps.js +132 -0
  126. package/src/TSL/Clearcoat.js +15 -6
  127. package/src/TSL/Common.js +20 -15
  128. package/src/TSL/ConnectKernel.js +167 -0
  129. package/src/TSL/Debugger.js +1 -1
  130. package/src/TSL/Displacement.js +4 -3
  131. package/src/TSL/EmissiveSampling.js +65 -18
  132. package/src/TSL/Environment.js +77 -106
  133. package/src/TSL/EnvironmentCDF.js +85 -39
  134. package/src/TSL/ExtendKernel.js +8 -3
  135. package/src/TSL/FinalWriteKernel.js +4 -4
  136. package/src/TSL/GenerateKernel.js +18 -4
  137. package/src/TSL/HitFacet.js +35 -5
  138. package/src/TSL/LightBVHSampling.js +40 -9
  139. package/src/TSL/LightGenerateKernel.js +293 -0
  140. package/src/TSL/LightGuide.js +198 -0
  141. package/src/TSL/LightSplatKernel.js +180 -0
  142. package/src/TSL/LightVisibility.js +111 -0
  143. package/src/TSL/LightsCore.js +13 -38
  144. package/src/TSL/LightsDirect.js +28 -29
  145. package/src/TSL/LightsIndirect.js +3 -1
  146. package/src/TSL/LightsSampling.js +239 -51
  147. package/src/TSL/MaterialEvaluation.js +113 -31
  148. package/src/TSL/MaterialProperties.js +44 -17
  149. package/src/TSL/MaterialTransmission.js +52 -47
  150. package/src/TSL/MergeKernel.js +224 -0
  151. package/src/TSL/PathTracerCore.js +65 -29
  152. package/src/TSL/Random.js +5 -1
  153. package/src/TSL/SceneResources.js +51 -0
  154. package/src/TSL/ShadeKernel.js +985 -101
  155. package/src/TSL/Struct.js +10 -0
  156. package/src/TSL/Sun.js +1 -1
  157. package/src/TSL/TextureSampling.js +66 -61
  158. package/src/addons/archives.js +14 -0
  159. package/src/addons/bidirectional.js +13 -0
  160. package/src/addons/color.js +32 -0
  161. package/src/addons/formats.js +16 -0
  162. package/src/addons/physicalSky.js +14 -0
  163. package/src/addons/storage.js +19 -0
  164. package/src/core.js +31 -0
  165. package/src/index.js +13 -23
  166. package/src/integrators/BidirectionalIntegrator.js +838 -0
  167. package/src/managers/AnimationManager.js +88 -6
  168. package/src/managers/CameraManager.js +103 -46
  169. package/src/managers/DenoisingManager.js +14 -10
  170. package/src/managers/EnvironmentManager.js +125 -103
  171. package/src/managers/GoboManager.js +9 -0
  172. package/src/managers/MaterialDataManager.js +48 -37
  173. package/src/managers/UniformManager.js +32 -10
  174. package/dist/ToneMapGPU-B6XIe3DU.js +0 -544
  175. package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
  176. package/src/Processor/TreeletOptimizer.js +0 -574
@@ -11,7 +11,7 @@
11
11
  * 3. Sort reinsertions by area improvement, apply non-conflicting ones greedily.
12
12
  * 4. Repeat for a configurable number of iterations.
13
13
  *
14
- * Typically yields 10-20% SAH cost reduction on top of treelet optimization.
14
+ * On this binned-SAH build it buys ~0.04 % tree SAH (3.5M triangles, 2026-08) for ~430 ms of worker time.
15
15
  */
16
16
  export class ReinsertionOptimizer {
17
17
 
@@ -52,7 +52,7 @@ export class ReinsertionOptimizer {
52
52
 
53
53
  }
54
54
 
55
- // --- Surface area (half SA, consistent with TreeletOptimizer) ---
55
+ // --- Surface area (half SA) ---
56
56
 
57
57
  surfaceArea( node ) {
58
58
 
@@ -14,6 +14,9 @@
14
14
  * "sourceFile": "https://.../brown_photostudio_02_1k.hdr",
15
15
  * "rotation": 0, // degrees around Y, matches the environmentRotation setting
16
16
  * "intensity": 1 // drives both environmentIntensity and backgroundIntensity
17
+ * },
18
+ * "render": { // what the file asks the renderer for; reported, not applied
19
+ * "maxBounces": 15, "samples": 1024, "width": 1920, "height": 1080
17
20
  * }
18
21
  * }
19
22
  *
@@ -56,10 +59,11 @@ function pickPayload( container ) {
56
59
  const obj = asObject( container );
57
60
  if ( ! obj ) return null;
58
61
 
62
+ const has = ( o ) => o && ( o.environment !== undefined || o.render !== undefined );
59
63
  const scoped = asObject( obj.rayzee );
60
- if ( scoped && scoped.environment !== undefined ) return scoped;
64
+ if ( has( scoped ) ) return scoped;
61
65
 
62
- return obj.environment !== undefined ? obj : null;
66
+ return has( obj ) ? obj : null;
63
67
 
64
68
  }
65
69
 
@@ -84,10 +88,29 @@ function normalizeEnvironment( raw ) {
84
88
 
85
89
  }
86
90
 
91
+ const RENDER_KEYS = [ 'maxBounces', 'samples', 'width', 'height' ];
92
+
93
+ function normalizeRender( raw ) {
94
+
95
+ const render = asObject( raw );
96
+ if ( ! render ) return null;
97
+
98
+ const normalized = {};
99
+ for ( const key of RENDER_KEYS ) {
100
+
101
+ const n = asNumber( render[ key ] );
102
+ if ( n !== undefined && n > 0 ) normalized[ key ] = Math.round( n );
103
+
104
+ }
105
+
106
+ return Object.keys( normalized ).length ? normalized : null;
107
+
108
+ }
109
+
87
110
  /**
88
111
  * Reads scene metadata out of one `extras`-shaped container.
89
112
  * @param {Object|string|null} container
90
- * @returns {{ environment?: { sourceFile: string, rotation?: number, intensity?: number } }|null}
113
+ * @returns {{ environment?: { sourceFile: string, rotation?: number, intensity?: number }, render?: { maxBounces?: number, samples?: number, width?: number, height?: number } }|null}
91
114
  */
92
115
  export function parseSceneMetadata( container ) {
93
116
 
@@ -95,14 +118,16 @@ export function parseSceneMetadata( container ) {
95
118
  if ( ! payload ) return null;
96
119
 
97
120
  const environment = normalizeEnvironment( payload.environment );
98
- return environment ? { environment } : null;
121
+ const render = normalizeRender( payload.render );
122
+ if ( ! environment && ! render ) return null;
123
+ return { ...( environment ? { environment } : {} ), ...( render ? { render } : {} ) };
99
124
 
100
125
  }
101
126
 
102
127
  /**
103
128
  * Reads scene metadata out of a GLTFLoader parse result.
104
129
  * @param {Object} gltf - The object GLTFLoader resolves with
105
- * @returns {{ environment?: { sourceFile: string, rotation?: number, intensity?: number } }|null}
130
+ * @returns {ReturnType<typeof parseSceneMetadata>}
106
131
  */
107
132
  export function extractSceneMetadata( gltf ) {
108
133
 
@@ -17,8 +17,10 @@ import { BuildTimer } from './BuildTimer.js';
17
17
  import { createLogger, fmt, workerLogLevel } from '../utils/Logger.js';
18
18
  import { SRGBColorSpace } from 'three';
19
19
  import {
20
- TRIANGLE_DATA_LAYOUT, TEXTURE_CONSTANTS, getTextureBucketId, packTextureIndex, planTextureBuckets,
21
- packNormalOct, BVH_LEAF_MARKERS, BVH_FOLDED_FIRST_LIMIT, assertBVHIndexFits, TLAS_PLACEMENT_MASK, TRI_MATERIAL_MASK } from '../EngineDefaults.js';
20
+ TRIANGLE_DATA_LAYOUT, packNormalOct, TRI_MATERIAL_MASK, BVH_LEAF_MARKERS, BVH_FOLDED_FIRST_LIMIT,
21
+ assertBVHIndexFits, TLAS_PLACEMENT_MASK,
22
+ } from './BufferLayout.js';
23
+ import { TEXTURE_CONSTANTS, getTextureBucketId, packTextureIndex, planTextureBuckets } from './TextureBuckets.js';
22
24
  import { ISSUE_CODES } from '../EngineIssues.js';
23
25
  import { BLAS_CACHE_FORMAT, openBLASCache, saveBLASCache, templateChecksums, readNodesInto, readOrder } from '../Storage/BLASCache.js';
24
26
  import { sharedStorage } from '../Storage/shared.js';
@@ -116,14 +118,6 @@ export class SceneProcessor {
116
118
  enableTextureCache: true,
117
119
  maxConcurrentTextureTasks: Math.min( hardwareThreads(), 6 ),
118
120
  maxSceneBytes: MAX_SCENE_BYTES,
119
- // Treelet optimization configuration
120
- // Keep: `_buildBVH` sends `enabled: value !== false`, so undefined re-enables treelets.
121
- enableTreeletOptimization: false,
122
- treeletSize: 7, // 7 nodes gives 315 topologies for optimal enumeration
123
- treeletOptimizationPasses: 1,
124
- treeletMinImprovement: 0.01, // Minimum SAH improvement threshold
125
- // Above this triangle count the builder drops treelets to size 3.
126
- treeletComplexityThreshold: 50000,
127
121
  // Store the per-mesh BVHs of a scene whose build took at least this long (needs a sceneKey).
128
122
  sceneCache: true,
129
123
  sceneCacheMinBuildMs: SCENE_CACHE_MIN_BUILD_MS,
@@ -152,6 +146,8 @@ export class SceneProcessor {
152
146
  this.iridescenceThicknessMaps = [];
153
147
  this.specularIntensityMaps = [];
154
148
  this.specularColorMaps = [];
149
+ this.diffuseTransmissionMaps = [];
150
+ this.diffuseTransmissionColorMaps = [];
155
151
  this.directionalLights = [];
156
152
  this.cameras = [];
157
153
  this.spheres = [];
@@ -235,15 +231,6 @@ export class SceneProcessor {
235
231
  this.bvhBuilder = new BVHBuilder();
236
232
  this.bvhBuilder.maxLeafSize = this.config.maxLeafSize;
237
233
 
238
- // Configure treelet optimization
239
- this.bvhBuilder.setTreeletConfig( {
240
- enabled: this.config.enableTreeletOptimization,
241
- size: this.config.treeletSize,
242
- passes: this.config.treeletOptimizationPasses,
243
- minImprovement: this.config.treeletMinImprovement,
244
- complexityThreshold: this.config.treeletComplexityThreshold
245
- } );
246
-
247
234
  // Create and configure texture creator
248
235
  this.textureCreator = new TextureCreator( { maxTextureSize: this.config.maxTextureSize, issues: this.config.issues } );
249
236
  // The optimized TextureCreator will auto-detect capabilities and select optimal methods
@@ -877,6 +864,8 @@ export class SceneProcessor {
877
864
  this.iridescenceThicknessMaps = extractedData.iridescenceThicknessMaps;
878
865
  this.specularIntensityMaps = extractedData.specularIntensityMaps;
879
866
  this.specularColorMaps = extractedData.specularColorMaps;
867
+ this.diffuseTransmissionMaps = extractedData.diffuseTransmissionMaps ?? [];
868
+ this.diffuseTransmissionColorMaps = extractedData.diffuseTransmissionColorMaps ?? [];
880
869
  this.directionalLights = extractedData.directionalLights;
881
870
  this.cameras = extractedData.cameras;
882
871
 
@@ -1032,7 +1021,7 @@ export class SceneProcessor {
1032
1021
  }
1033
1022
 
1034
1023
  // Store all results, summing per-mesh split stats for one aggregate BVH line
1035
- const blasStats = { sah: 0, objMed: 0, spatMed: 0, failed: 0, treeletsImproved: 0, treeletsProcessed: 0 };
1024
+ const blasStats = { sah: 0, objMed: 0, spatMed: 0, failed: 0 };
1036
1025
 
1037
1026
  for ( const { m, range, result } of [ ...poolResults, ...parallelResults ] ) {
1038
1027
 
@@ -1043,8 +1032,6 @@ export class SceneProcessor {
1043
1032
  blasStats.objMed += st.objectMedianSplits ?? 0;
1044
1033
  blasStats.spatMed += st.spatialMedianSplits ?? 0;
1045
1034
  blasStats.failed += st.failedSplits ?? 0;
1046
- blasStats.treeletsImproved += st.treeletsImproved ?? 0;
1047
- blasStats.treeletsProcessed += st.treeletsProcessed ?? 0;
1048
1035
  this.performanceMetrics.blasWorkerTime += st.totalBuildTime ?? 0;
1049
1036
 
1050
1037
  }
@@ -1190,7 +1177,6 @@ export class SceneProcessor {
1190
1177
  `${fmt.n( table.setCount )} BLASes + TLAS`,
1191
1178
  `${fmt.n( this.bvh.recordCount )} nodes`,
1192
1179
  `SAH ${fmt.n( blasStats.sah )} · objMed ${blasStats.objMed} · spatMed ${blasStats.spatMed} · failed ${blasStats.failed}`,
1193
- blasStats.treeletsProcessed ? `treelets ${blasStats.treeletsImproved}/${blasStats.treeletsProcessed} improved` : null,
1194
1180
  fmt.ms( duration ),
1195
1181
  ] ) );
1196
1182
 
@@ -1218,13 +1204,6 @@ export class SceneProcessor {
1218
1204
 
1219
1205
  return {
1220
1206
  depth: this.config.bvhDepth,
1221
- treeletOptimization: {
1222
- enabled: this.config.enableTreeletOptimization !== false,
1223
- size: this.config.treeletSize,
1224
- passes: this.config.treeletOptimizationPasses,
1225
- minImprovement: this.config.treeletMinImprovement,
1226
- complexityThreshold: this.config.treeletComplexityThreshold
1227
- },
1228
1207
  reinsertionOptimization: {
1229
1208
  enabled: this.bvhBuilder.enableReinsertionOptimization,
1230
1209
  batchSizeRatio: this.bvhBuilder.reinsertionBatchSizeRatio,
@@ -1702,7 +1681,7 @@ export class SceneProcessor {
1702
1681
  * Each mesh is dispatched to an available BVHWorker; at most poolSize workers run concurrently.
1703
1682
  *
1704
1683
  * @param {Array<{m: number, range: {start: number, count: number}}>} tasks
1705
- * @param {Object} opts - Worker build options (depth, treeletOptimization, reinsertionOptimization)
1684
+ * @param {Object} opts - Worker build options (depth, reinsertionOptimization)
1706
1685
  * @param {Function} onProgress - Called with (completedCount) as builds finish
1707
1686
  * @returns {Promise<Array<{m, range, result}>>}
1708
1687
  * @private
@@ -1766,11 +1745,7 @@ export class SceneProcessor {
1766
1745
 
1767
1746
  const meshTriData = this.triangles.copyOf( range.start, range.count );
1768
1747
 
1769
- // Disable treelet for tiny meshes
1770
1748
  const triCount = range.count;
1771
- const treeletOpts = triCount <= 500
1772
- ? { ...opts.treeletOptimization, enabled: false }
1773
- : opts.treeletOptimization;
1774
1749
 
1775
1750
  worker._currentTask = { m, range };
1776
1751
  worker.postMessage( {
@@ -1781,7 +1756,6 @@ export class SceneProcessor {
1781
1756
  depth: opts.depth,
1782
1757
  reportProgress: false,
1783
1758
  sharedReorderBuffer: null,
1784
- treeletOptimization: treeletOpts,
1785
1759
  reinsertionOptimization: opts.reinsertionOptimization,
1786
1760
  maxLeafSize: opts.maxLeafSize,
1787
1761
  foldLeaves: opts.foldLeaves,
@@ -1824,7 +1798,7 @@ export class SceneProcessor {
1824
1798
  const result = {
1825
1799
  bvhData: data.bvhData,
1826
1800
  originalToBvh: data.originalToBvh || null,
1827
- splitStats: data.treeletStats || null,
1801
+ splitStats: data.splitStats || null,
1828
1802
  };
1829
1803
  onResult?.( m, range, result );
1830
1804
  results.push( { m, range, result } );
@@ -2041,11 +2015,12 @@ export class SceneProcessor {
2041
2015
 
2042
2016
  };
2043
2017
 
2044
- const srgbTypes = [ this.maps, this.emissiveMaps, this.sheenColorMaps, this.specularColorMaps ];
2018
+ const srgbTypes = [ this.maps, this.emissiveMaps, this.sheenColorMaps, this.specularColorMaps, this.diffuseTransmissionColorMaps ];
2045
2019
  const linearTypes = [
2046
2020
  this.normalMaps, this.bumpMaps, this.roughnessMaps, this.metalnessMaps, this.displacementMaps,
2047
2021
  this.anisotropyMaps, this.transmissionMaps, this.clearcoatMaps, this.clearcoatRoughnessMaps,
2048
2022
  this.sheenRoughnessMaps, this.iridescenceMaps, this.iridescenceThicknessMaps, this.specularIntensityMaps,
2023
+ this.diffuseTransmissionMaps,
2049
2024
  ];
2050
2025
 
2051
2026
  const srgbShapes = planTextureBuckets( poolSizes( srgbTypes ), cap, K );
@@ -2136,6 +2111,8 @@ export class SceneProcessor {
2136
2111
  iridescenceThickness: remapType( this.iridescenceThicknessMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2137
2112
  specularIntensity: remapType( this.specularIntensityMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2138
2113
  specularColor: remapType( this.specularColorMaps, srgbLists, srgbDedup, this._srgbTexPacked, srgbShapes ),
2114
+ diffuseTransmission: remapType( this.diffuseTransmissionMaps, linearLists, linearDedup, this._linearTexPacked, linearShapes ),
2115
+ diffuseTransmissionColor: remapType( this.diffuseTransmissionColorMaps, srgbLists, srgbDedup, this._srgbTexPacked, srgbShapes ),
2139
2116
  };
2140
2117
 
2141
2118
  return { srgbLists, linearLists, remap };
@@ -2170,6 +2147,8 @@ export class SceneProcessor {
2170
2147
  mat.iridescenceThicknessMap = fix( mat.iridescenceThicknessMap, remap.iridescenceThickness );
2171
2148
  mat.specularIntensityMap = fix( mat.specularIntensityMap, remap.specularIntensity );
2172
2149
  mat.specularColorMap = fix( mat.specularColorMap, remap.specularColor );
2150
+ mat.diffuseTransmissionMap = fix( mat.diffuseTransmissionMap, remap.diffuseTransmission );
2151
+ mat.diffuseTransmissionColorMap = fix( mat.diffuseTransmissionColorMap, remap.diffuseTransmissionColor );
2173
2152
 
2174
2153
  }
2175
2154
 
@@ -2265,6 +2244,8 @@ export class SceneProcessor {
2265
2244
  this.iridescenceThicknessMaps = [];
2266
2245
  this.specularIntensityMaps = [];
2267
2246
  this.specularColorMaps = [];
2247
+ this.diffuseTransmissionMaps = [];
2248
+ this.diffuseTransmissionColorMaps = [];
2268
2249
  this.directionalLights = [];
2269
2250
  this.cameras = [];
2270
2251
  this.spheres = [];
@@ -2477,15 +2458,6 @@ export class SceneProcessor {
2477
2458
 
2478
2459
  this.bvhBuilder.maxLeafSize = this.config.maxLeafSize;
2479
2460
 
2480
- // Update treelet optimization configuration
2481
- this.bvhBuilder.setTreeletConfig( {
2482
- enabled: this.config.enableTreeletOptimization,
2483
- size: this.config.treeletSize,
2484
- passes: this.config.treeletOptimizationPasses,
2485
- minImprovement: this.config.treeletMinImprovement,
2486
- complexityThreshold: this.config.treeletComplexityThreshold
2487
- } );
2488
-
2489
2461
  }
2490
2462
 
2491
2463
  // Note: TextureCreator auto-configures based on capabilities
@@ -2597,6 +2569,7 @@ export class SceneProcessor {
2597
2569
  async refitBVH( newPositions, newNormals ) {
2598
2570
 
2599
2571
  this._geometryVersion = ( this._geometryVersion ?? 0 ) + 1;
2572
+ this._tlasLeafBoxesOf = null;
2600
2573
 
2601
2574
  if ( ! this.bvh || ! this.triangles || ! this.instanceTable ) {
2602
2575
 
@@ -2648,13 +2621,19 @@ export class SceneProcessor {
2648
2621
  // can be released as soon as it is read. Callers hand over world space; triangles are
2649
2622
  // stored per instance, so each mesh comes back through its own inverse.
2650
2623
  const table = this.instanceTable;
2624
+ const FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
2625
+ const triRanges = [];
2626
+ const blasRanges = [];
2627
+ let owners = 0;
2651
2628
  for ( let i = 0; i < table.count; i ++ ) {
2652
2629
 
2653
2630
  if ( ! table.isSet[ i ] || ! table.isOwner( i ) ) continue;
2631
+ owners ++;
2654
2632
 
2655
2633
  const p = positionsFor( i );
2656
2634
  if ( ! p ) continue;
2657
2635
  this._updateMeshTrianglePositions( i, p );
2636
+ blasRanges.push( table.blasOffsetOf( i ), table.blasNodeCountOf( i ) );
2658
2637
 
2659
2638
  // Smooth normals overwrite the face normals just computed, so they follow per mesh.
2660
2639
  if ( normalsFor ) {
@@ -2664,8 +2643,18 @@ export class SceneProcessor {
2664
2643
 
2665
2644
  }
2666
2645
 
2646
+ const offset = table.triOffsetOf( i ) * FPT, count = table.triCountOf( i ) * FPT;
2647
+ const last = triRanges.at( - 1 );
2648
+ if ( last && last.offset + last.count === offset ) last.count += count;
2649
+ else triRanges.push( { offset, count } );
2650
+
2667
2651
  }
2668
2652
 
2653
+ const tlasNodeCount = table.tlasNodeCount;
2654
+ const partial = tlasNodeCount > 0 && blasRanges.length / 2 < owners;
2655
+ const bvhRanges = partial ? [ this.computeTLASDirtyRange() ] : null;
2656
+ for ( let r = 0; partial && r < blasRanges.length; r += 2 ) bvhRanges.push( { offset: blasRanges[ r ] * 16, count: blasRanges[ r + 1 ] * 16 } );
2657
+
2669
2658
  return new Promise( ( resolve, reject ) => {
2670
2659
 
2671
2660
  this._refitWorker.onmessage = ( e ) => {
@@ -2673,7 +2662,7 @@ export class SceneProcessor {
2673
2662
  const msg = e.data;
2674
2663
  if ( msg.type === 'refitComplete' ) {
2675
2664
 
2676
- resolve( { refitTimeMs: msg.refitTimeMs } );
2665
+ resolve( { refitTimeMs: msg.refitTimeMs, triRanges, bvhRanges } );
2677
2666
 
2678
2667
  } else if ( msg.type === 'error' ) {
2679
2668
 
@@ -2684,7 +2673,7 @@ export class SceneProcessor {
2684
2673
  };
2685
2674
 
2686
2675
  // Signal worker — no data transfer needed, everything is in shared memory
2687
- this._refitWorker.postMessage( { type: 'refit' } );
2676
+ this._refitWorker.postMessage( { type: 'refit', blasRanges: partial ? blasRanges : null, tlasNodeCount } );
2688
2677
 
2689
2678
  } );
2690
2679
 
@@ -2834,6 +2823,8 @@ export class SceneProcessor {
2834
2823
  const start = performance.now();
2835
2824
  const table = this.instanceTable;
2836
2825
  const composed = this._transformScratch ??= new Float32Array( 16 );
2826
+ if ( this._movedPlacements?.length !== table.count ) this._movedPlacements = new Uint8Array( table.count );
2827
+ const movedMask = this._movedPlacements;
2837
2828
  let moved = 0;
2838
2829
 
2839
2830
  for ( const meshIndex of meshIndices ) {
@@ -2870,13 +2861,15 @@ export class SceneProcessor {
2870
2861
  }
2871
2862
 
2872
2863
  this._writeLeafMatrix( p );
2864
+ movedMask[ p ] = 1;
2873
2865
  moved ++;
2874
2866
 
2875
2867
  }
2876
2868
 
2877
2869
  }
2878
2870
 
2879
- this._refitTLAS();
2871
+ this._refitTLAS( movedMask );
2872
+ movedMask.fill( 0 );
2880
2873
 
2881
2874
  return { refitTimeMs: performance.now() - start, placements: moved };
2882
2875
 
@@ -3357,6 +3350,8 @@ export class SceneProcessor {
3357
3350
  /** Keep the chunked BVH and its u32 index view in step. @private */
3358
3351
  _setBVHData( data ) {
3359
3352
 
3353
+ this._tlasLeafBoxesOf = null;
3354
+
3360
3355
  const records = ! data ? null
3361
3356
  : ( data instanceof ChunkedRecords
3362
3357
  ? data
@@ -3538,9 +3533,11 @@ export class SceneProcessor {
3538
3533
  /**
3539
3534
  * Refit TLAS AABBs in-place without rebuilding the tree structure.
3540
3535
  * O(tlasNodeCount) bottom-up pass — much faster than full SAH rebuild.
3536
+ * @param {Uint8Array} [moved] - by placement: only these leaves' world boxes are recomputed, the
3537
+ * rest kept from the last pass. Every leaf when omitted, or when nothing valid was kept.
3541
3538
  * @private
3542
3539
  */
3543
- _refitTLAS() {
3540
+ _refitTLAS( moved = null ) {
3544
3541
 
3545
3542
  const tlasNodeCount = this.instanceTable.tlasNodeCount;
3546
3543
 
@@ -3548,10 +3545,12 @@ export class SceneProcessor {
3548
3545
  if ( ! this._tlasBounds || this._tlasBounds.length < tlasNodeCount * 6 ) {
3549
3546
 
3550
3547
  this._tlasBounds = new Float32Array( tlasNodeCount * 6 );
3548
+ this._tlasLeafBoxesOf = null;
3551
3549
 
3552
3550
  }
3553
3551
 
3554
3552
  const table = this.instanceTable;
3553
+ const keep = moved && this._tlasLeafBoxesOf === table;
3555
3554
 
3556
3555
  // Bottom-up pass: reverse iteration over TLAS nodes
3557
3556
  for ( let i = tlasNodeCount - 1; i >= 0; i -- ) {
@@ -3567,7 +3566,7 @@ export class SceneProcessor {
3567
3566
  // every placement of a shared geometry onto one box, so all but one copy sat
3568
3567
  // outside its own bounds and rays walked straight past it.
3569
3568
  const entryIndex = idxChunk[ o + 1 ] & TLAS_PLACEMENT_MASK;
3570
- if ( entryIndex < table.count ) {
3569
+ if ( entryIndex < table.count && ! ( keep && ! moved[ entryIndex ] ) ) {
3571
3570
 
3572
3571
  table.writeWorldAABB( entryIndex, this._tlasBounds, i * 6 );
3573
3572
 
@@ -3608,6 +3607,8 @@ export class SceneProcessor {
3608
3607
 
3609
3608
  }
3610
3609
 
3610
+ this._tlasLeafBoxesOf = table;
3611
+
3611
3612
  }
3612
3613
 
3613
3614
  /**
@@ -3659,9 +3660,6 @@ export class SceneProcessor {
3659
3660
 
3660
3661
  };
3661
3662
 
3662
- // Disable treelet for tiny meshes
3663
- const treeletEnabled = entry.triCount > 500;
3664
-
3665
3663
  worker.postMessage( {
3666
3664
  triangleData: meshTriData.buffer,
3667
3665
  triangleByteOffset: meshTriData.byteOffset,
@@ -3677,13 +3675,6 @@ export class SceneProcessor {
3677
3675
  minBins: this.bvhBuilder.minBins,
3678
3676
  reportProgress: false,
3679
3677
  sharedReorderBuffer: null,
3680
- treeletOptimization: {
3681
- enabled: treeletEnabled,
3682
- size: this.config.treeletSize,
3683
- passes: this.config.treeletOptimizationPasses,
3684
- minImprovement: this.config.treeletMinImprovement,
3685
- complexityThreshold: this.config.treeletComplexityThreshold
3686
- },
3687
3678
  reinsertionOptimization: {
3688
3679
  enabled: this.bvhBuilder.enableReinsertionOptimization,
3689
3680
  batchSizeRatio: this.bvhBuilder.reinsertionBatchSizeRatio,
@@ -1,16 +1,13 @@
1
1
  /**
2
2
  * ShaderBuilder.js — shared scene texture-node factory for the path tracer.
3
3
  *
4
- * Creates the texture/storage nodes the wavefront kernels read (environment, material map
5
- * arrays, previous-frame MRT, adaptive-sampling, gobo/IES) and configures the module-level
6
- * shadow/alpha/gobo/IES shader state. Nodes are created once and updated in-place via
7
- * .value mutation to preserve compiled shader-graph references.
4
+ * Creates the texture/storage nodes the wavefront kernels read (environment, previous-frame MRT,
5
+ * adaptive-sampling, gobo/IES). Nodes are created once and updated in-place via .value mutation to
6
+ * preserve compiled shader-graph references.
8
7
  */
9
8
 
10
9
  import { texture } from 'three/tsl';
11
10
  import { LinearFilter, DataArrayTexture } from 'three';
12
- import { setAlphaShadowsUniform } from '../TSL/LightsDirect.js';
13
- import { setGoboMapsTexture, setIESProfilesTexture } from '../TSL/LightsCore.js';
14
11
  import { createLogger } from '../utils/Logger.js';
15
12
 
16
13
  const log = createLogger( 'shader' );
@@ -81,8 +78,7 @@ export class ShaderBuilder {
81
78
 
82
79
  }
83
80
 
84
- // Creates the shared scene texture nodes (env, material maps, prev-frame, adaptive, gobo, IES)
85
- // + configures the module-level shadow/alpha/gobo/IES shader state read by the wavefront kernels.
81
+ // Creates the shared scene texture nodes (env, prev-frame, adaptive, gobo, IES).
86
82
  // Call from setupMaterial before the kernels are built.
87
83
  createSceneTextureNodes( stage, storageTextures ) {
88
84
 
@@ -93,9 +89,6 @@ export class ShaderBuilder {
93
89
  // TSL params; emissive reads offset by stage.emissiveVec4Offset.
94
90
  const lightBufferStorage = stage.lightStorageNode;
95
91
 
96
- // Set alpha-shadow uniform (module-level in LightsDirect.js, read at runtime)
97
- setAlphaShadowsUniform( stage.uniforms.get( 'enableAlphaShadows' ) );
98
-
99
92
  const envTex = texture( stage.environment.environmentTexture );
100
93
 
101
94
  // Previous-frame texture nodes — initialized from readTarget textures
@@ -115,17 +108,14 @@ export class ShaderBuilder {
115
108
 
116
109
  };
117
110
 
118
- // Material map arrays (consolidated size buckets) are owned by PathTracer's
119
- // independent wavefront nodes + setMaterialBucketTextures/setShadowAlbedoMaps —
120
- // see PathTracer._buildWavefrontKernels. Nothing material-map related is bound here.
111
+ // Material map arrays (consolidated size buckets) are owned by PathTracer's independent wavefront
112
+ // nodes, handed to its kernels as SceneResources — see PathTracer._buildWavefrontKernels.
121
113
 
122
114
  // Spot light gobo array — placeholder until GoboManager populates it.
123
115
  const goboMapsTex = stage.goboMaps ? texture( stage.goboMaps ) : createArrayPlaceholder();
124
- setGoboMapsTexture( goboMapsTex );
125
116
 
126
117
  // Spot light IES profiles array — placeholder until IESManager populates it.
127
118
  const iesProfilesTex = stage.iesProfiles ? texture( stage.iesProfiles ) : createArrayPlaceholder();
128
- setIESProfilesTexture( iesProfilesTex );
129
119
 
130
120
  const result = {
131
121
  triStorage, bvhStorage, matStorage, lightBufferStorage,
@@ -6,12 +6,11 @@ import {
6
6
  /**
7
7
  * SimpleSky
8
8
  *
9
- * CPU-based gradient and solid colour environment texture generator.
9
+ * CPU-based solid colour environment texture generator.
10
10
  * Produces a DataTexture directly — no render targets, no GPU readback,
11
11
  * no resource lifecycle issues with the WebGPU backend.
12
12
  *
13
13
  * Public API matches SimpleSkyRenderer:
14
- * renderGradient(params) → texture
15
14
  * renderSolid(params) → texture
16
15
  */
17
16
 
@@ -36,66 +35,6 @@ export class SimpleSky {
36
35
 
37
36
  }
38
37
 
39
- /**
40
- * Generate a three-colour vertical gradient sky.
41
- * @param {Object} params - { zenithColor, horizonColor, groundColor } (Three.js Color)
42
- * @returns {DataTexture} Equirectangular gradient texture
43
- */
44
- renderGradient( params ) {
45
-
46
- const startTime = performance.now();
47
- const { width, height } = this;
48
- const pixels = this._pixels;
49
-
50
- const zr = params.zenithColor.r, zg = params.zenithColor.g, zb = params.zenithColor.b;
51
- const hr = params.horizonColor.r, hg = params.horizonColor.g, hb = params.horizonColor.b;
52
- const gr = params.groundColor.r, gg = params.groundColor.g, gb = params.groundColor.b;
53
-
54
- for ( let y = 0; y < height; y ++ ) {
55
-
56
- const t = ( y + 0.5 ) / height;
57
- let r, g, b;
58
-
59
- if ( t > 0.5 ) {
60
-
61
- // Top half: horizon → zenith
62
- const blend = ( t - 0.5 ) * 2.0;
63
- r = hr + ( zr - hr ) * blend;
64
- g = hg + ( zg - hg ) * blend;
65
- b = hb + ( zb - hb ) * blend;
66
-
67
- } else {
68
-
69
- // Bottom half: ground → horizon
70
- const blend = t * 2.0;
71
- r = gr + ( hr - gr ) * blend;
72
- g = gg + ( hg - gg ) * blend;
73
- b = gb + ( hb - gb ) * blend;
74
-
75
- }
76
-
77
- for ( let x = 0; x < width; x ++ ) {
78
-
79
- const idx = ( y * width + x ) * 4;
80
- pixels[ idx ] = r;
81
- pixels[ idx + 1 ] = g;
82
- pixels[ idx + 2 ] = b;
83
- pixels[ idx + 3 ] = 1.0;
84
-
85
- }
86
-
87
- }
88
-
89
- this._texture.needsUpdate = true;
90
- // Fresh linear Rec.709 pixels in a reused texture. Colour management records which space a
91
- // texture's pixels hold so it never converts them twice; left in place, that record would
92
- // claim these were already converted and they never would be.
93
- delete this._texture.userData?.__rayzeeColorSpace;
94
- this.lastRenderTime = performance.now() - startTime;
95
- return this._texture;
96
-
97
- }
98
-
99
38
  /**
100
39
  * Generate a uniform solid-colour sky.
101
40
  * @param {Object} params - { color } (Three.js Color)
@@ -0,0 +1,3 @@
1
+ // Hestroffer & Magnan 1998: I(µ)/I(1) = µ^α, α = −0.023 + 0.292 / λ[µm], at each primary's centroid. The core's sun
2
+ // (TSL/Sun.js) and the physical sky's disc average both read it.
3
+ export const LIMB_DARKENING_EXPONENT = [ 610, 550, 465 ].map( l => - 0.023 + 0.292 / ( l / 1000 ) );
@@ -1,4 +1,4 @@
1
- import { TRIANGLE_DATA_LAYOUT } from '../EngineDefaults.js';
1
+ import { TRIANGLE_DATA_LAYOUT } from './BufferLayout.js';
2
2
  import { rebaseNodes } from './BVHLeafFold.js';
3
3
 
4
4
  const FLOATS_PER_NODE = 16;
@@ -14,9 +14,30 @@
14
14
 
15
15
  import { StorageTexture, RenderTarget } from 'three/webgpu';
16
16
  import { RGBAFormat, FloatType, LinearFilter, NearestFilter, Box2, Vector2 } from 'three';
17
- import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
18
17
  import { createLogger, fmt } from '../utils/Logger.js';
19
18
 
19
+ // Hard ceiling the engine supports for the reserved (pre-allocated) render size. 4K (3840×2160)
20
+ // fits within 4096². Raising the reserved size to this pins ~1.5 GB of MRT textures — opt-in only.
21
+ export const MAX_RESERVABLE_RENDER_SIZE = 4096;
22
+
23
+ // Reserved render size: every per-resolution compute StorageTexture + aux buffer is pre-allocated at
24
+ // this SQUARE size and never resized at runtime (works around three.js StorageTexture-resize bugs —
25
+ // see TSL/patches history). Render resolution must not exceed it; the engine warns + ignores larger.
26
+ // It is a LIVE binding (mutable) so it can be raised (e.g. for 4K) via setReservedRenderSize(); consumers
27
+ // read it inside their constructors, and stages already built at a lower value are re-initialised in place
28
+ // by PathTracerApp.setReservedRenderResolution() — which is the device-gated API hosts should call, at any
29
+ // point in the lifecycle. Default 2048 (zero VRAM regression). See docs/internal/specs/wavefront-chunked-pool.md.
30
+ export let MAX_STORAGE_TEXTURE_SIZE = 2048;
31
+
32
+ // Set the reserved render size. MUST be called before pipeline construction (stages pre-allocate at
33
+ // this value and cannot resize). Clamped to [256, MAX_RESERVABLE_RENDER_SIZE]. Returns the applied value.
34
+ export function setReservedRenderSize( px ) {
35
+
36
+ MAX_STORAGE_TEXTURE_SIZE = Math.max( 256, Math.min( MAX_RESERVABLE_RENDER_SIZE, Math.floor( px ) || 256 ) );
37
+ return MAX_STORAGE_TEXTURE_SIZE;
38
+
39
+ }
40
+
20
41
  const log = createLogger( 'gpu' );
21
42
 
22
43
  // Compute-output StorageTexture at the reserved size, never resized (see above). LinearFilter is
@@ -15,9 +15,7 @@
15
15
  * known before its subtree is built, which is what lets the write be single-pass.
16
16
  */
17
17
 
18
- import {
19
- BVH_LEAF_MARKERS, assertBVHIndexFits, bvhIndexView, TLAS_LEAF_IDENTITY, TLAS_PLACEMENT_MASK
20
- } from '../EngineDefaults.js';
18
+ import { BVH_LEAF_MARKERS, assertBVHIndexFits, bvhIndexView, TLAS_LEAF_IDENTITY, TLAS_PLACEMENT_MASK } from './BufferLayout.js';
21
19
  import { invertAffineInto, isIdentityAt } from './InstanceTable.js';
22
20
 
23
21
  const FLOATS_PER_NODE = 16;