rayzee 6.5.0 → 7.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.
- package/README.md +24 -5
- package/dist/rayzee.es.js +7624 -7063
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +157 -236
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +26 -9
- package/src/PathTracerApp.js +118 -26
- package/src/Pipeline/PipelineContext.js +1 -2
- package/src/Pipeline/RenderPipeline.js +1 -1
- package/src/Pipeline/RenderStage.js +1 -1
- package/src/Processor/CameraOptimizer.js +0 -5
- package/src/Processor/GeometryExtractor.js +6 -0
- package/src/Processor/KernelManager.js +277 -0
- package/src/Processor/PackedRayBuffer.js +291 -0
- package/src/Processor/QueueManager.js +173 -0
- package/src/Processor/SceneProcessor.js +1 -0
- package/src/Processor/ShaderBuilder.js +11 -317
- package/src/Processor/StorageTexturePool.js +29 -15
- package/src/Processor/VRAMTracker.js +169 -0
- package/src/Processor/utils.js +11 -110
- package/src/RenderSettings.js +0 -3
- package/src/Stages/ASVGF.js +151 -78
- package/src/Stages/BilateralFilter.js +34 -10
- package/src/Stages/EdgeFilter.js +2 -3
- package/src/Stages/MotionVector.js +16 -9
- package/src/Stages/NormalDepth.js +17 -5
- package/src/Stages/PathTracer.js +671 -1456
- package/src/Stages/PathTracerStage.js +1451 -0
- package/src/Stages/SSRC.js +32 -15
- package/src/Stages/Variance.js +35 -12
- package/src/TSL/CompactKernel.js +110 -0
- package/src/TSL/DebugKernel.js +98 -0
- package/src/TSL/Environment.js +13 -11
- package/src/TSL/ExtendKernel.js +75 -0
- package/src/TSL/FinalWriteKernel.js +121 -0
- package/src/TSL/GenerateKernel.js +111 -0
- package/src/TSL/LightsSampling.js +2 -2
- package/src/TSL/PathTracerCore.js +43 -1039
- package/src/TSL/ShadeKernel.js +876 -0
- package/src/TSL/patches.js +81 -4
- package/src/index.js +3 -0
- package/src/managers/CameraManager.js +1 -1
- package/src/managers/DenoisingManager.js +40 -75
- package/src/managers/EnvironmentManager.js +30 -39
- package/src/managers/OverlayManager.js +7 -22
- package/src/managers/UniformManager.js +0 -3
- package/src/managers/helpers/TileHelper.js +2 -2
- package/src/Stages/AdaptiveSampling.js +0 -483
- package/src/TSL/PathTracer.js +0 -384
- package/src/managers/TileManager.js +0 -298
package/src/TSL/patches.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rayzee patches for Three.js / TSL.
|
|
3
3
|
*
|
|
4
|
-
* Side-effect on import: installs `WebGPUBackend.
|
|
5
|
-
* (restores r183 function-scoped `var` emission for compute
|
|
6
|
-
* a register-allocation regression in the path tracer's hot
|
|
4
|
+
* Side-effect on import: installs two `WebGPUBackend.prototype` overrides —
|
|
5
|
+
* `createNodeBuilder` (restores r183 function-scoped `var` emission for compute
|
|
6
|
+
* shaders, preventing a register-allocation regression in the path tracer's hot
|
|
7
|
+
* loop) and `initTimestampQuery` (enlarges the stats-gl timestamp query pool so
|
|
8
|
+
* the wavefront tracer's high per-frame compute-pass count doesn't overflow it).
|
|
7
9
|
*
|
|
8
10
|
* Export: `struct()` — drop-in replacement for TSL's `struct()` returning
|
|
9
11
|
* a proxy factory that supports GLSL-style dot-notation field access.
|
|
@@ -64,7 +66,82 @@ WebGPUBackend.prototype.createNodeBuilder = function ( object, renderer ) {
|
|
|
64
66
|
};
|
|
65
67
|
|
|
66
68
|
// ---------------------------------------------------------------------------
|
|
67
|
-
// 2.
|
|
69
|
+
// 2. Larger timestamp query pool (stats-gl GPU/compute timing)
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Three.js lazily creates each timestamp query pool with a hardcoded 2048
|
|
72
|
+
// queries (= 1024 passes) — `WebGPUBackend.initTimestampQuery` / the upstream
|
|
73
|
+
// `// TODO: Variable maxQueries?`. The wavefront tracer issues hundreds of
|
|
74
|
+
// compute passes per frame (peak right after a maxBounces change: the survivor
|
|
75
|
+
// curve is invalid, so the bounce loop runs the full `loopBound` at full
|
|
76
|
+
// dispatch with no early-exit — ~560 passes / 1124 queries at production
|
|
77
|
+
// settings). stats-gl resolves once per frame, but the resolve is async and
|
|
78
|
+
// `mapAsync` lags several frames under that GPU load, so the counter isn't reset
|
|
79
|
+
// before it overflows → "Maximum number of queries exceeded" + dropped timings.
|
|
80
|
+
//
|
|
81
|
+
// Two parts:
|
|
82
|
+
// a) Grow the pool to 4096 queries on first use. 4096 is the WebGPU hard cap on
|
|
83
|
+
// a query set's count ("Query count exceeds the maximum query count (4096)")
|
|
84
|
+
// — 2× the upstream default, the most a single pool can hold (~2048 passes).
|
|
85
|
+
// That alone fully covers the interactive case (~200 passes/frame); anything
|
|
86
|
+
// larger fails CreateQuerySet validation.
|
|
87
|
+
// b) When even 4096 isn't enough (production spike), degrade gracefully: skip
|
|
88
|
+
// tracking the overflow passes silently instead of `warnOnce` + an invalid
|
|
89
|
+
// descriptor. The reported compute ms briefly undercounts during the spike;
|
|
90
|
+
// rendering is never affected (timestamps don't gate compute correctness).
|
|
91
|
+
const TIMESTAMP_POOL_MAX_QUERIES = 4096;
|
|
92
|
+
|
|
93
|
+
// Drop-in for the pool's allocateQueriesForContext minus the warnOnce on overflow
|
|
94
|
+
// — returns null silently when full so the pass is cleanly skipped (see below).
|
|
95
|
+
function _allocateQueriesSilently( uid ) {
|
|
96
|
+
|
|
97
|
+
if ( ! this.trackTimestamp || this.isDisposed ) return null;
|
|
98
|
+
if ( this.currentQueryIndex + 2 > this.maxQueries ) return null; // full: skip, no warn
|
|
99
|
+
const baseOffset = this.currentQueryIndex;
|
|
100
|
+
this.currentQueryIndex += 2;
|
|
101
|
+
this.queryOffsets.set( uid, baseOffset );
|
|
102
|
+
return baseOffset;
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const _origInitTimestampQuery = WebGPUBackend.prototype.initTimestampQuery;
|
|
107
|
+
|
|
108
|
+
WebGPUBackend.prototype.initTimestampQuery = function ( type, uid, descriptor ) {
|
|
109
|
+
|
|
110
|
+
const poolWasMissing = this.trackTimestamp && ! this.timestampQueryPool[ type ];
|
|
111
|
+
|
|
112
|
+
_origInitTimestampQuery.call( this, type, uid, descriptor );
|
|
113
|
+
|
|
114
|
+
// (a) First use: replace the fresh 2048 pool with a 4096 one of the same class,
|
|
115
|
+
// migrating the single allocation just made (offset 0) and re-pointing this
|
|
116
|
+
// pass's descriptor. Safe — first pass of the first tracked frame, nothing in
|
|
117
|
+
// flight. Swap in the silent allocator so future overflows don't warn.
|
|
118
|
+
if ( poolWasMissing ) {
|
|
119
|
+
|
|
120
|
+
const pool = this.timestampQueryPool[ type ];
|
|
121
|
+
if ( pool && pool.maxQueries < TIMESTAMP_POOL_MAX_QUERIES && descriptor.timestampWrites ) {
|
|
122
|
+
|
|
123
|
+
const Pool = pool.constructor;
|
|
124
|
+
const bigPool = new Pool( this.device, type, TIMESTAMP_POOL_MAX_QUERIES );
|
|
125
|
+
bigPool.allocateQueriesForContext = _allocateQueriesSilently;
|
|
126
|
+
bigPool.allocateQueriesForContext( uid ); // re-take offset 0 for this pass
|
|
127
|
+
this.timestampQueryPool[ type ] = bigPool;
|
|
128
|
+
descriptor.timestampWrites.querySet = bigPool.querySet; // offsets 0/1 unchanged
|
|
129
|
+
pool.dispose(); // nothing in flight — first pass of the first tracked frame
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// (b) On overflow the (silent) allocator returns null, but upstream still wrote
|
|
136
|
+
// a descriptor with a null begin index — which both collides on slot 1 and is
|
|
137
|
+
// invalid. Drop it so the pass is cleanly untimed.
|
|
138
|
+
const tw = descriptor.timestampWrites;
|
|
139
|
+
if ( tw && tw.beginningOfPassWriteIndex == null ) descriptor.timestampWrites = undefined;
|
|
140
|
+
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// 3. TSL struct proxy — enables GLSL-style dot-notation field access
|
|
68
145
|
// ---------------------------------------------------------------------------
|
|
69
146
|
// TSL structs require `.get('fieldName')` for member access, but GLSL-style
|
|
70
147
|
// dot notation (`.fieldName`) is more natural and matches ported code.
|
package/src/index.js
CHANGED
|
@@ -44,6 +44,9 @@ export { IESManager } from './managers/IESManager.js';
|
|
|
44
44
|
export { DenoisingManager } from './managers/DenoisingManager.js';
|
|
45
45
|
export { OverlayManager } from './managers/OverlayManager.js';
|
|
46
46
|
|
|
47
|
+
// VRAM accounting
|
|
48
|
+
export { VRAMTracker, bufferBytes, textureBytes } from './Processor/VRAMTracker.js';
|
|
49
|
+
|
|
47
50
|
// Pipeline infrastructure (for advanced consumers building custom stages)
|
|
48
51
|
export { RenderPipeline } from './Pipeline/RenderPipeline.js';
|
|
49
52
|
export { RenderStage, StageExecutionMode } from './Pipeline/RenderStage.js';
|
|
@@ -261,7 +261,7 @@ export class CameraManager extends EventDispatcher {
|
|
|
261
261
|
* @param {Object} params.assetLoader
|
|
262
262
|
* @param {import('three').Mesh} params.floorPlane
|
|
263
263
|
* @param {number} params.currentFocusDistance
|
|
264
|
-
* @param {import('
|
|
264
|
+
* @param {import('../Stages/PathTracer.js').PathTracer} params.pathTracer
|
|
265
265
|
* @param {Function} params.setFocusDistance - Callback to update uniform + settings
|
|
266
266
|
* @param {Function} params.softReset - Callback for soft accumulation reset
|
|
267
267
|
* @param {Function} params.hardReset - Callback for hard accumulation reset
|
|
@@ -41,7 +41,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
41
41
|
this.pipeline = pipeline;
|
|
42
42
|
|
|
43
43
|
// Stage references — only used internally for orchestration
|
|
44
|
-
this._stages = stages; // { pathTracer, asvgf, variance, bilateralFilter,
|
|
44
|
+
this._stages = stages; // { pathTracer, asvgf, variance, bilateralFilter, edgeFilter, ssrc, autoExposure, compositor }
|
|
45
45
|
|
|
46
46
|
this._getExposure = getExposure;
|
|
47
47
|
this._getSaturation = getSaturation;
|
|
@@ -231,7 +231,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
231
231
|
|
|
232
232
|
// Disable all real-time denoisers first
|
|
233
233
|
if ( s.asvgf ) s.asvgf.enabled = false;
|
|
234
|
-
if ( s.variance
|
|
234
|
+
if ( s.variance ) s.variance.enabled = false;
|
|
235
235
|
if ( s.bilateralFilter ) s.bilateralFilter.enabled = false;
|
|
236
236
|
if ( s.edgeFilter ) s.edgeFilter.setFilteringEnabled( false );
|
|
237
237
|
if ( s.ssrc ) s.ssrc.enabled = false;
|
|
@@ -258,6 +258,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
258
258
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
this._syncGBufferStages();
|
|
262
|
+
|
|
261
263
|
}
|
|
262
264
|
|
|
263
265
|
/**
|
|
@@ -282,6 +284,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
282
284
|
// Coordinate with EdgeAware filtering
|
|
283
285
|
if ( s.edgeFilter ) s.edgeFilter.setFilteringEnabled( ! enabled );
|
|
284
286
|
|
|
287
|
+
this._syncGBufferStages();
|
|
288
|
+
|
|
285
289
|
}
|
|
286
290
|
|
|
287
291
|
/**
|
|
@@ -315,35 +319,49 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
315
319
|
}
|
|
316
320
|
|
|
317
321
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
322
|
+
* Gate the G-buffer stages (NormalDepth, MotionVector) on demand: they only
|
|
323
|
+
* need to run when a real-time denoiser consumes their output. Idling them
|
|
324
|
+
* otherwise skips MotionVector's per-frame compute + copies during preview
|
|
325
|
+
* navigation and frees their textures. Call after any consumer toggle.
|
|
326
|
+
*
|
|
327
|
+
* MotionVector requires NormalDepth (reads pathtracer:normalDepth) and its
|
|
328
|
+
* consumers (ASVGF, SSRC) are a subset of NormalDepth's, so NormalDepth is
|
|
329
|
+
* always enabled whenever MotionVector is. Adaptive sampling / Variance / OIDN
|
|
330
|
+
* do NOT read these signals, so they don't keep the G-buffer alive.
|
|
320
331
|
*/
|
|
321
|
-
|
|
332
|
+
_syncGBufferStages() {
|
|
322
333
|
|
|
323
334
|
const s = this._stages;
|
|
335
|
+
const nd = s.normalDepth;
|
|
336
|
+
const mv = s.motionVector;
|
|
324
337
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
338
|
+
// motionVector:* consumed by ASVGF + SSRC
|
|
339
|
+
const motionNeeded = !! ( s.asvgf?.enabled || s.ssrc?.enabled );
|
|
340
|
+
// pathtracer:normalDepth consumed by ASVGF, SSRC, EdgeFilter, BilateralFilter
|
|
341
|
+
const normalNeeded = motionNeeded || !! ( s.edgeFilter?.enabled || s.bilateralFilter?.enabled );
|
|
329
342
|
|
|
330
|
-
|
|
343
|
+
if ( nd ) {
|
|
331
344
|
|
|
332
|
-
|
|
333
|
-
|
|
345
|
+
// On disabled→enabled, re-arm dirty/history so the first frame recomputes
|
|
346
|
+
// (not the stale static fast-path) and seeds prev = current.
|
|
347
|
+
if ( normalNeeded && ! nd.enabled ) nd.reset();
|
|
348
|
+
nd.enabled = normalNeeded;
|
|
334
349
|
|
|
335
|
-
|
|
350
|
+
}
|
|
336
351
|
|
|
337
|
-
|
|
352
|
+
if ( mv ) {
|
|
338
353
|
|
|
339
|
-
|
|
354
|
+
// On re-enable, force a camera-history reseed (matricesInitialized survives
|
|
355
|
+
// normal resets) so the first frame reports zero motion, not a spike.
|
|
356
|
+
if ( motionNeeded && ! mv.enabled ) {
|
|
340
357
|
|
|
341
|
-
|
|
358
|
+
mv.matricesInitialized = false;
|
|
359
|
+
mv.isFirstFrame = true;
|
|
360
|
+
mv.frameCount = 0;
|
|
342
361
|
|
|
343
|
-
|
|
344
|
-
if ( ! enabled && this.pipeline?.context && ! s.asvgf?.enabled ) {
|
|
362
|
+
}
|
|
345
363
|
|
|
346
|
-
|
|
364
|
+
mv.enabled = motionNeeded;
|
|
347
365
|
|
|
348
366
|
}
|
|
349
367
|
|
|
@@ -546,6 +564,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
546
564
|
|
|
547
565
|
}
|
|
548
566
|
|
|
567
|
+
this._syncGBufferStages();
|
|
568
|
+
|
|
549
569
|
}
|
|
550
570
|
|
|
551
571
|
/** Updates SSRC stage parameters. */
|
|
@@ -569,29 +589,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
569
589
|
|
|
570
590
|
}
|
|
571
591
|
|
|
572
|
-
/**
|
|
573
|
-
* Updates adaptive sampling parameters (with settings bridge).
|
|
574
|
-
* @param {Object} params
|
|
575
|
-
*/
|
|
576
|
-
setAdaptiveSamplingParams( params ) {
|
|
577
|
-
|
|
578
|
-
if ( params.min !== undefined ) this._stages.pathTracer?.setUniform( 'adaptiveSamplingMin', params.min );
|
|
579
|
-
if ( params.adaptiveSamplingMax !== undefined ) this._settings?.set( 'adaptiveSamplingMax', params.adaptiveSamplingMax );
|
|
580
|
-
this._stages.adaptiveSampling?.setAdaptiveSamplingParameters( params );
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Toggle the AdaptiveSampling heatmap compute pass. When enabled, the
|
|
586
|
-
* stage writes the heatmap to its public `heatmapTarget` RenderTarget —
|
|
587
|
-
* the host is responsible for rendering it.
|
|
588
|
-
*/
|
|
589
|
-
toggleAdaptiveSamplingHelper( enabled ) {
|
|
590
|
-
|
|
591
|
-
this._stages.adaptiveSampling?.setHeatmapEnabled( enabled );
|
|
592
|
-
|
|
593
|
-
}
|
|
594
|
-
|
|
595
592
|
// ── OIDN ─────────────────────────────────────────────────────
|
|
596
593
|
|
|
597
594
|
/** Enables or disables Intel OIDN denoiser. */
|
|
@@ -608,27 +605,13 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
608
605
|
|
|
609
606
|
}
|
|
610
607
|
|
|
611
|
-
/** Enables or disables the
|
|
612
|
-
setOIDNTileHelper( enabled ) {
|
|
613
|
-
|
|
614
|
-
this._setTileHelper( enabled );
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/** Enables or disables the tile helper overlay. */
|
|
608
|
+
/** Enables or disables the denoise/upscale progress overlay. */
|
|
619
609
|
setTileHelperEnabled( enabled ) {
|
|
620
610
|
|
|
621
611
|
this._setTileHelper( enabled );
|
|
622
612
|
|
|
623
613
|
}
|
|
624
614
|
|
|
625
|
-
/** Enables or disables tile highlight. */
|
|
626
|
-
setTileHighlightEnabled( enabled ) {
|
|
627
|
-
|
|
628
|
-
this._setTileHelper( enabled );
|
|
629
|
-
|
|
630
|
-
}
|
|
631
|
-
|
|
632
615
|
// ── AI Upscaler ──────────────────────────────────────────────
|
|
633
616
|
|
|
634
617
|
/** Enables or disables the AI upscaler. */
|
|
@@ -665,17 +648,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
665
648
|
|
|
666
649
|
}
|
|
667
650
|
|
|
668
|
-
/**
|
|
669
|
-
* Enables or disables adaptive sampling (convenience wrapper with settings bridge).
|
|
670
|
-
* @param {boolean} enabled
|
|
671
|
-
*/
|
|
672
|
-
setAdaptiveSampling( enabled ) {
|
|
673
|
-
|
|
674
|
-
this._settings?.set( 'useAdaptiveSampling', enabled );
|
|
675
|
-
this.setAdaptiveSamplingEnabled( enabled );
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
|
|
679
651
|
/**
|
|
680
652
|
* Switches strategy with automatic reset (convenience wrapper).
|
|
681
653
|
* @param {'none'|'asvgf'|'ssrc'|'edgeaware'} strategy
|
|
@@ -702,7 +674,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
702
674
|
|
|
703
675
|
}
|
|
704
676
|
|
|
705
|
-
|
|
706
677
|
_getEffectiveExposure() {
|
|
707
678
|
|
|
708
679
|
return this._stages.autoExposure?.enabled
|
|
@@ -717,12 +688,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
717
688
|
|
|
718
689
|
}
|
|
719
690
|
|
|
720
|
-
_isAdaptiveSamplingActive() {
|
|
721
|
-
|
|
722
|
-
return this._stages.adaptiveSampling?.enabled ?? false;
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
|
|
726
691
|
_clearDenoiserTextures() {
|
|
727
692
|
|
|
728
693
|
const ctx = this.pipeline?.context;
|
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* is mutated to preserve TSL shader graph references after compilation.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
11
|
-
import { storage } from 'three/tsl';
|
|
12
10
|
import {
|
|
13
|
-
RGBAFormat, FloatType, Vector2, Vector3, Color, Matrix4, DataTexture,
|
|
11
|
+
RGBAFormat, RedFormat, FloatType, Vector2, Vector3, Color, Matrix4, DataTexture,
|
|
14
12
|
} from 'three';
|
|
15
13
|
import { EquirectHDRInfo } from '../Processor/EquirectHDRInfo.js';
|
|
16
14
|
import { ProceduralSky } from '../Processor/ProceduralSky.js';
|
|
@@ -43,12 +41,10 @@ export class EnvironmentManager {
|
|
|
43
41
|
this.environmentTexture = this._envPlaceholder;
|
|
44
42
|
this.envTexSize = new Vector2();
|
|
45
43
|
|
|
46
|
-
// CDF
|
|
47
|
-
// Layout:
|
|
48
|
-
|
|
49
|
-
this.
|
|
50
|
-
this.envCDFStorageNode = null;
|
|
51
|
-
this._initCDFStorageBuffers();
|
|
44
|
+
// Importance-sampling CDF as an R32F texture (frees a Shade storage-buffer binding — the limit is 10).
|
|
45
|
+
// Layout: (envResolution.x + 1) × envResolution.y; conditional in columns [0,W), marginal in column W.
|
|
46
|
+
this.envCDFTexture = null;
|
|
47
|
+
this._initCDFTexture();
|
|
52
48
|
|
|
53
49
|
// Environment rotation
|
|
54
50
|
this.environmentRotationMatrix = new Matrix4();
|
|
@@ -183,46 +179,42 @@ export class EnvironmentManager {
|
|
|
183
179
|
* Initialize the packed CDF storage buffer with placeholder data.
|
|
184
180
|
* Must be called before shader compilation so the node exists in the graph.
|
|
185
181
|
*
|
|
186
|
-
*
|
|
187
|
-
* Placeholder shape is a 1x2 env map: marginal=[0,1], conditional=[0,0,1,1].
|
|
182
|
+
* 1×1 R32F placeholder until a real env CDF is built (env IS is off meanwhile).
|
|
188
183
|
* @private
|
|
189
184
|
*/
|
|
190
|
-
|
|
185
|
+
_initCDFTexture() {
|
|
191
186
|
|
|
192
|
-
|
|
193
|
-
this.
|
|
194
|
-
this.envCDFStorageNode = storage( this.envCDFStorageAttr, 'float', placeholder.length ).toReadOnly();
|
|
187
|
+
this.envCDFTexture = new DataTexture( new Float32Array( [ 0 ] ), 1, 1, RedFormat, FloatType );
|
|
188
|
+
this.envCDFTexture.needsUpdate = true;
|
|
195
189
|
|
|
196
190
|
}
|
|
197
191
|
|
|
198
192
|
/**
|
|
199
|
-
*
|
|
200
|
-
*
|
|
193
|
+
* Rebuild the CDF texture from equirectHdrInfo. Packs the 2D conditional + 1D marginal into one
|
|
194
|
+
* R32F texture of (W+1)×H: conditional[cy*W+cx] at texel (cx, cy); marginal[cy] at texel (W, cy).
|
|
201
195
|
* @private
|
|
202
196
|
*/
|
|
203
|
-
|
|
197
|
+
_updateCDFTexture() {
|
|
204
198
|
|
|
205
199
|
const marginal = this.equirectHdrInfo.marginalData;
|
|
206
200
|
const conditional = this.equirectHdrInfo.conditionalData;
|
|
207
201
|
if ( ! marginal || ! conditional ) return;
|
|
208
202
|
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
203
|
+
const W = this.equirectHdrInfo.width;
|
|
204
|
+
const H = this.equirectHdrInfo.height;
|
|
205
|
+
const texW = W + 1;
|
|
206
|
+
const data = new Float32Array( texW * H );
|
|
207
|
+
for ( let cy = 0; cy < H; cy ++ ) {
|
|
212
208
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
const dstRow = cy * texW;
|
|
210
|
+
data.set( conditional.subarray( cy * W, cy * W + W ), dstRow ); // conditional → columns [0,W)
|
|
211
|
+
data[ dstRow + W ] = marginal[ cy ]; // marginal → column W
|
|
216
212
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get the packed CDF storage node for shader graph.
|
|
221
|
-
* @returns {{ cdfNode: StorageNode }}
|
|
222
|
-
*/
|
|
223
|
-
getCDFStorageNodes() {
|
|
213
|
+
}
|
|
224
214
|
|
|
225
|
-
|
|
215
|
+
this.envCDFTexture?.dispose?.();
|
|
216
|
+
this.envCDFTexture = new DataTexture( data, texW, H, RedFormat, FloatType );
|
|
217
|
+
this.envCDFTexture.needsUpdate = true;
|
|
226
218
|
|
|
227
219
|
}
|
|
228
220
|
|
|
@@ -278,7 +270,7 @@ export class EnvironmentManager {
|
|
|
278
270
|
|
|
279
271
|
if ( ! this.scene.environment ) {
|
|
280
272
|
|
|
281
|
-
this.
|
|
273
|
+
this._updateCDFTexture();
|
|
282
274
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
283
275
|
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
284
276
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
@@ -293,7 +285,7 @@ export class EnvironmentManager {
|
|
|
293
285
|
|
|
294
286
|
if ( ! textureForCDF.image ) {
|
|
295
287
|
|
|
296
|
-
this.
|
|
288
|
+
this._updateCDFTexture();
|
|
297
289
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
298
290
|
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
299
291
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
@@ -313,7 +305,7 @@ export class EnvironmentManager {
|
|
|
313
305
|
|
|
314
306
|
this.cdfBuildTime = performance.now() - startTime;
|
|
315
307
|
|
|
316
|
-
this.
|
|
308
|
+
this._updateCDFTexture();
|
|
317
309
|
this.uniforms.set( 'envTotalSum', this.equirectHdrInfo.totalSum );
|
|
318
310
|
this.uniforms.set( 'envCompensationDelta', this.equirectHdrInfo.compensationDelta );
|
|
319
311
|
this.uniforms.set( 'useEnvMapIS', 1 );
|
|
@@ -377,7 +369,7 @@ export class EnvironmentManager {
|
|
|
377
369
|
|
|
378
370
|
} else {
|
|
379
371
|
|
|
380
|
-
this.
|
|
372
|
+
this._updateCDFTexture();
|
|
381
373
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
382
374
|
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
383
375
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
@@ -543,9 +535,8 @@ export class EnvironmentManager {
|
|
|
543
535
|
this.proceduralSkyRenderer = null;
|
|
544
536
|
this.simpleSkyRenderer = null;
|
|
545
537
|
|
|
546
|
-
this.
|
|
547
|
-
this.
|
|
548
|
-
this.envCDFStorageNode = null;
|
|
538
|
+
this.envCDFTexture?.dispose?.();
|
|
539
|
+
this.envCDFTexture = null;
|
|
549
540
|
|
|
550
541
|
// Dispose the HDRI environment texture unless it's the shared placeholder
|
|
551
542
|
// (the placeholder is handled separately just below).
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { TileHelper } from './helpers/TileHelper.js';
|
|
2
2
|
import { OutlineHelper } from './helpers/OutlineHelper.js';
|
|
3
|
-
import { EngineEvents } from '../EngineEvents.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* OverlayManager — Unified overlay system for visual helpers.
|
|
@@ -9,7 +8,7 @@ import { EngineEvents } from '../EngineEvents.js';
|
|
|
9
8
|
* 1. **HelperScene** — A Three.js Scene rendered on top of the WebGPU backbuffer
|
|
10
9
|
* (light gizmos, bounding boxes, outlines). Renders at display resolution.
|
|
11
10
|
* 2. **HUDCanvas** — A 2D `<canvas>` element overlaid via CSS for screen-space
|
|
12
|
-
* elements (
|
|
11
|
+
* elements (AF points, debug labels). Completely separate
|
|
13
12
|
* from the WebGPU canvas, so it is never captured in saved images.
|
|
14
13
|
*
|
|
15
14
|
* Helpers are registered by name and implement a simple interface:
|
|
@@ -17,8 +16,8 @@ import { EngineEvents } from '../EngineEvents.js';
|
|
|
17
16
|
*
|
|
18
17
|
* @example
|
|
19
18
|
* const overlay = new OverlayManager( renderer, camera );
|
|
20
|
-
* overlay.register( '
|
|
21
|
-
* overlay.show( '
|
|
19
|
+
* overlay.register( 'outline', new OutlineHelper() );
|
|
20
|
+
* overlay.show( 'outline' );
|
|
22
21
|
* // in animate():
|
|
23
22
|
* overlay.render();
|
|
24
23
|
*/
|
|
@@ -84,7 +83,7 @@ export class OverlayManager {
|
|
|
84
83
|
// ═══════════════════════════════════════════════════════════════
|
|
85
84
|
|
|
86
85
|
/**
|
|
87
|
-
* Creates and wires the default overlay helpers (
|
|
86
|
+
* Creates and wires the default overlay helpers (denoise/upscale progress, outline).
|
|
88
87
|
* Call once during app init after pipeline and managers are ready.
|
|
89
88
|
*
|
|
90
89
|
* @param {Object} config
|
|
@@ -92,7 +91,7 @@ export class OverlayManager {
|
|
|
92
91
|
* @param {import('three').Scene} config.meshScene
|
|
93
92
|
* @param {import('../Pipeline/RenderPipeline.js').RenderPipeline} config.pipeline
|
|
94
93
|
* @param {import('./DenoisingManager.js').DenoisingManager} config.denoisingManager
|
|
95
|
-
* @param {import('three').EventDispatcher} config.app - App instance for resize
|
|
94
|
+
* @param {import('three').EventDispatcher} config.app - App instance for resize events
|
|
96
95
|
* @param {number} config.renderWidth
|
|
97
96
|
* @param {number} config.renderHeight
|
|
98
97
|
*/
|
|
@@ -100,7 +99,7 @@ export class OverlayManager {
|
|
|
100
99
|
|
|
101
100
|
this.setHelperScene( helperScene );
|
|
102
101
|
|
|
103
|
-
// ── Tile helper
|
|
102
|
+
// ── Tile helper — shows OIDN denoise + AI upscale progress ──
|
|
104
103
|
const tileHelper = new TileHelper();
|
|
105
104
|
this.register( 'tiles', tileHelper );
|
|
106
105
|
|
|
@@ -112,22 +111,8 @@ export class OverlayManager {
|
|
|
112
111
|
|
|
113
112
|
} );
|
|
114
113
|
|
|
115
|
-
// Path tracer tile events
|
|
116
|
-
pipeline.eventBus.on( 'tile:changed', ( e ) => {
|
|
117
|
-
|
|
118
|
-
if ( e.renderMode === 1 && e.tileBounds ) {
|
|
119
|
-
|
|
120
|
-
tileHelper.setActiveTile( e.tileBounds );
|
|
121
|
-
tileHelper.show();
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
} );
|
|
126
|
-
|
|
127
114
|
pipeline.eventBus.on( 'pipeline:reset', () => tileHelper.hide() );
|
|
128
|
-
app.addEventListener( EngineEvents.RENDER_COMPLETE, () => tileHelper.hide() );
|
|
129
115
|
|
|
130
|
-
// OIDN/upscaler tile events
|
|
131
116
|
this._wireDenoiserTileEvents( tileHelper, denoisingManager );
|
|
132
117
|
|
|
133
118
|
// ── Outline helper ──
|
|
@@ -137,7 +122,7 @@ export class OverlayManager {
|
|
|
137
122
|
}
|
|
138
123
|
|
|
139
124
|
/**
|
|
140
|
-
* Wires denoiser/upscaler tile
|
|
125
|
+
* Wires denoiser/upscaler tile-progress events to the tile helper.
|
|
141
126
|
* These fire while the animation loop is stopped, so we trigger manual HUD redraws.
|
|
142
127
|
*/
|
|
143
128
|
_wireDenoiserTileEvents( tileHelper, denoisingManager ) {
|
|
@@ -231,9 +231,6 @@ export class UniformManager {
|
|
|
231
231
|
this._uniforms.set( 'samplingTechnique', samplingTechniqueUniform );
|
|
232
232
|
samplingTechniqueUniform.value = DEFAULT_STATE.samplingTechnique;
|
|
233
233
|
|
|
234
|
-
ub( 'useAdaptiveSampling', DEFAULT_STATE.adaptiveSampling );
|
|
235
|
-
u( 'adaptiveSamplingMin', DEFAULT_STATE.adaptiveSamplingMin ?? 1, 'int' );
|
|
236
|
-
u( 'adaptiveSamplingMax', DEFAULT_STATE.adaptiveSamplingMax, 'int' );
|
|
237
234
|
u( 'fireflyThreshold', DEFAULT_STATE.fireflyThreshold, 'float' );
|
|
238
235
|
|
|
239
236
|
// Emissive
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TileHelper — 2D canvas overlay showing the active tile border.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Shows progress of the OIDN denoiser and AI upscaler (both process the
|
|
5
|
+
* image in tiles). Callers provide pixel-space tile bounds via
|
|
6
6
|
* `setActiveTile()` and control visibility via `show()` / `hide()`.
|
|
7
7
|
*
|
|
8
8
|
* Layer: 'hud' (rendered by OverlayManager's 2D canvas pass).
|