rayzee 7.10.1 → 7.10.3
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 +2 -3
- package/dist/rayzee.es.js +1823 -1948
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +33 -45
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +9 -8
- package/src/PathTracerApp.js +0 -4
- package/src/Processor/TextureCreator.js +16 -1
- package/src/Stages/Compositor.js +0 -1
- package/src/Stages/EdgeFilter.js +284 -123
- package/src/TSL/Common.js +0 -17
- package/src/managers/DenoisingManager.js +14 -23
- package/src/Stages/SSRC.js +0 -328
- package/src/TSL/SSRC.js +0 -266
|
@@ -6,7 +6,7 @@ import { ENGINE_DEFAULTS as DEFAULT_STATE, ASVGF_QUALITY_PRESETS } from '../Engi
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Orchestrates all denoising, post-processing, and AI upscaling:
|
|
9
|
-
* - Real-time denoiser strategy switching (ASVGF /
|
|
9
|
+
* - Real-time denoiser strategy switching (ASVGF / EdgeAware / None)
|
|
10
10
|
* - OIDN (offline denoise on render completion)
|
|
11
11
|
* - AI Upscaler
|
|
12
12
|
* - Auto-exposure coordination
|
|
@@ -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, edgeFilter,
|
|
44
|
+
this._stages = stages; // { pathTracer, asvgf, variance, bilateralFilter, edgeFilter, autoExposure, compositor }
|
|
45
45
|
|
|
46
46
|
this._getExposure = getExposure;
|
|
47
47
|
this._getSaturation = getSaturation;
|
|
@@ -222,7 +222,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
224
|
* Switches the real-time denoiser strategy.
|
|
225
|
-
* @param {string} strategy - 'none' | 'asvgf' | '
|
|
225
|
+
* @param {string} strategy - 'none' | 'asvgf' | 'edgeaware'
|
|
226
226
|
* @param {string} [asvgfPreset] - ASVGF quality preset when strategy is 'asvgf'
|
|
227
227
|
*/
|
|
228
228
|
setDenoiserStrategy( strategy, asvgfPreset ) {
|
|
@@ -234,7 +234,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
234
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
|
-
if ( s.ssrc ) s.ssrc.enabled = false;
|
|
238
237
|
|
|
239
238
|
this._clearDenoiserTextures();
|
|
240
239
|
|
|
@@ -248,11 +247,10 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
248
247
|
this._applyASVGFPreset( asvgfPreset || 'medium' );
|
|
249
248
|
break;
|
|
250
249
|
|
|
251
|
-
case 'ssrc':
|
|
252
|
-
if ( s.ssrc ) s.ssrc.enabled = true;
|
|
253
|
-
break;
|
|
254
|
-
|
|
255
250
|
case 'edgeaware':
|
|
251
|
+
// EdgeAware is a spatial-only SVGF à-trous — it consumes the Variance
|
|
252
|
+
// stage's per-pixel variance to drive its luminance edge-stop.
|
|
253
|
+
if ( s.variance ) s.variance.enabled = true;
|
|
256
254
|
if ( s.edgeFilter ) s.edgeFilter.setFilteringEnabled( true );
|
|
257
255
|
break;
|
|
258
256
|
|
|
@@ -325,7 +323,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
325
323
|
* navigation and frees their textures. Call after any consumer toggle.
|
|
326
324
|
*
|
|
327
325
|
* MotionVector requires NormalDepth (reads pathtracer:normalDepth) and its
|
|
328
|
-
* consumers (ASVGF
|
|
326
|
+
* consumers (ASVGF) are a subset of NormalDepth's, so NormalDepth is
|
|
329
327
|
* always enabled whenever MotionVector is. Adaptive sampling / Variance / OIDN
|
|
330
328
|
* do NOT read these signals, so they don't keep the G-buffer alive.
|
|
331
329
|
*/
|
|
@@ -335,9 +333,9 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
335
333
|
const nd = s.normalDepth;
|
|
336
334
|
const mv = s.motionVector;
|
|
337
335
|
|
|
338
|
-
// motionVector:* consumed by ASVGF
|
|
339
|
-
const motionNeeded = !! ( s.asvgf?.enabled
|
|
340
|
-
// pathtracer:normalDepth consumed by ASVGF,
|
|
336
|
+
// motionVector:* consumed by ASVGF
|
|
337
|
+
const motionNeeded = !! ( s.asvgf?.enabled );
|
|
338
|
+
// pathtracer:normalDepth consumed by ASVGF, EdgeFilter, BilateralFilter
|
|
341
339
|
const normalNeeded = motionNeeded || !! ( s.edgeFilter?.enabled || s.bilateralFilter?.enabled );
|
|
342
340
|
|
|
343
341
|
if ( nd ) {
|
|
@@ -366,7 +364,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
366
364
|
}
|
|
367
365
|
|
|
368
366
|
// PathTracer's aux MRT (normalDepth + albedo) is consumed by the real-time denoisers (ASVGF/
|
|
369
|
-
// BilateralFilter read albedo; ASVGF/
|
|
367
|
+
// BilateralFilter read albedo; ASVGF/EdgeFilter read normalDepth) and by OIDN (reads the
|
|
370
368
|
// MRT read-targets directly). When none are active the wavefront skips those writes entirely.
|
|
371
369
|
s.pathTracer?.setAuxGBufferEnabled?.( normalNeeded || !! this.denoiser?.enabled );
|
|
372
370
|
|
|
@@ -374,7 +372,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
374
372
|
// disabled (lazily re-created on the next dispatch after re-enable). Every strategy/denoiser
|
|
375
373
|
// toggle funnels through here after the enabled flags above are settled, so this is the one
|
|
376
374
|
// choke point. dispose() is idempotent, so re-running it for an already-released stage is a no-op.
|
|
377
|
-
for ( const stage of [ s.asvgf, s.variance, s.bilateralFilter, s.
|
|
375
|
+
for ( const stage of [ s.asvgf, s.variance, s.bilateralFilter, s.edgeFilter, nd, mv ] ) {
|
|
378
376
|
|
|
379
377
|
if ( stage && ! stage.enabled ) stage.releaseGPUMemory?.();
|
|
380
378
|
|
|
@@ -583,13 +581,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
583
581
|
|
|
584
582
|
}
|
|
585
583
|
|
|
586
|
-
/** Updates SSRC stage parameters. */
|
|
587
|
-
setSSRCParams( params ) {
|
|
588
|
-
|
|
589
|
-
this._stages.ssrc?.updateParameters( params );
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
|
|
593
584
|
/** Updates edge-aware filtering parameters. */
|
|
594
585
|
setEdgeAwareParams( params ) {
|
|
595
586
|
|
|
@@ -667,7 +658,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
667
658
|
|
|
668
659
|
/**
|
|
669
660
|
* Switches strategy with automatic reset (convenience wrapper).
|
|
670
|
-
* @param {'none'|'asvgf'|'
|
|
661
|
+
* @param {'none'|'asvgf'|'edgeaware'} strategy
|
|
671
662
|
* @param {string} [asvgfPreset]
|
|
672
663
|
*/
|
|
673
664
|
setStrategy( strategy, asvgfPreset ) {
|
|
@@ -713,7 +704,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
713
704
|
const keys = [
|
|
714
705
|
'asvgf:output', 'asvgf:demodulated', 'asvgf:gradient',
|
|
715
706
|
'variance:output', 'bilateralFiltering:output',
|
|
716
|
-
'edgeFiltering:output',
|
|
707
|
+
'edgeFiltering:output',
|
|
717
708
|
];
|
|
718
709
|
keys.forEach( k => ctx.removeTexture( k ) );
|
|
719
710
|
|
package/src/Stages/SSRC.js
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
// Screen-Space Radiance Cache (SSRC) Stage
|
|
2
|
-
//
|
|
3
|
-
// Two compute passes per frame:
|
|
4
|
-
// Pass 1 (Temporal): Reproject previous cache via motion vectors + EMA blend
|
|
5
|
-
// Pass 2 (Spatial): 8-tap neighbor reuse weighted by normal/depth similarity
|
|
6
|
-
//
|
|
7
|
-
// Execution: PER_CYCLE
|
|
8
|
-
//
|
|
9
|
-
// Events listened: pipeline:reset, camera:moved
|
|
10
|
-
//
|
|
11
|
-
// Textures published: ssrc:output
|
|
12
|
-
// Textures read: pathtracer:color, pathtracer:normalDepth, motionVector:screenSpace
|
|
13
|
-
|
|
14
|
-
import { uniform } from 'three/tsl';
|
|
15
|
-
import { StorageTexture, TextureNode, RenderTarget } from 'three/webgpu';
|
|
16
|
-
import { HalfFloatType, RGBAFormat, NearestFilter, LinearFilter, Box2, Vector2 } from 'three';
|
|
17
|
-
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
18
|
-
import { buildTemporalPass, buildSpatialPass } from '../TSL/SSRC.js';
|
|
19
|
-
import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
20
|
-
|
|
21
|
-
export class SSRC extends RenderStage {
|
|
22
|
-
|
|
23
|
-
constructor( renderer, options = {} ) {
|
|
24
|
-
|
|
25
|
-
super( 'SSRC', {
|
|
26
|
-
...options,
|
|
27
|
-
executionMode: StageExecutionMode.PER_CYCLE,
|
|
28
|
-
} );
|
|
29
|
-
|
|
30
|
-
this.renderer = renderer;
|
|
31
|
-
|
|
32
|
-
// ─── Uniforms ───
|
|
33
|
-
this.resW = uniform( 1 );
|
|
34
|
-
this.resH = uniform( 1 );
|
|
35
|
-
this.temporalAlpha = uniform( options.temporalAlpha ?? 0.1 );
|
|
36
|
-
this.phiNormal = uniform( options.phiNormal ?? 128.0 );
|
|
37
|
-
this.phiDepth = uniform( options.phiDepth ?? 0.5 );
|
|
38
|
-
this.maxHistory = uniform( options.maxHistory ?? 128.0 );
|
|
39
|
-
this.spatialRadius = uniform( options.spatialRadius ?? 4, 'int' );
|
|
40
|
-
this.spatialWeight = uniform( options.spatialWeight ?? 0.4 );
|
|
41
|
-
// 0 on reset → temporal pass skips cache; incremented each render frame
|
|
42
|
-
this._framesSinceReset = uniform( 0, 'int' );
|
|
43
|
-
|
|
44
|
-
// ─── Input TextureNodes (set from pipeline context each frame) ───
|
|
45
|
-
this._colorTexNode = new TextureNode();
|
|
46
|
-
this._ndTexNode = new TextureNode();
|
|
47
|
-
this._motionTexNode = new TextureNode();
|
|
48
|
-
|
|
49
|
-
// ─── Read-side wrappers for ping-pong StorageTextures ───
|
|
50
|
-
this._readCacheTexNode = new TextureNode(); // prev cache (for temporal pass)
|
|
51
|
-
this._readPrevNDTexNode = new TextureNode(); // prev normalDepth (for edge-stopping)
|
|
52
|
-
this._readPass1CacheTexNode = new TextureNode(); // current cache (for spatial pass)
|
|
53
|
-
|
|
54
|
-
// ─── StorageTextures (5 total) ───
|
|
55
|
-
// StorageTextures stay at max alloc — see resize crash fix (three.js #33061).
|
|
56
|
-
const w = 1, h = 1; // RTs/uniforms resized on first render
|
|
57
|
-
|
|
58
|
-
// Ping-pong temporal cache: .rgb = radiance, .w = history count
|
|
59
|
-
this._cacheTexA = this._createStorageTex( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE, NearestFilter );
|
|
60
|
-
this._cacheTexB = this._createStorageTex( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE, NearestFilter );
|
|
61
|
-
|
|
62
|
-
// Ping-pong previous-frame normalDepth (for edge-stopping in temporal pass)
|
|
63
|
-
this._prevNDTexA = this._createStorageTex( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE, NearestFilter );
|
|
64
|
-
this._prevNDTexB = this._createStorageTex( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE, NearestFilter );
|
|
65
|
-
|
|
66
|
-
// Final output (LinearFilter for Display fragment shader sampling)
|
|
67
|
-
this._outputTex = this._createStorageTex( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE, LinearFilter );
|
|
68
|
-
|
|
69
|
-
// Active-region copy target — published downstream (storage tex is over-allocated)
|
|
70
|
-
this._srcRegion = new Box2( new Vector2( 0, 0 ), new Vector2( 0, 0 ) );
|
|
71
|
-
this.outputTarget = new RenderTarget( w, h, {
|
|
72
|
-
type: HalfFloatType,
|
|
73
|
-
format: RGBAFormat,
|
|
74
|
-
minFilter: LinearFilter,
|
|
75
|
-
magFilter: LinearFilter,
|
|
76
|
-
depthBuffer: false,
|
|
77
|
-
stencilBuffer: false
|
|
78
|
-
} );
|
|
79
|
-
|
|
80
|
-
// ─── State ───
|
|
81
|
-
this._currentPingPong = 0; // 0: read B, write A; 1: read A, write B
|
|
82
|
-
this._dispatchX = 1;
|
|
83
|
-
this._dispatchY = 1;
|
|
84
|
-
|
|
85
|
-
// ─── Compute nodes ───
|
|
86
|
-
this._buildComputeNodes();
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ──────────────────────────────────────────────────
|
|
91
|
-
// Lifecycle
|
|
92
|
-
// ──────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
setupEventListeners() {
|
|
95
|
-
|
|
96
|
-
this.on( 'pipeline:reset', () => this._resetCache() );
|
|
97
|
-
this.on( 'camera:moved', () => this._resetCache() );
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
render( context ) {
|
|
102
|
-
|
|
103
|
-
if ( ! this.enabled ) {
|
|
104
|
-
|
|
105
|
-
context.removeTexture( 'ssrc:output' );
|
|
106
|
-
return;
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Auto-resize if render resolution changed
|
|
111
|
-
const colorTex = context.getTexture( 'pathtracer:color' );
|
|
112
|
-
if ( colorTex?.image ) {
|
|
113
|
-
|
|
114
|
-
const { width, height } = colorTex.image;
|
|
115
|
-
if ( width !== this.outputTarget.width || height !== this.outputTarget.height ) {
|
|
116
|
-
|
|
117
|
-
this.setSize( width, height );
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Bind current-frame inputs from context
|
|
124
|
-
const normalDepthTex = context.getTexture( 'pathtracer:normalDepth' );
|
|
125
|
-
if ( ! normalDepthTex || ! colorTex ) return;
|
|
126
|
-
|
|
127
|
-
this._colorTexNode.value = colorTex;
|
|
128
|
-
this._ndTexNode.value = normalDepthTex;
|
|
129
|
-
|
|
130
|
-
const motionTex = context.getTexture( 'motionVector:screenSpace' );
|
|
131
|
-
if ( motionTex ) this._motionTexNode.value = motionTex;
|
|
132
|
-
|
|
133
|
-
// ─── Ping-pong assignment ───
|
|
134
|
-
// _currentPingPong 0: pass1 reads B, writes A; pass2 reads A
|
|
135
|
-
// _currentPingPong 1: pass1 reads A, writes B; pass2 reads B
|
|
136
|
-
const [ readCache, writeCache, readPrevND ] =
|
|
137
|
-
this._currentPingPong === 0
|
|
138
|
-
? [ this._cacheTexB, this._cacheTexA, this._prevNDTexB ]
|
|
139
|
-
: [ this._cacheTexA, this._cacheTexB, this._prevNDTexA ];
|
|
140
|
-
|
|
141
|
-
// ─── Pass 1: Temporal ───
|
|
142
|
-
this._readCacheTexNode.value = readCache;
|
|
143
|
-
this._readPrevNDTexNode.value = readPrevND;
|
|
144
|
-
|
|
145
|
-
// patch the write-side storages by recreating nodes is NOT needed — the pass was built
|
|
146
|
-
// with the actual StorageTexture references (not TextureNode wrappers), so we swap them
|
|
147
|
-
// via the ping-pong writeCacheTex / writePrevNDTex references in the closure.
|
|
148
|
-
// Since TSL captures StorageTexture directly for writes, we must rebuild or use a flag.
|
|
149
|
-
// Instead we use the swappable-write pattern: build two pass1 nodes (one per write target).
|
|
150
|
-
this.renderer.compute( this._currentPingPong === 0 ? this._pass1NodeA : this._pass1NodeB );
|
|
151
|
-
|
|
152
|
-
// ─── Pass 2: Spatial ───
|
|
153
|
-
// Spatial pass reads the just-written cache from pass 1
|
|
154
|
-
this._readPass1CacheTexNode.value = writeCache;
|
|
155
|
-
|
|
156
|
-
this.renderer.compute( this._pass2Node );
|
|
157
|
-
|
|
158
|
-
// Advance frames-since-reset counter (capped to avoid overflow)
|
|
159
|
-
this._framesSinceReset.value = Math.min( this._framesSinceReset.value + 1, 9999 );
|
|
160
|
-
|
|
161
|
-
// Copy active region out of the over-allocated StorageTexture into the
|
|
162
|
-
// right-sized RenderTarget; downstream stages UV-sample the latter.
|
|
163
|
-
this._srcRegion.max.set( this.outputTarget.width, this.outputTarget.height );
|
|
164
|
-
this.renderer.copyTextureToTexture( this._outputTex, this.outputTarget.texture, this._srcRegion );
|
|
165
|
-
|
|
166
|
-
// Publish final output
|
|
167
|
-
context.setTexture( 'ssrc:output', this.outputTarget.texture );
|
|
168
|
-
|
|
169
|
-
// Advance ping-pong
|
|
170
|
-
this._currentPingPong = 1 - this._currentPingPong;
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Free the 2048² StorageTextures when disabled; three.js re-creates them on the next dispatch
|
|
175
|
-
// after re-enable, and reset() clears the temporal cache. See ASVGF.releaseGPUMemory.
|
|
176
|
-
releaseGPUMemory() {
|
|
177
|
-
|
|
178
|
-
this._cacheTexA?.dispose();
|
|
179
|
-
this._cacheTexB?.dispose();
|
|
180
|
-
this._prevNDTexA?.dispose();
|
|
181
|
-
this._prevNDTexB?.dispose();
|
|
182
|
-
this._outputTex?.dispose();
|
|
183
|
-
// Render-res RT texture (dispose .texture, not the RT — RT.dispose() doesn't free it here).
|
|
184
|
-
this.context?.removeTexture( 'ssrc:output' );
|
|
185
|
-
this.outputTarget?.texture?.dispose();
|
|
186
|
-
this.reset();
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
reset() {
|
|
191
|
-
|
|
192
|
-
this._resetCache();
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
setSize( width, height ) {
|
|
197
|
-
|
|
198
|
-
if ( width < 1 || height < 1 ) return;
|
|
199
|
-
|
|
200
|
-
// StorageTextures stay at their max allocation (see constructor).
|
|
201
|
-
this.outputTarget.setSize( width, height );
|
|
202
|
-
this.outputTarget.texture.needsUpdate = true;
|
|
203
|
-
|
|
204
|
-
this.resW.value = width;
|
|
205
|
-
this.resH.value = height;
|
|
206
|
-
|
|
207
|
-
this._dispatchX = Math.ceil( width / 8 );
|
|
208
|
-
this._dispatchY = Math.ceil( height / 8 );
|
|
209
|
-
|
|
210
|
-
const dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
211
|
-
if ( this._pass1NodeA ) this._pass1NodeA.dispatchSize = dispatchSize;
|
|
212
|
-
if ( this._pass1NodeB ) this._pass1NodeB.dispatchSize = dispatchSize;
|
|
213
|
-
if ( this._pass2Node ) this._pass2Node.dispatchSize = dispatchSize;
|
|
214
|
-
|
|
215
|
-
this._resetCache();
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
dispose() {
|
|
220
|
-
|
|
221
|
-
this._pass1NodeA?.dispose();
|
|
222
|
-
this._pass1NodeB?.dispose();
|
|
223
|
-
this._pass2Node?.dispose();
|
|
224
|
-
this._cacheTexA.dispose();
|
|
225
|
-
this._cacheTexB.dispose();
|
|
226
|
-
this._prevNDTexA.dispose();
|
|
227
|
-
this._prevNDTexB.dispose();
|
|
228
|
-
this._outputTex.dispose();
|
|
229
|
-
this.outputTarget?.dispose();
|
|
230
|
-
this._colorTexNode?.dispose();
|
|
231
|
-
this._ndTexNode?.dispose();
|
|
232
|
-
this._motionTexNode?.dispose();
|
|
233
|
-
this._readCacheTexNode?.dispose();
|
|
234
|
-
this._readPrevNDTexNode?.dispose();
|
|
235
|
-
this._readPass1CacheTexNode?.dispose();
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
updateParameters( params ) {
|
|
240
|
-
|
|
241
|
-
if ( params.temporalAlpha !== undefined ) this.temporalAlpha.value = params.temporalAlpha;
|
|
242
|
-
if ( params.phiNormal !== undefined ) this.phiNormal.value = params.phiNormal;
|
|
243
|
-
if ( params.phiDepth !== undefined ) this.phiDepth.value = params.phiDepth;
|
|
244
|
-
if ( params.maxHistory !== undefined ) this.maxHistory.value = params.maxHistory;
|
|
245
|
-
if ( params.spatialRadius !== undefined ) this.spatialRadius.value = params.spatialRadius;
|
|
246
|
-
if ( params.spatialWeight !== undefined ) this.spatialWeight.value = params.spatialWeight;
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ──────────────────────────────────────────────────
|
|
251
|
-
// Private
|
|
252
|
-
// ──────────────────────────────────────────────────
|
|
253
|
-
|
|
254
|
-
_createStorageTex( w, h, filter ) {
|
|
255
|
-
|
|
256
|
-
const tex = new StorageTexture( w, h );
|
|
257
|
-
tex.type = HalfFloatType;
|
|
258
|
-
tex.format = RGBAFormat;
|
|
259
|
-
tex.minFilter = filter;
|
|
260
|
-
tex.magFilter = filter;
|
|
261
|
-
return tex;
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
_resetCache() {
|
|
266
|
-
|
|
267
|
-
this._currentPingPong = 0;
|
|
268
|
-
this._framesSinceReset.value = 0;
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
_buildComputeNodes() {
|
|
273
|
-
|
|
274
|
-
const commonArgs = {
|
|
275
|
-
colorTexNode: this._colorTexNode,
|
|
276
|
-
ndTexNode: this._ndTexNode,
|
|
277
|
-
motionTexNode: this._motionTexNode,
|
|
278
|
-
readCacheTexNode: this._readCacheTexNode,
|
|
279
|
-
readPrevNDTexNode: this._readPrevNDTexNode,
|
|
280
|
-
resW: this.resW,
|
|
281
|
-
resH: this.resH,
|
|
282
|
-
temporalAlpha: this.temporalAlpha,
|
|
283
|
-
phiNormal: this.phiNormal,
|
|
284
|
-
phiDepth: this.phiDepth,
|
|
285
|
-
maxHistory: this.maxHistory,
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
// Build two temporal nodes — one writing to cacheTexA, one to cacheTexB.
|
|
289
|
-
// This is required because StorageTexture write targets are fixed at compile time.
|
|
290
|
-
const pass1FnA = buildTemporalPass( {
|
|
291
|
-
...commonArgs,
|
|
292
|
-
writeCacheTex: this._cacheTexA,
|
|
293
|
-
writePrevNDTex: this._prevNDTexA,
|
|
294
|
-
framesSinceReset: this._framesSinceReset,
|
|
295
|
-
} );
|
|
296
|
-
|
|
297
|
-
const pass1FnB = buildTemporalPass( {
|
|
298
|
-
...commonArgs,
|
|
299
|
-
writeCacheTex: this._cacheTexB,
|
|
300
|
-
writePrevNDTex: this._prevNDTexB,
|
|
301
|
-
framesSinceReset: this._framesSinceReset,
|
|
302
|
-
} );
|
|
303
|
-
|
|
304
|
-
const dispatchCount = [ this._dispatchX, this._dispatchY, 1 ];
|
|
305
|
-
const wgSize = [ 8, 8, 1 ];
|
|
306
|
-
|
|
307
|
-
this._pass1NodeA = pass1FnA().compute( dispatchCount, wgSize );
|
|
308
|
-
this._pass1NodeB = pass1FnB().compute( dispatchCount, wgSize );
|
|
309
|
-
|
|
310
|
-
// Spatial pass: reads from _readPass1CacheTexNode (assigned per-frame to the just-written cache)
|
|
311
|
-
const pass2Fn = buildSpatialPass( {
|
|
312
|
-
colorTexNode: this._colorTexNode,
|
|
313
|
-
ndTexNode: this._ndTexNode,
|
|
314
|
-
readCacheTexNode: this._readPass1CacheTexNode,
|
|
315
|
-
outputTex: this._outputTex,
|
|
316
|
-
resW: this.resW,
|
|
317
|
-
resH: this.resH,
|
|
318
|
-
spatialRadius: this.spatialRadius,
|
|
319
|
-
spatialWeight: this.spatialWeight,
|
|
320
|
-
phiNormal: this.phiNormal,
|
|
321
|
-
phiDepth: this.phiDepth,
|
|
322
|
-
} );
|
|
323
|
-
|
|
324
|
-
this._pass2Node = pass2Fn().compute( dispatchCount, wgSize );
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
}
|