rayzee 11.6.0 → 11.8.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 +26 -14
- package/dist/{ArchiveImporter-iofnC8zB.js → ArchiveImporter-_sJm5uaK.js} +1923 -1243
- package/dist/{BidirectionalIntegrator-CqAilt2I.js → BidirectionalIntegrator-Dzt4Z97x.js} +290 -289
- package/dist/{ColorManagement-oeEk7oSu.js → ColorManagement-30MfpHwQ.js} +1 -1
- package/dist/{EngineIssues-CIZnXHsF.js → EngineIssues-xljoXM0E.js} +18 -8
- package/dist/{NeuralRetouch-DFI4pOjO.js → NeuralRetouch-BKQCsmEs.js} +2 -2
- package/dist/{NeuralSuperRes-ChCiiilS.js → NeuralSuperRes-C4ymq68e.js} +2 -2
- package/dist/{PathTracer-BX4sJLx7.js → PathTracer-C_to_i40.js} +3158 -3102
- package/dist/{PhysicalSky-Biv39eTA.js → PhysicalSky-bMHbm_7H.js} +1 -1
- package/dist/{RayzeeRenderer-D8XRKU3A.js → RayzeeRenderer-B_aB6VTJ.js} +370 -372
- package/dist/SceneMetadata-CSmlJW4c.js +183 -0
- package/dist/{ToneMapGPU-sAs8wdgI.js → ToneMapGPU-4GPwoG_c.js} +13 -6
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/color.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/addons/storage.es.js +1 -1
- package/dist/{openStorage-Fe9BoS1_.js → openStorage-B085D9FR.js} +1 -1
- package/dist/rayzee-core.es.js +5 -5
- package/dist/rayzee.es.js +917 -1624
- package/dist/rayzee.umd.js +113 -156
- package/dist/{sceneCachePolicy-W5PLbzuR.js → sceneCachePolicy-D_uBSbQF.js} +2 -2
- package/package.json +1 -1
- package/src/EngineIssues.js +20 -6
- package/src/Passes/AIUpscaler.js +4 -0
- package/src/Passes/OIDNDenoiser.js +17 -17
- package/src/PathTracerApp.js +16 -18
- package/src/Pipeline/BindingAudit.js +2 -3
- package/src/Pipeline/PipelineContext.js +0 -1
- package/src/Pipeline/RenderPipeline.js +1 -2
- package/src/Pipeline/RenderStage.js +1 -1
- package/src/Processor/ArchiveImporter.js +23 -6
- package/src/Processor/AssetLoader.js +15 -0
- package/src/Processor/LightSerializer.js +6 -2
- package/src/Processor/PBRT/PBRTMaterials.js +2 -1
- package/src/Processor/PBRT/PBRTSceneBuilder.js +1 -1
- package/src/Processor/PackedRayBuffer.js +2 -2
- package/src/Processor/PlatformImageLoader.js +9 -0
- package/src/Processor/StorageTexturePool.js +1 -1
- package/src/Processor/TextureCreator.js +44 -4
- package/src/Processor/ToneMapCPU.js +34 -5
- package/src/Processor/ToneMapGPU.js +15 -9
- package/src/Processor/USD/USDScene.js +981 -17
- package/src/RayzeeRenderer.js +82 -27
- package/src/Stages/AutoExposure.js +21 -8
- package/src/Stages/Compositor.js +39 -1
- package/src/Stages/DenoiserSettings.js +5 -81
- package/src/Stages/LocalExposure.js +44 -18
- package/src/Stages/MotionVector.js +21 -215
- package/src/Stages/NormalDepth.js +12 -8
- package/src/Stages/PathTracer.js +26 -2
- package/src/Stages/PathTracerStage.js +13 -18
- package/src/TSL/CameraRay.js +12 -1
- package/src/TSL/DebugKernel.js +3 -2
- package/src/TSL/FinalWriteKernel.js +6 -9
- package/src/TSL/GenerateKernel.js +6 -2
- package/src/TSL/LightSplatKernel.js +7 -4
- package/src/TSL/LightsCore.js +6 -2
- package/src/TSL/LightsSampling.js +45 -12
- package/src/TSL/SceneResources.js +11 -0
- package/src/TSL/ShadeKernel.js +4 -5
- package/src/index.js +1 -1
- package/src/integrators/BidirectionalIntegrator.js +1 -0
- package/src/managers/CameraManager.js +9 -0
- package/src/managers/DenoisingManager.js +79 -187
- package/src/managers/InteractionManager.js +6 -0
- package/src/managers/UniformManager.js +3 -1
- package/dist/SceneMetadata-D2Sh-0e-.js +0 -110
- package/src/Stages/ASVGF.js +0 -932
- package/src/Stages/BilateralFilter.js +0 -486
- package/src/Stages/EdgeFilter.js +0 -545
- package/src/Stages/Variance.js +0 -443
package/src/Stages/Variance.js
DELETED
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
import { Fn, wgslFn, float, int, uint, ivec2, uvec2, uniform, If, max, select,
|
|
2
|
-
textureLoad, textureStore, workgroupArray, workgroupBarrier, localId, workgroupId } from 'three/tsl';
|
|
3
|
-
import { RenderTarget, TextureNode, StorageTexture } from 'three/webgpu';
|
|
4
|
-
import { FloatType, RGBAFormat, LinearFilter, Box2, Vector2 } from 'three';
|
|
5
|
-
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
6
|
-
import { luminance } from '../TSL/Common.js';
|
|
7
|
-
import { MAX_STORAGE_TEXTURE_SIZE } from '../Processor/StorageTexturePool.js';
|
|
8
|
-
|
|
9
|
-
// NaN/±Inf guard: a poisoned luminance would otherwise corrupt the moment EMA forever
|
|
10
|
-
// (mean/meanSq → variance → BilateralFilter sigmaL). NaN (x!=x) → 0, ±Inf → [0,1e7].
|
|
11
|
-
const sanitizeLum = ( x ) => select( x.equal( x ), x, float( 0.0 ) ).clamp( 0.0, 1e7 );
|
|
12
|
-
|
|
13
|
-
// ── wgslFn helpers ──────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Temporal moment accumulation via exponential moving average.
|
|
17
|
-
*
|
|
18
|
-
* Returns vec4f(newMean, newMeanSq, temporalVariance * boost, spatialVariance * boost).
|
|
19
|
-
*/
|
|
20
|
-
const temporalAccumulate = /*@__PURE__*/ wgslFn( `
|
|
21
|
-
fn temporalAccumulate(
|
|
22
|
-
lum: f32,
|
|
23
|
-
prevMean: f32,
|
|
24
|
-
prevMeanSq: f32,
|
|
25
|
-
alpha: f32,
|
|
26
|
-
spatialVariance: f32,
|
|
27
|
-
varianceBoost: f32
|
|
28
|
-
) -> vec4f {
|
|
29
|
-
|
|
30
|
-
let newMean = prevMean + ( lum - prevMean ) * alpha;
|
|
31
|
-
let newMeanSq = prevMeanSq + ( lum * lum - prevMeanSq ) * alpha;
|
|
32
|
-
let temporalVariance = max( newMeanSq - newMean * newMean, 0.0 );
|
|
33
|
-
|
|
34
|
-
return vec4f(
|
|
35
|
-
newMean,
|
|
36
|
-
newMeanSq,
|
|
37
|
-
temporalVariance * varianceBoost,
|
|
38
|
-
spatialVariance * varianceBoost
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
` );
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* WebGPU Variance Estimation Stage (Compute Shader)
|
|
46
|
-
*
|
|
47
|
-
* Computes temporal and spatial variance from the path tracer output.
|
|
48
|
-
* Used by BilateralFilter for variance-guided filtering.
|
|
49
|
-
*
|
|
50
|
-
* Uses compute shader with workgroup shared memory for the 3×3
|
|
51
|
-
* spatial variance computation. Each 8×8 workgroup loads a 10×10
|
|
52
|
-
* luminance tile into shared memory.
|
|
53
|
-
*
|
|
54
|
-
* Ping-pong between two StorageTextures for temporal moment
|
|
55
|
-
* accumulation — two compute nodes, one for each write direction.
|
|
56
|
-
*
|
|
57
|
-
* Algorithm:
|
|
58
|
-
* 1. Cooperative tile loading → shared memory (luminance from color)
|
|
59
|
-
* 2. Barrier
|
|
60
|
-
* 3. Spatial variance from 3×3 shared memory neighbourhood
|
|
61
|
-
* 4. Temporal accumulation via textureLoad on previous moments
|
|
62
|
-
* 5. Write (mean, meanSq, temporalVar, spatialVar) to StorageTexture
|
|
63
|
-
*
|
|
64
|
-
* Output format (RGBA HalfFloat):
|
|
65
|
-
* R — mean luminance
|
|
66
|
-
* G — second moment (mean of squared luminance)
|
|
67
|
-
* B — temporal variance
|
|
68
|
-
* A — spatial variance
|
|
69
|
-
*
|
|
70
|
-
* Execution: ALWAYS
|
|
71
|
-
*
|
|
72
|
-
* Textures published: variance:output
|
|
73
|
-
* Textures read: configurable (default pathtracer:color)
|
|
74
|
-
*/
|
|
75
|
-
export class Variance extends RenderStage {
|
|
76
|
-
|
|
77
|
-
constructor( renderer, options = {} ) {
|
|
78
|
-
|
|
79
|
-
super( 'VarianceEstimation', {
|
|
80
|
-
...options,
|
|
81
|
-
executionMode: StageExecutionMode.ALWAYS
|
|
82
|
-
} );
|
|
83
|
-
|
|
84
|
-
this.renderer = renderer;
|
|
85
|
-
this.inputTextureName = options.inputTextureName || 'pathtracer:color';
|
|
86
|
-
|
|
87
|
-
// Parameters
|
|
88
|
-
this.varianceBoost = uniform( options.varianceBoost ?? 1.0 );
|
|
89
|
-
this.temporalAlpha = uniform( options.temporalAlpha ?? 0.1 );
|
|
90
|
-
this.resW = uniform( options.width || 1 );
|
|
91
|
-
this.resH = uniform( options.height || 1 );
|
|
92
|
-
|
|
93
|
-
// Input texture node
|
|
94
|
-
this._colorTexNode = new TextureNode();
|
|
95
|
-
|
|
96
|
-
// Ping-pong StorageTextures for temporal moments
|
|
97
|
-
const w = options.width || 1;
|
|
98
|
-
const h = options.height || 1;
|
|
99
|
-
|
|
100
|
-
// LinearFilter so downstream fragment shaders can sample these without hitting
|
|
101
|
-
// Three.js WGSL codegen bug (textureLoad without level for StorageTextures)
|
|
102
|
-
// FloatType (f32) required — HalfFloat's ~3.3 decimal digits cause catastrophic
|
|
103
|
-
// cancellation in (meanSq - mean²) for converged pixels, producing a variance
|
|
104
|
-
// floor of ~0.0001 that the (frame+1)² scaling amplifies to enormous values.
|
|
105
|
-
// StorageTextures over-allocated at max — defensive against three.js #33061
|
|
106
|
-
// (TSL compute pipeline keeps stale GPUTextureView after StorageTexture.setSize).
|
|
107
|
-
this._storageTexA = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
108
|
-
this._storageTexA.type = FloatType;
|
|
109
|
-
this._storageTexA.format = RGBAFormat;
|
|
110
|
-
this._storageTexA.minFilter = LinearFilter;
|
|
111
|
-
this._storageTexA.magFilter = LinearFilter;
|
|
112
|
-
|
|
113
|
-
this._storageTexB = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
114
|
-
this._storageTexB.type = FloatType;
|
|
115
|
-
this._storageTexB.format = RGBAFormat;
|
|
116
|
-
this._storageTexB.minFilter = LinearFilter;
|
|
117
|
-
this._storageTexB.magFilter = LinearFilter;
|
|
118
|
-
|
|
119
|
-
this._srcRegion = new Box2( new Vector2( 0, 0 ), new Vector2( 0, 0 ) );
|
|
120
|
-
|
|
121
|
-
// Right-sized RenderTarget published to downstream (UV-sampled). The
|
|
122
|
-
// over-allocated StorageTexture itself must not be published — UV
|
|
123
|
-
// sampling a 2048 texture would read the wrong region.
|
|
124
|
-
this._outputTarget = new RenderTarget( w, h, {
|
|
125
|
-
type: FloatType,
|
|
126
|
-
format: RGBAFormat,
|
|
127
|
-
minFilter: LinearFilter,
|
|
128
|
-
magFilter: LinearFilter,
|
|
129
|
-
depthBuffer: false,
|
|
130
|
-
stencilBuffer: false
|
|
131
|
-
} );
|
|
132
|
-
|
|
133
|
-
this.currentMoments = 0; // 0 = write A, read B; 1 = write B, read A
|
|
134
|
-
this._compiled = false;
|
|
135
|
-
this._needsWarmReset = false;
|
|
136
|
-
|
|
137
|
-
// Dispatch dimensions
|
|
138
|
-
this._dispatchX = Math.ceil( w / 8 );
|
|
139
|
-
this._dispatchY = Math.ceil( h / 8 );
|
|
140
|
-
|
|
141
|
-
this._buildCompute();
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
setupEventListeners() {
|
|
146
|
-
|
|
147
|
-
this.on( 'pathtracer:viewpointChanged', () => this.reset() );
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Build two compute nodes — one for each ping-pong direction.
|
|
153
|
-
*
|
|
154
|
-
* _computeNodeA: writes to StorageTexA, reads previous moments from StorageTexB
|
|
155
|
-
* _computeNodeB: writes to StorageTexB, reads previous moments from StorageTexA
|
|
156
|
-
*
|
|
157
|
-
* Read-side textures wrapped in TextureNode so compile-time type is regular Texture
|
|
158
|
-
* (avoids Three.js WGSL codegen bug: textureLoad without level for StorageTexture).
|
|
159
|
-
* Values are set to actual StorageTextures in render().
|
|
160
|
-
*/
|
|
161
|
-
_buildCompute() {
|
|
162
|
-
|
|
163
|
-
// TextureNode wrappers for reading ping-pong textures
|
|
164
|
-
// Default to EmptyTexture at compile time → codegen includes level parameter
|
|
165
|
-
this._readTexNodeA = new TextureNode();
|
|
166
|
-
this._readTexNodeB = new TextureNode();
|
|
167
|
-
|
|
168
|
-
// A writes to StorageTexA, reads previous moments from B
|
|
169
|
-
this._computeNodeA = this._buildComputeForDirection( this._storageTexA, this._readTexNodeB );
|
|
170
|
-
// B writes to StorageTexB, reads previous moments from A
|
|
171
|
-
this._computeNodeB = this._buildComputeForDirection( this._storageTexB, this._readTexNodeA );
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
_buildComputeForDirection( writeStorageTex, readTexNode ) {
|
|
176
|
-
|
|
177
|
-
const colorTex = this._colorTexNode;
|
|
178
|
-
const alpha = this.temporalAlpha;
|
|
179
|
-
const varianceBoost = this.varianceBoost;
|
|
180
|
-
const resW = this.resW;
|
|
181
|
-
const resH = this.resH;
|
|
182
|
-
|
|
183
|
-
const TILE_W = 10;
|
|
184
|
-
const TILE_TOTAL = TILE_W * TILE_W; // 100
|
|
185
|
-
const WG_SIZE = 8;
|
|
186
|
-
const WG_THREADS = WG_SIZE * WG_SIZE; // 64
|
|
187
|
-
const EXTRA_LOAD = TILE_TOTAL - WG_THREADS; // 36
|
|
188
|
-
|
|
189
|
-
const sharedLum = workgroupArray( 'float', TILE_TOTAL );
|
|
190
|
-
|
|
191
|
-
const computeFn = Fn( () => {
|
|
192
|
-
|
|
193
|
-
const lx = localId.x;
|
|
194
|
-
const ly = localId.y;
|
|
195
|
-
const linearIdx = ly.mul( WG_SIZE ).add( lx );
|
|
196
|
-
|
|
197
|
-
// Tile origin in global image coords (1px border before the core)
|
|
198
|
-
const tileOriginX = int( workgroupId.x ).mul( WG_SIZE ).sub( 1 );
|
|
199
|
-
const tileOriginY = int( workgroupId.y ).mul( WG_SIZE ).sub( 1 );
|
|
200
|
-
|
|
201
|
-
// ── Cooperative tile loading ─────────────────────
|
|
202
|
-
|
|
203
|
-
// Load #1: all 64 threads load positions 0-63
|
|
204
|
-
const sx1 = linearIdx.mod( TILE_W );
|
|
205
|
-
const sy1 = linearIdx.div( TILE_W );
|
|
206
|
-
const gx1 = tileOriginX.add( int( sx1 ) ).clamp( int( 0 ), int( resW ).sub( 1 ) );
|
|
207
|
-
const gy1 = tileOriginY.add( int( sy1 ) ).clamp( int( 0 ), int( resH ).sub( 1 ) );
|
|
208
|
-
|
|
209
|
-
const sColor1 = textureLoad( colorTex, ivec2( gx1, gy1 ) ).xyz;
|
|
210
|
-
sharedLum.element( linearIdx ).assign( sanitizeLum( luminance( sColor1 ) ) );
|
|
211
|
-
|
|
212
|
-
// Load #2: threads 0-35 load positions 64-99
|
|
213
|
-
If( linearIdx.lessThan( uint( EXTRA_LOAD ) ), () => {
|
|
214
|
-
|
|
215
|
-
const idx2 = linearIdx.add( uint( WG_THREADS ) );
|
|
216
|
-
const sx2 = idx2.mod( TILE_W );
|
|
217
|
-
const sy2 = idx2.div( TILE_W );
|
|
218
|
-
const gx2 = tileOriginX.add( int( sx2 ) ).clamp( int( 0 ), int( resW ).sub( 1 ) );
|
|
219
|
-
const gy2 = tileOriginY.add( int( sy2 ) ).clamp( int( 0 ), int( resH ).sub( 1 ) );
|
|
220
|
-
|
|
221
|
-
const sColor2 = textureLoad( colorTex, ivec2( gx2, gy2 ) ).xyz;
|
|
222
|
-
sharedLum.element( idx2 ).assign( sanitizeLum( luminance( sColor2 ) ) );
|
|
223
|
-
|
|
224
|
-
} );
|
|
225
|
-
|
|
226
|
-
workgroupBarrier();
|
|
227
|
-
|
|
228
|
-
// ── Per-thread computation ───────────────────────
|
|
229
|
-
|
|
230
|
-
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( lx ) );
|
|
231
|
-
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( ly ) );
|
|
232
|
-
|
|
233
|
-
If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
|
|
234
|
-
|
|
235
|
-
// ── Spatial variance from 3×3 shared memory ──
|
|
236
|
-
|
|
237
|
-
const spatMean = float( 0.0 ).toVar();
|
|
238
|
-
const spatMeanSq = float( 0.0 ).toVar();
|
|
239
|
-
|
|
240
|
-
for ( let dy = - 1; dy <= 1; dy ++ ) {
|
|
241
|
-
|
|
242
|
-
for ( let dx = - 1; dx <= 1; dx ++ ) {
|
|
243
|
-
|
|
244
|
-
const val = sharedLum.element(
|
|
245
|
-
ly.add( 1 + dy ).mul( TILE_W ).add( lx.add( 1 + dx ) )
|
|
246
|
-
);
|
|
247
|
-
spatMean.addAssign( val );
|
|
248
|
-
spatMeanSq.addAssign( val.mul( val ) );
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
spatMean.divAssign( 9.0 );
|
|
255
|
-
spatMeanSq.divAssign( 9.0 );
|
|
256
|
-
const spatialVariance = max( spatMeanSq.sub( spatMean.mul( spatMean ) ), float( 0.0 ) );
|
|
257
|
-
|
|
258
|
-
// ── Temporal accumulation ─────────────────────
|
|
259
|
-
// Current luminance from center of shared memory tile
|
|
260
|
-
const lum = sharedLum.element( ly.add( 1 ).mul( TILE_W ).add( lx.add( 1 ) ) );
|
|
261
|
-
|
|
262
|
-
// Previous moments via textureLoad (per-pixel, not tiled)
|
|
263
|
-
const prevMoments = textureLoad( readTexNode, ivec2( gx, gy ) );
|
|
264
|
-
|
|
265
|
-
// ── Write output ──────────────────────────────
|
|
266
|
-
|
|
267
|
-
textureStore(
|
|
268
|
-
writeStorageTex,
|
|
269
|
-
uvec2( uint( gx ), uint( gy ) ),
|
|
270
|
-
temporalAccumulate(
|
|
271
|
-
lum, prevMoments.x, prevMoments.y,
|
|
272
|
-
alpha, spatialVariance, varianceBoost
|
|
273
|
-
)
|
|
274
|
-
).toWriteOnly();
|
|
275
|
-
|
|
276
|
-
} );
|
|
277
|
-
|
|
278
|
-
} );
|
|
279
|
-
|
|
280
|
-
return computeFn().compute(
|
|
281
|
-
[ this._dispatchX, this._dispatchY, 1 ],
|
|
282
|
-
[ WG_SIZE, WG_SIZE, 1 ]
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
render( context ) {
|
|
288
|
-
|
|
289
|
-
if ( ! this.enabled ) return;
|
|
290
|
-
|
|
291
|
-
const colorTex = context.getTexture( this.inputTextureName );
|
|
292
|
-
if ( ! colorTex ) return;
|
|
293
|
-
|
|
294
|
-
// Auto-size
|
|
295
|
-
const img = colorTex.image;
|
|
296
|
-
if ( img && img.width > 0 && img.height > 0 ) {
|
|
297
|
-
|
|
298
|
-
if ( img.width !== this._outputTarget.width ||
|
|
299
|
-
img.height !== this._outputTarget.height ) {
|
|
300
|
-
|
|
301
|
-
this.setSize( img.width, img.height );
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
this._colorTexNode.value = colorTex;
|
|
308
|
-
|
|
309
|
-
// Force-compile both compute nodes on first frame while _readTexNodeA/B
|
|
310
|
-
// still hold EmptyTexture. This ensures WGSLNodeBuilder.generateTextureLoad()
|
|
311
|
-
// sees isStorageTexture=false and emits the required level parameter.
|
|
312
|
-
// The throwaway dispatches initialise both StorageTextures to near-zero,
|
|
313
|
-
// which is correct for temporal moment accumulation on frame 0.
|
|
314
|
-
if ( ! this._compiled ) {
|
|
315
|
-
|
|
316
|
-
this.renderer.compute( this._computeNodeA );
|
|
317
|
-
this.renderer.compute( this._computeNodeB );
|
|
318
|
-
this._compiled = true;
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Set read-side TextureNode values to actual StorageTextures.
|
|
323
|
-
// Shaders are already compiled — this only updates the uniform binding.
|
|
324
|
-
this._readTexNodeA.value = this._storageTexA;
|
|
325
|
-
this._readTexNodeB.value = this._storageTexB;
|
|
326
|
-
|
|
327
|
-
// Warm reset: initialize BOTH ping-pong textures to current scene data
|
|
328
|
-
// using alpha=1.0 so mean=currentLum, meanSq=currentLum², variance=0.
|
|
329
|
-
// This avoids ~30 frames of EMA convergence from stale pre-reset data.
|
|
330
|
-
if ( this._needsWarmReset ) {
|
|
331
|
-
|
|
332
|
-
const savedAlpha = this.temporalAlpha.value;
|
|
333
|
-
this.temporalAlpha.value = 1.0;
|
|
334
|
-
this.renderer.compute( this._computeNodeA );
|
|
335
|
-
this.renderer.compute( this._computeNodeB );
|
|
336
|
-
this.temporalAlpha.value = savedAlpha;
|
|
337
|
-
this._needsWarmReset = false;
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Dispatch correct ping-pong direction
|
|
342
|
-
const computeNode = this.currentMoments === 0
|
|
343
|
-
? this._computeNodeA // write A, read B
|
|
344
|
-
: this._computeNodeB; // write B, read A
|
|
345
|
-
|
|
346
|
-
this.renderer.compute( computeNode );
|
|
347
|
-
|
|
348
|
-
// The write target this frame
|
|
349
|
-
const writeTarget = this.currentMoments === 0
|
|
350
|
-
? this._storageTexA
|
|
351
|
-
: this._storageTexB;
|
|
352
|
-
|
|
353
|
-
// Swap for next frame
|
|
354
|
-
this.currentMoments = 1 - this.currentMoments;
|
|
355
|
-
|
|
356
|
-
// Copy the active region out of the over-allocated StorageTexture into the
|
|
357
|
-
// right-sized RenderTarget; downstream stages UV-sample the latter.
|
|
358
|
-
this._srcRegion.max.set( this._outputTarget.width, this._outputTarget.height );
|
|
359
|
-
this.renderer.copyTextureToTexture( writeTarget, this._outputTarget.texture, this._srcRegion );
|
|
360
|
-
|
|
361
|
-
// Publish the RenderTarget (not the over-allocated StorageTexture)
|
|
362
|
-
context.setTexture( 'variance:output', this._outputTarget.texture );
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Free the 2048² StorageTextures when disabled; three.js re-creates them on the next dispatch
|
|
367
|
-
// after re-enable, and reset() re-anchors the EMA. See ASVGF.releaseGPUMemory.
|
|
368
|
-
releaseGPUMemory() {
|
|
369
|
-
|
|
370
|
-
this._storageTexA?.dispose();
|
|
371
|
-
this._storageTexB?.dispose();
|
|
372
|
-
// Render-res RT texture (dispose .texture, not the RT — RT.dispose() doesn't free it here).
|
|
373
|
-
this.context?.removeTexture( 'variance:output' );
|
|
374
|
-
this._outputTarget?.texture?.dispose();
|
|
375
|
-
this.reset();
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
reset() {
|
|
380
|
-
|
|
381
|
-
this.currentMoments = 0;
|
|
382
|
-
// Defer initialization to next render() when fresh scene data is available.
|
|
383
|
-
// Using alpha=1.0 on the first post-reset frame immediately anchors the EMA
|
|
384
|
-
// to the new scene, avoiding ~30 frames of convergence from stale data.
|
|
385
|
-
this._needsWarmReset = true;
|
|
386
|
-
|
|
387
|
-
if ( this.context ) this.context.removeTexture( 'variance:output' );
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
setSize( width, height ) {
|
|
392
|
-
|
|
393
|
-
// StorageTextures stay at their max allocation (see constructor).
|
|
394
|
-
this._outputTarget.setSize( width, height );
|
|
395
|
-
this._outputTarget.texture.needsUpdate = true;
|
|
396
|
-
this.resW.value = width;
|
|
397
|
-
this.resH.value = height;
|
|
398
|
-
|
|
399
|
-
// Update dispatch dimensions
|
|
400
|
-
this._dispatchX = Math.ceil( width / 8 );
|
|
401
|
-
this._dispatchY = Math.ceil( height / 8 );
|
|
402
|
-
this._computeNodeA.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
403
|
-
this._computeNodeB.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
408
|
-
// reserved size + rebuild the compute nodes in place. Same stage object, so refs/wiring stay valid.
|
|
409
|
-
reallocateReservedStorage() {
|
|
410
|
-
|
|
411
|
-
this._computeNodeA?.dispose();
|
|
412
|
-
this._computeNodeB?.dispose();
|
|
413
|
-
this._storageTexA?.dispose();
|
|
414
|
-
this._storageTexB?.dispose();
|
|
415
|
-
const mk = () => {
|
|
416
|
-
|
|
417
|
-
const t = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
418
|
-
t.type = FloatType; t.format = RGBAFormat; t.minFilter = LinearFilter; t.magFilter = LinearFilter;
|
|
419
|
-
return t;
|
|
420
|
-
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
this._storageTexA = mk();
|
|
424
|
-
this._storageTexB = mk();
|
|
425
|
-
this._buildCompute();
|
|
426
|
-
this._compiled = false;
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
dispose() {
|
|
431
|
-
|
|
432
|
-
this._computeNodeA?.dispose();
|
|
433
|
-
this._computeNodeB?.dispose();
|
|
434
|
-
this._storageTexA?.dispose();
|
|
435
|
-
this._storageTexB?.dispose();
|
|
436
|
-
this._outputTarget?.dispose();
|
|
437
|
-
this._colorTexNode?.dispose();
|
|
438
|
-
this._readTexNodeA?.dispose();
|
|
439
|
-
this._readTexNodeB?.dispose();
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
}
|