rayzee 9.4.1 → 9.5.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 (145) hide show
  1. package/README.md +226 -25
  2. package/dist/ActiveColor-YvuQq44O.js +13 -0
  3. package/dist/ArchiveImporter-xuUJiklP.js +4301 -0
  4. package/dist/AssetConfig-DSEntE65.js +30 -0
  5. package/dist/BidirectionalIntegrator-C5PKjJoT.js +934 -0
  6. package/dist/ColorManagement-CheBCPaW.js +1488 -0
  7. package/dist/EngineDefaults-DH3bI5t-.js +844 -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-BnHghprG.js +97 -0
  12. package/dist/{NeuralRetouch-vkdXXyK2.js → NeuralRetouch-CW11yLZ_.js} +8 -6
  13. package/dist/{NeuralSuperRes-FVqRJKQV.js → NeuralSuperRes-BekaecYg.js} +55 -120
  14. package/dist/PathTracer-Ccl9KOuF.js +29729 -0
  15. package/dist/PhysicalSky-Dv2PMFGW.js +1219 -0
  16. package/dist/RayzeeRenderer-BQxnzCTD.js +3542 -0
  17. package/dist/SolarLimb-ken-JbDK.js +721 -0
  18. package/dist/ToneMapGPU-BHi5JzIq.js +177 -0
  19. package/dist/ViewTransforms-D0rTLBsX.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 +2266 -44125
  33. package/dist/rayzee.umd.js +485 -473
  34. package/dist/sceneCachePolicy-BmwYOYDu.js +183 -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/EngineDefaults.js +23 -14
  41. package/src/EngineEvents.js +2 -0
  42. package/src/EngineIssues.js +1 -0
  43. package/src/PathTracerApp.js +617 -4016
  44. package/src/Pipeline/PipelineContext.js +0 -4
  45. package/src/Platform.js +36 -0
  46. package/src/Processor/ArchiveCache.js +1 -1
  47. package/src/Processor/ArchiveImporter.js +1259 -0
  48. package/src/Processor/AssetLoader.js +322 -1746
  49. package/src/Processor/AtmosphereModel.js +1 -2
  50. package/src/Processor/BVHBuilder.js +4 -95
  51. package/src/Processor/EnvironmentExactTable.js +187 -0
  52. package/src/Processor/EquirectHDRInfo.js +30 -223
  53. package/src/Processor/FileFormats.js +116 -0
  54. package/src/Processor/GLTFDecoders.js +85 -0
  55. package/src/Processor/GLTFDiffuseTransmission.js +63 -0
  56. package/src/Processor/GeometryExtractor.js +12 -1
  57. package/src/Processor/IESParser.js +47 -35
  58. package/src/Processor/KernelManager.js +35 -2
  59. package/src/Processor/MaterialPacking.js +8 -7
  60. package/src/Processor/PBRT/PBRTMaterials.js +129 -41
  61. package/src/Processor/PBRT/PBRTParser.js +30 -17
  62. package/src/Processor/PBRT/PBRTSceneBuilder.js +548 -37
  63. package/src/Processor/PBRT/PBRTTextureBake.js +154 -0
  64. package/src/Processor/PBRT/PFM.js +59 -0
  65. package/src/Processor/PackedRayBuffer.js +116 -9
  66. package/src/Processor/ParallelBVHBuilder.js +3 -6
  67. package/src/Processor/PhysicalSky.js +5 -4
  68. package/src/Processor/PlatformImageLoader.js +50 -6
  69. package/src/Processor/QueueManager.js +12 -3
  70. package/src/Processor/ReinsertionOptimizer.js +2 -2
  71. package/src/Processor/SceneProcessor.js +15 -55
  72. package/src/Processor/ShaderBuilder.js +6 -16
  73. package/src/Processor/SimpleSky.js +1 -62
  74. package/src/Processor/SolarLimb.js +3 -0
  75. package/src/Processor/TextureCreator.js +1 -1
  76. package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
  77. package/src/Processor/Workers/BVHWorker.js +5 -18
  78. package/src/Processor/Workers/CDFWorker.js +6 -183
  79. package/src/Processor/archiveFormats.js +7 -0
  80. package/src/Processor/utils.js +2 -1
  81. package/src/RayzeeRenderer.js +3902 -0
  82. package/src/RenderSettings.js +49 -41
  83. package/src/Stages/ASVGF.js +5 -7
  84. package/src/Stages/AutoExposure.js +3 -12
  85. package/src/Stages/Compositor.js +8 -23
  86. package/src/Stages/NRD.js +9 -2
  87. package/src/Stages/NormalDepth.js +9 -6
  88. package/src/Stages/PathTracer.js +254 -38
  89. package/src/Stages/PathTracerStage.js +11 -16
  90. package/src/Storage/BLASCache.js +1 -1
  91. package/src/Storage/CDFCache.js +18 -17
  92. package/src/Storage/DownloadCache.js +1 -1
  93. package/src/Storage/SceneGraphCodec.js +46 -3
  94. package/src/Storage/SpillStore.js +1 -1
  95. package/src/Storage/StorageManager.js +2 -4
  96. package/src/Storage/areas.js +4 -0
  97. package/src/TSL/BVHTraversal.js +62 -24
  98. package/src/TSL/Bidirectional.js +204 -0
  99. package/src/TSL/BidirectionalLamps.js +132 -0
  100. package/src/TSL/Clearcoat.js +15 -6
  101. package/src/TSL/Common.js +19 -14
  102. package/src/TSL/ConnectKernel.js +147 -0
  103. package/src/TSL/Debugger.js +1 -1
  104. package/src/TSL/Displacement.js +4 -3
  105. package/src/TSL/EmissiveSampling.js +64 -17
  106. package/src/TSL/Environment.js +77 -106
  107. package/src/TSL/EnvironmentCDF.js +85 -39
  108. package/src/TSL/ExtendKernel.js +8 -3
  109. package/src/TSL/FinalWriteKernel.js +4 -4
  110. package/src/TSL/GenerateKernel.js +18 -4
  111. package/src/TSL/HitFacet.js +35 -5
  112. package/src/TSL/LightBVHSampling.js +40 -9
  113. package/src/TSL/LightGenerateKernel.js +293 -0
  114. package/src/TSL/LightGuide.js +198 -0
  115. package/src/TSL/LightSplatKernel.js +165 -0
  116. package/src/TSL/LightsCore.js +13 -38
  117. package/src/TSL/LightsDirect.js +28 -29
  118. package/src/TSL/LightsIndirect.js +3 -1
  119. package/src/TSL/LightsSampling.js +90 -38
  120. package/src/TSL/MaterialEvaluation.js +113 -31
  121. package/src/TSL/MaterialProperties.js +44 -17
  122. package/src/TSL/MaterialTransmission.js +52 -47
  123. package/src/TSL/MergeKernel.js +218 -0
  124. package/src/TSL/PathTracerCore.js +65 -29
  125. package/src/TSL/Random.js +4 -0
  126. package/src/TSL/SceneResources.js +51 -0
  127. package/src/TSL/ShadeKernel.js +881 -103
  128. package/src/TSL/Struct.js +10 -0
  129. package/src/TSL/Sun.js +1 -1
  130. package/src/TSL/TextureSampling.js +65 -60
  131. package/src/addons/archives.js +14 -0
  132. package/src/addons/bidirectional.js +13 -0
  133. package/src/addons/color.js +32 -0
  134. package/src/addons/formats.js +16 -0
  135. package/src/addons/physicalSky.js +14 -0
  136. package/src/addons/storage.js +19 -0
  137. package/src/core.js +35 -0
  138. package/src/index.js +3 -1
  139. package/src/integrators/BidirectionalIntegrator.js +832 -0
  140. package/src/managers/EnvironmentManager.js +94 -91
  141. package/src/managers/MaterialDataManager.js +42 -0
  142. package/src/managers/UniformManager.js +0 -1
  143. package/dist/ToneMapGPU-DKQM1pMd.js +0 -552
  144. package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
  145. package/src/Processor/TreeletOptimizer.js +0 -574
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * EnvironmentManager.js
3
- * Manages HDRI loading, CDF importance sampling, procedural/gradient/solid sky
3
+ * Manages HDRI loading, CDF importance sampling, procedural/solid sky
4
4
  * generation, and environment rotation for the path tracing pipeline.
5
5
  *
6
6
  * Storage buffer nodes are created once and never replaced — only .value
@@ -8,18 +8,19 @@
8
8
  */
9
9
 
10
10
  import {
11
- RGBAFormat, RedFormat, FloatType, LinearFilter, Vector2, Vector3, Color, Matrix4, DataTexture,
11
+ RGBAFormat, FloatType, LinearFilter, Vector2, Vector3, Color, Matrix4, DataTexture,
12
12
  } from 'three';
13
13
  import { EquirectHDRInfo } from '../Processor/EquirectHDRInfo.js';
14
- import { PhysicalSky } from '../Processor/PhysicalSky.js';
14
+ import { packExactTable } from '../Processor/EnvironmentExactTable.js';
15
15
  import { SimpleSky } from '../Processor/SimpleSky.js';
16
16
  import { convertLinearTriple } from '../Color/WorkingMatrix.js';
17
17
  import { createLogger, fmt } from '../utils/Logger.js';
18
18
 
19
19
  const log = createLogger( 'env' );
20
20
  import { ENGINE_DEFAULTS as DEFAULT_STATE } from '../EngineDefaults.js';
21
- import { getActiveColorManagement } from '../Color/ColorManagement.js';
21
+ import { getActiveColorManagement } from '../Color/ActiveColor.js';
22
22
  import { loadCDF, saveCDF } from '../Storage/CDFCache.js';
23
+ import { ISSUE_CODES } from '../EngineIssues.js';
23
24
 
24
25
  const SKY_WIDTH = 1024;
25
26
  const SKY_HEIGHT = 512;
@@ -45,7 +46,12 @@ export class EnvironmentManager {
45
46
  // CDF computation engine
46
47
  this.equirectHdrInfo = new EquirectHDRInfo();
47
48
 
48
- // Sky renderers (lazy init)
49
+ // Sky renderers (lazy init). The physical sky is a capability: setProceduralSky() installs its class.
50
+ this.ProceduralSky = null;
51
+ this._loadProceduralSky = null;
52
+ this._proceduralSkyLoading = null;
53
+ /** @type {?import('../EngineIssues.js').IssueLog} */
54
+ this.issues = null;
49
55
  this.physicalSky = null;
50
56
  this.simpleSkyRenderer = null;
51
57
  this._sun = null;
@@ -83,11 +89,6 @@ export class EnvironmentManager {
83
89
  this.envParams = {
84
90
  mode: 'hdri',
85
91
 
86
- // Gradient Sky
87
- gradientZenithColor: new Color( DEFAULT_STATE.gradientZenithColor ),
88
- gradientHorizonColor: new Color( DEFAULT_STATE.gradientHorizonColor ),
89
- gradientGroundColor: new Color( DEFAULT_STATE.gradientGroundColor ),
90
-
91
92
  // Solid Color Sky
92
93
  solidSkyColor: new Color( DEFAULT_STATE.solidSkyColor ),
93
94
 
@@ -104,7 +105,7 @@ export class EnvironmentManager {
104
105
 
105
106
  /**
106
107
  * Optional callbacks set by the owning stage.
107
- * @type {{ onReset?: Function, onAutoExposureReset?: Function, getSceneTextureNodes?: Function }}
108
+ * @type {{ onReset?: Function, onLightingChanged?: Function, getSceneTextureNodes?: Function }}
108
109
  */
109
110
  this.callbacks = {};
110
111
 
@@ -116,9 +117,9 @@ export class EnvironmentManager {
116
117
  // ===== MODE STATE MACHINE =====
117
118
 
118
119
  /**
119
- * Switches the environment mode (hdri, gradient, color, procedural — the physical sky).
120
+ * Switches the environment mode (hdri, color, procedural — the physical sky).
120
121
  * Preserves the HDRI texture when switching away, restores when switching back.
121
- * @param {'hdri'|'gradient'|'color'|'procedural'} mode
122
+ * @param {'hdri'|'color'|'procedural'} mode
122
123
  */
123
124
  async setMode( mode ) {
124
125
 
@@ -132,11 +133,7 @@ export class EnvironmentManager {
132
133
 
133
134
  }
134
135
 
135
- if ( mode === 'gradient' ) {
136
-
137
- await this.generateGradientTexture();
138
-
139
- } else if ( mode === 'color' ) {
136
+ if ( mode === 'color' ) {
140
137
 
141
138
  await this.generateSolidColorTexture();
142
139
 
@@ -154,7 +151,7 @@ export class EnvironmentManager {
154
151
  if ( mode !== 'procedural' && prev === 'procedural' ) this._releaseSky();
155
152
 
156
153
  this.markDirty();
157
- this.callbacks.onAutoExposureReset?.();
154
+ this.callbacks.onLightingChanged?.();
158
155
  this._notifyReset();
159
156
 
160
157
  }
@@ -185,9 +182,6 @@ export class EnvironmentManager {
185
182
  return {
186
183
  mode: p.mode,
187
184
  hdri: this.hdriSource,
188
- gradientZenithColor: p.gradientZenithColor.toArray(),
189
- gradientHorizonColor: p.gradientHorizonColor.toArray(),
190
- gradientGroundColor: p.gradientGroundColor.toArray(),
191
185
  solidSkyColor: p.solidSkyColor.toArray(),
192
186
  skyModel: 'atmosphere',
193
187
  skySunDirection: p.skySunDirection.toArray(),
@@ -212,7 +206,7 @@ export class EnvironmentManager {
212
206
  const p = this.envParams;
213
207
  // Sessions saved before the physical sky gave the same names other meanings.
214
208
  const atmosphere = state.skyModel === 'atmosphere';
215
- const colors = [ 'gradientZenithColor', 'gradientHorizonColor', 'gradientGroundColor', 'solidSkyColor', 'skySunDirection' ];
209
+ const colors = [ 'solidSkyColor', 'skySunDirection' ];
216
210
  for ( const key of atmosphere ? [ ...colors, 'skyGroundAlbedo' ] : colors ) {
217
211
 
218
212
  if ( Array.isArray( state[ key ] ) ) p[ key ].fromArray( state[ key ] );
@@ -229,7 +223,9 @@ export class EnvironmentManager {
229
223
 
230
224
  }
231
225
 
232
- if ( state.mode && ( state.mode !== 'hdri' || p.mode !== 'hdri' ) ) await this.setMode( state.mode );
226
+ // A session saved with the removed gradient sky keeps the sky it is already showing.
227
+ const known = [ 'hdri', 'color', 'procedural' ].includes( state.mode );
228
+ if ( known && ( state.mode !== 'hdri' || p.mode !== 'hdri' ) ) await this.setMode( state.mode );
233
229
 
234
230
  }
235
231
 
@@ -249,13 +245,6 @@ export class EnvironmentManager {
249
245
 
250
246
  }
251
247
 
252
- /** @see generateGradientTexture */
253
- generateGradient() {
254
-
255
- return this.generateGradientTexture();
256
-
257
- }
258
-
259
248
  /** @see generateSolidColorTexture */
260
249
  generateSolid() {
261
250
 
@@ -276,42 +265,43 @@ export class EnvironmentManager {
276
265
  * Initialize the packed CDF storage buffer with placeholder data.
277
266
  * Must be called before shader compilation so the node exists in the graph.
278
267
  *
279
- * 1×1 R32F placeholder until a real env CDF is built (env IS is off meanwhile).
268
+ * 1×1 RGBA32F placeholder until a real env table is built (env IS is off meanwhile).
280
269
  * @private
281
270
  */
282
271
  _initCDFTexture() {
283
272
 
284
- this.envCDFTexture = new DataTexture( new Float32Array( [ 0 ] ), 1, 1, RedFormat, FloatType );
273
+ this.envCDFTexture = new DataTexture( new Float32Array( 4 ), 1, 1, RGBAFormat, FloatType );
285
274
  this.envCDFTexture.needsUpdate = true;
275
+ this._exactTable = null;
286
276
 
287
277
  }
288
278
 
289
279
  /**
290
- * Rebuild the CDF texture from equirectHdrInfo. Packs the 2D conditional + 1D marginal into one
291
- * R32F texture of (W+1)×H: conditional[cy*W+cx] at texel (cx, cy); marginal[cy] at texel (W, cy).
280
+ * Rebuild the CDF texture from equirectHdrInfo's table, laid out as packExactTable (EnvironmentExactTable.js)
281
+ * has it.
292
282
  * @private
293
283
  */
294
284
  _updateCDFTexture() {
295
285
 
296
- const marginal = this.equirectHdrInfo.marginalData;
297
- const conditional = this.equirectHdrInfo.conditionalData;
298
- if ( ! marginal || ! conditional ) return;
286
+ const info = this.equirectHdrInfo;
287
+ if ( ! info.exactConditional || ! ( info.exactWidth > 0 ) ) return;
299
288
 
300
- const W = this.equirectHdrInfo.width;
301
- const H = this.equirectHdrInfo.height;
302
- const texW = W + 1;
303
- const data = new Float32Array( texW * H );
304
- for ( let cy = 0; cy < H; cy ++ ) {
289
+ const { data, width, height } = packExactTable( info );
290
+ if ( ! this.envCDFTexture?._isPhysicalSky ) this.envCDFTexture?.dispose?.();
291
+ this.envCDFTexture = new DataTexture( data, width, height, RGBAFormat, FloatType );
292
+ this.envCDFTexture.needsUpdate = true;
293
+ this._exactTable = { radianceIntegral: info.radianceIntegral };
305
294
 
306
- const dstRow = cy * texW;
307
- data.set( conditional.subarray( cy * W, cy * W + W ), dstRow ); // conditional → columns [0,W)
308
- data[ dstRow + W ] = marginal[ cy ]; // marginal → column W
295
+ }
309
296
 
310
- }
297
+ /**
298
+ * The environment as a bidirectional light source: ∫ luminance dω of the map, before intensity, while the
299
+ * CDF texture carries its table; null otherwise.
300
+ * @returns {?{ radianceIntegral: number }}
301
+ */
302
+ get exactTable() {
311
303
 
312
- if ( ! this.envCDFTexture?._isPhysicalSky ) this.envCDFTexture?.dispose?.();
313
- this.envCDFTexture = new DataTexture( data, texW, H, RedFormat, FloatType );
314
- this.envCDFTexture.needsUpdate = true;
304
+ return this.scene.environment ? this._exactTable ?? null : null;
315
305
 
316
306
  }
317
307
 
@@ -388,7 +378,6 @@ export class EnvironmentManager {
388
378
  this._cdfSignature = null;
389
379
  this._updateCDFTexture();
390
380
  this.uniforms.set( 'envTotalSum', 0.0 );
391
- this.uniforms.set( 'envCompensationDelta', 0.0 );
392
381
  return;
393
382
 
394
383
  }
@@ -406,7 +395,6 @@ export class EnvironmentManager {
406
395
  this._cdfSignature = null;
407
396
  this._updateCDFTexture();
408
397
  this.uniforms.set( 'envTotalSum', 0.0 );
409
- this.uniforms.set( 'envCompensationDelta', 0.0 );
410
398
  return;
411
399
 
412
400
  }
@@ -436,7 +424,6 @@ export class EnvironmentManager {
436
424
 
437
425
  this._updateCDFTexture();
438
426
  this.uniforms.set( 'envTotalSum', this.equirectHdrInfo.totalSum );
439
- this.uniforms.set( 'envCompensationDelta', this.equirectHdrInfo.compensationDelta );
440
427
 
441
428
  const { width, height } = this.equirectHdrInfo;
442
429
  if ( width && height ) {
@@ -455,7 +442,6 @@ export class EnvironmentManager {
455
442
  this._cdfSignature = null;
456
443
  log.error( 'CDF build failed:', error );
457
444
  this.uniforms.set( 'envTotalSum', 0.0 );
458
- this.uniforms.set( 'envCompensationDelta', 0.0 );
459
445
 
460
446
  }
461
447
 
@@ -517,7 +503,6 @@ export class EnvironmentManager {
517
503
  this._cdfSignature = null;
518
504
  this._updateCDFTexture();
519
505
  this.uniforms.set( 'envTotalSum', 0.0 );
520
- this.uniforms.set( 'envCompensationDelta', 0.0 );
521
506
 
522
507
  }
523
508
 
@@ -547,7 +532,7 @@ export class EnvironmentManager {
547
532
 
548
533
  /**
549
534
  * Enter 'hdri' mode ahead of an async HDRI install, and drop the texture stashed by a
550
- * previous setMode( 'gradient' | 'color' | 'procedural' ): a new HDRI makes it
535
+ * previous setMode( 'color' | 'procedural' ): a new HDRI makes it
551
536
  * unreachable, so leaving it in place both leaks it and lets a setMode( 'hdri' )
552
537
  * arriving mid-download restore it over the map we are about to install.
553
538
  * @param {import('three').Texture} [incoming] - Never disposed, even if it is the stash.
@@ -576,38 +561,6 @@ export class EnvironmentManager {
576
561
 
577
562
  // ===== SKY GENERATORS =====
578
563
 
579
- /**
580
- * Generate gradient sky texture and set as environment.
581
- */
582
- async generateGradientTexture() {
583
-
584
- if ( ! this.simpleSkyRenderer ) {
585
-
586
- this.simpleSkyRenderer = new SimpleSky( 512, 256 );
587
-
588
- }
589
-
590
- const params = {
591
- zenithColor: this.envParams.gradientZenithColor,
592
- horizonColor: this.envParams.gradientHorizonColor,
593
- groundColor: this.envParams.gradientGroundColor,
594
- };
595
-
596
- try {
597
-
598
- const texture = this.simpleSkyRenderer.renderGradient( params );
599
- texture._isGeneratedProcedural = true;
600
- await this.setEnvironmentMap( texture );
601
- this.uniforms.set( 'hasSun', 0 );
602
-
603
- } catch ( error ) {
604
-
605
- log.error( 'gradient sky generation failed:', error );
606
-
607
- }
608
-
609
- }
610
-
611
564
  /**
612
565
  * Generate solid color sky texture and set as environment.
613
566
  */
@@ -645,6 +598,29 @@ export class EnvironmentManager {
645
598
  */
646
599
  generateProceduralSkyTexture() {
647
600
 
601
+ if ( ! this.ProceduralSky && this._loadProceduralSky ) {
602
+
603
+ this._proceduralSkyLoading ??= this._loadProceduralSky().then( ( Sky ) => {
604
+
605
+ this._proceduralSkyLoading = null;
606
+ if ( ! this.ProceduralSky ) this.ProceduralSky = Sky;
607
+
608
+ } );
609
+ return this._proceduralSkyLoading.then( () => this.generateProceduralSkyTexture() );
610
+
611
+ }
612
+
613
+ if ( ! this.ProceduralSky ) {
614
+
615
+ this.issues?.record(
616
+ ISSUE_CODES.CAPABILITY_MISSING,
617
+ 'procedural mode needs the physical sky: environmentManager.setProceduralSky( PhysicalSky ), from rayzee/addons/physical-sky',
618
+ { capability: 'physical-sky' }
619
+ );
620
+ return Promise.resolve();
621
+
622
+ }
623
+
648
624
  this._skyRequested = true;
649
625
  this._skyCaughtUp ??= new Promise( resolve => void ( this._resolveSky = resolve ) );
650
626
  this._pumpSky();
@@ -688,7 +664,7 @@ export class EnvironmentManager {
688
664
 
689
665
  }
690
666
 
691
- const sky = this.physicalSky ??= new PhysicalSky( SKY_WIDTH, SKY_HEIGHT );
667
+ const sky = this.physicalSky ??= new this.ProceduralSky( SKY_WIDTH, SKY_HEIGHT );
692
668
  const albedo = p.skyGroundAlbedo;
693
669
  const bake = ++ this._skyBakes;
694
670
  const latest = () => bake === this._skyBakes && ! this._skyRequested;
@@ -710,12 +686,12 @@ export class EnvironmentManager {
710
686
  this._installSky( texture, cdfTexture );
711
687
 
712
688
  // Frames until they land normalise by an older bake's.
713
- const { totalSum, compensationDelta } = await stats;
689
+ const { totalSum, radianceIntegral } = await stats;
714
690
  if ( p.mode === 'procedural' && sky === this.physicalSky && bake > this._skyStatsBake ) {
715
691
 
716
692
  this._skyStatsBake = bake;
717
693
  this.uniforms.set( 'envTotalSum', totalSum );
718
- this.uniforms.set( 'envCompensationDelta', compensationDelta );
694
+ if ( this._exactTable?.sky ) this._exactTable.radianceIntegral = radianceIntegral;
719
695
  if ( latest() ) this._notifyReset();
720
696
 
721
697
  }
@@ -736,6 +712,32 @@ export class EnvironmentManager {
736
712
 
737
713
  }
738
714
 
715
+ /**
716
+ * Installs the class that bakes 'procedural' mode — `PhysicalSky` from rayzee/addons/physical-sky: constructed with
717
+ * (width, height), `bake( renderer, params )` returns `{ texture, cdfTexture, sun, stats }`, and `dispose( renderer )`.
718
+ * @param {?Function} Sky
719
+ */
720
+ setProceduralSky( Sky ) {
721
+
722
+ this._loadProceduralSky = null;
723
+ if ( Sky === this.ProceduralSky ) return;
724
+ this._releaseSky();
725
+ this.ProceduralSky = Sky;
726
+ if ( Sky && this.envParams.mode === 'procedural' ) this.generateProceduralSkyTexture();
727
+
728
+ }
729
+
730
+ /**
731
+ * Installs the physical sky on first use: `load()` resolves to the class setProceduralSky() takes, the first time
732
+ * 'procedural' mode is baked.
733
+ * @param {function(): Promise<Function>} load
734
+ */
735
+ setProceduralSkyLoader( load ) {
736
+
737
+ this._loadProceduralSky = load;
738
+
739
+ }
740
+
739
741
  /** @private */
740
742
  _installSky( texture, cdfTexture ) {
741
743
 
@@ -750,6 +752,7 @@ export class EnvironmentManager {
750
752
 
751
753
  // So an HDRI coming back rebuilds its own table.
752
754
  this._cdfSignature = null;
755
+ if ( ! this._exactTable?.sky ) this._exactTable = { radianceIntegral: 0, sky: true };
753
756
  texture._isGeneratedProcedural = true;
754
757
  this.scene.environment = texture;
755
758
  this.environmentTexture = texture;
@@ -18,6 +18,7 @@ import { resolveMaterialTextures, MATERIAL_VALUE_SOURCE } from '../Processor/Geo
18
18
  import { convertLinearTriple, convertLinearTriples, getWorkingMatrixSpace } from '../Color/WorkingMatrix.js';
19
19
  import { createLogger, fmt } from '../utils/Logger.js';
20
20
  import { toPortable } from '../SceneState/portable.js';
21
+ import { MATERIAL_LAYERS } from '../TSL/SceneResources.js';
21
22
 
22
23
  /** Material buffers already moved into the working space, so a re-init cannot convert twice. */
23
24
  const convertedBuffers = new WeakSet();
@@ -41,6 +42,7 @@ const TEXTURE_POOLS = [
41
42
  [ 'displacementMap', false ], [ 'anisotropyMap', false ], [ 'transmissionMap', false ],
42
43
  [ 'clearcoatMap', false ], [ 'clearcoatRoughnessMap', false ], [ 'sheenRoughnessMap', false ],
43
44
  [ 'iridescenceMap', false ], [ 'iridescenceThicknessMap', false ], [ 'specularIntensityMap', false ],
45
+ [ 'diffuseTransmissionColorMap', true ], [ 'diffuseTransmissionMap', false ],
44
46
  ];
45
47
 
46
48
  // Scalar slots readable via getMaterialProperty().
@@ -67,6 +69,7 @@ const SCALAR_PROPERTY_OFFSETS = {
67
69
  subsurfaceAnisotropy: M.SUBSURFACE_ANISOTROPY,
68
70
  anisotropy: M.ANISOTROPY,
69
71
  anisotropyRotation: M.ANISOTROPY_ROTATION, // radians, unlike the degrees hosts usually show
72
+ diffuseTransmission: M.DIFFUSE_TRANSMISSION,
70
73
  };
71
74
 
72
75
  export class MaterialDataManager {
@@ -119,6 +122,7 @@ export class MaterialDataManager {
119
122
  */
120
123
  static COLOR_OFFSETS = [
121
124
  M.COLOR, M.EMISSIVE, M.ATTENUATION_COLOR, M.SHEEN_COLOR, M.SPECULAR_COLOR, M.SUBSURFACE_COLOR,
125
+ M.DIFFUSE_TRANSMISSION_COLOR,
122
126
  ].filter( o => Number.isInteger( o ) );
123
127
 
124
128
  /**
@@ -186,6 +190,7 @@ export class MaterialDataManager {
186
190
  this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
187
191
  this._sources = [ ...sources ];
188
192
  this._hostSet = [];
193
+ this.callbacks.onMaterialFeaturesChanged?.();
189
194
  log.debug( `${fmt.n( this.materialCount )} materials (storage buffer)` );
190
195
 
191
196
  }
@@ -300,6 +305,26 @@ export class MaterialDataManager {
300
305
 
301
306
  }
302
307
 
308
+ /**
309
+ * Which MATERIAL_LAYERS some material uses (its factor above 0): the kernels compile the others out.
310
+ * @returns {Object<string, boolean>}
311
+ */
312
+ materialLayers() {
313
+
314
+ const layers = Object.fromEntries( MATERIAL_LAYERS.map( ( layer ) => [ layer, false ] ) );
315
+ const data = this.materialStorageAttr?.array;
316
+ if ( ! data ) return layers;
317
+ for ( let i = 0; i < this.materialCount; i ++ ) {
318
+
319
+ const base = i * M.FLOATS_PER_MATERIAL;
320
+ for ( const layer of MATERIAL_LAYERS ) if ( data[ base + SCALAR_PROPERTY_OFFSETS[ layer ] ] > 0 ) layers[ layer ] = true;
321
+
322
+ }
323
+
324
+ return layers;
325
+
326
+ }
327
+
303
328
  /**
304
329
  * Where a material property's value came from, as a MATERIAL_VALUE_SOURCE. `default` means the
305
330
  * model never said and the engine filled it in.
@@ -466,6 +491,19 @@ export class MaterialDataManager {
466
491
  case 'subsurfaceAnisotropy': data[ stride + M.SUBSURFACE_ANISOTROPY ] = value; break;
467
492
  case 'anisotropy': data[ stride + M.ANISOTROPY ] = value; break;
468
493
  case 'anisotropyRotation': data[ stride + M.ANISOTROPY_ROTATION ] = value; break;
494
+ case 'diffuseTransmission': data[ stride + M.DIFFUSE_TRANSMISSION ] = value; break;
495
+ case 'diffuseTransmissionColor':
496
+ if ( value.r !== undefined ) {
497
+
498
+ this._writeColor( data, stride + M.DIFFUSE_TRANSMISSION_COLOR, value.r, value.g, value.b );
499
+
500
+ } else if ( Array.isArray( value ) ) {
501
+
502
+ this._writeColor( data, stride + M.DIFFUSE_TRANSMISSION_COLOR, value[ 0 ], value[ 1 ], value[ 2 ] );
503
+
504
+ }
505
+
506
+ break;
469
507
  case 'subsurfaceColor':
470
508
  if ( value.r !== undefined ) {
471
509
 
@@ -510,6 +548,9 @@ export class MaterialDataManager {
510
548
 
511
549
  }
512
550
 
551
+ // A material layer is compiled in only while some material uses it.
552
+ if ( MATERIAL_LAYERS.includes( property ) ) this.callbacks.onMaterialFeaturesChanged?.();
553
+
513
554
  this._notifyReset();
514
555
 
515
556
  }
@@ -545,6 +586,7 @@ export class MaterialDataManager {
545
586
  // Both read back the block, so they must follow the write.
546
587
  this._patchTriangleSideForMaterial( materialIndex, data[ base + M.SIDE ] );
547
588
  this._recomputeOpaqueBlockerForMaterial( materialIndex );
589
+ this.callbacks.onMaterialFeaturesChanged?.();
548
590
 
549
591
  this.materialStorageAttr.needsUpdate = true;
550
592
  this._notifyReset();
@@ -265,7 +265,6 @@ export class UniformManager {
265
265
  ub( 'enableEnvironment', DEFAULT_STATE.enableEnvironment );
266
266
  u( 'environmentMatrix', new Matrix4(), 'mat4' );
267
267
  u( 'envTotalSum', 0.0, 'float' );
268
- u( 'envCompensationDelta', 0.0, 'float' );
269
268
  u( 'envResolution', new Vector2( 1, 1 ), 'vec2' );
270
269
  ub( 'groundProjectionEnabled', DEFAULT_STATE.groundProjectionEnabled );
271
270
  u( 'groundProjectionRadius', DEFAULT_STATE.groundProjectionRadius, 'float' );