rayzee 11.5.0 → 11.7.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 +14 -14
- package/dist/{ArchiveImporter-29Nl1567.js → ArchiveImporter-DYcRXeKE.js} +2 -2
- package/dist/{BidirectionalIntegrator-CJay8tXK.js → BidirectionalIntegrator-BvjRpbTP.js} +289 -289
- package/dist/{EngineEvents-Ci8dVWCP.js → EngineEvents-CLckhYbL.js} +1 -0
- package/dist/{NeuralRetouch-KqGVPV7u.js → NeuralRetouch-ClaMiKYg.js} +1 -1
- package/dist/{NeuralSuperRes-BHxhYzlD.js → NeuralSuperRes-WNEXun9s.js} +2 -2
- package/dist/{PathTracer-Cldt0mXi.js → PathTracer-B9wZPPVL.js} +3197 -3167
- package/dist/{PhysicalSky-CUWrTcmT.js → PhysicalSky-DcjItqKi.js} +1 -1
- package/dist/{RayzeeRenderer-Dd-Dit0v.js → RayzeeRenderer-p-NeE6E2.js} +405 -437
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/rayzee-core.es.js +5 -5
- package/dist/rayzee.es.js +1003 -1686
- package/dist/rayzee.umd.js +104 -143
- package/dist/{sceneCachePolicy--ta6moJu.js → sceneCachePolicy-B9w3BnaP.js} +2 -2
- package/package.json +2 -2
- package/src/EngineEvents.js +2 -0
- package/src/Passes/OIDNDenoiser.js +106 -100
- package/src/PathTracerApp.js +8 -19
- 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/PackedRayBuffer.js +2 -2
- package/src/Processor/StorageTexturePool.js +1 -1
- package/src/Processor/TextureReadback.js +18 -1
- package/src/RayzeeRenderer.js +5 -5
- package/src/Stages/DenoiserSettings.js +5 -81
- package/src/Stages/MotionVector.js +21 -215
- package/src/Stages/NormalDepth.js +5 -5
- package/src/Stages/PathTracer.js +3 -11
- package/src/Stages/PathTracerStage.js +4 -17
- package/src/TSL/FinalWriteKernel.js +2 -2
- package/src/TSL/ShadeKernel.js +3 -3
- package/src/index.js +1 -1
- package/src/managers/DenoisingManager.js +93 -187
- 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
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
import { Fn, wgslFn, vec3, vec4, float, int, uint, ivec2, uvec2, uniform, If, max, mix, sqrt,
|
|
2
|
-
textureLoad, textureStore, localId, workgroupId } from 'three/tsl';
|
|
3
|
-
import { RenderTarget, TextureNode, StorageTexture } from 'three/webgpu';
|
|
4
|
-
import { HalfFloatType, 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
|
-
import { ALBEDO_EPS } from './DenoiserSettings.js';
|
|
9
|
-
|
|
10
|
-
// SVGF bilateral edge-stopping weight. All three φ params are relative
|
|
11
|
-
// tolerances (unitless fractions) so the filter is scale-invariant across
|
|
12
|
-
// scenes, HDR ranges, and camera distances. sigmaL is precomputed by the
|
|
13
|
-
// caller as phiLum * √variance / albedoLum + ε, compensating for the
|
|
14
|
-
// 1/albedo noise amplification introduced by demodulation.
|
|
15
|
-
const bilateralWeight = /*@__PURE__*/ wgslFn( `
|
|
16
|
-
fn bilateralWeight(
|
|
17
|
-
centerLum: f32, sLum: f32,
|
|
18
|
-
centerNormal: vec3f, sNormal: vec3f,
|
|
19
|
-
centerDepth: f32, sDepth: f32,
|
|
20
|
-
centerColor: vec3f, sColor: vec3f,
|
|
21
|
-
kernelW: f32,
|
|
22
|
-
sigmaL: f32, phiNorm: f32, phiDep: f32, phiCol: f32
|
|
23
|
-
) -> f32 {
|
|
24
|
-
|
|
25
|
-
let lumW = exp( -abs( centerLum - sLum ) / sigmaL );
|
|
26
|
-
// clamp dot to [0,1]: miss-ray normals decode to (-1,-1,-1) with
|
|
27
|
-
// dot=3 → pow saturates to +inf → inf*0 = NaN. See project_tsl_pitfalls.
|
|
28
|
-
// max(phiNorm, 1e-6): pow(0.0, 0.0) is undefined in WGSL and returned NaN on ~12%
|
|
29
|
-
// of pixels when phiNormal was set to 0.
|
|
30
|
-
let normW = pow( clamp( dot( centerNormal, sNormal ), 0.0, 1.0 ), max( phiNorm, 1e-6 ) );
|
|
31
|
-
let depW = exp( -abs( centerDepth - sDepth ) / max( centerDepth * phiDep, 0.001 ) );
|
|
32
|
-
let maxDiff = max( max( abs( centerColor.x - sColor.x ),
|
|
33
|
-
abs( centerColor.y - sColor.y ) ),
|
|
34
|
-
abs( centerColor.z - sColor.z ) );
|
|
35
|
-
let avgLum = max( ( centerLum + sLum ) * 0.5, 0.0001 );
|
|
36
|
-
let colW = exp( -( maxDiff / avgLum ) / max( phiCol, 0.0001 ) );
|
|
37
|
-
return kernelW * lumW * normW * depW * colW;
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
` );
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* BilateralFilter — 5×5 à-trous wavelet, edge-preserving, multi-iteration.
|
|
44
|
-
*
|
|
45
|
-
* Reads asvgf:demodulated (lighting), filters in demodulated space across
|
|
46
|
-
* `iterations` ping-pong passes with step size 2^i, multiplies by albedo on
|
|
47
|
-
* the final pass to remodulate. φ params are relative tolerances.
|
|
48
|
-
*
|
|
49
|
-
* Publishes: bilateralFiltering:output (modulated)
|
|
50
|
-
* Reads: asvgf:demodulated (or fallback), pathtracer:normalDepth,
|
|
51
|
-
* pathtracer:albedo, variance:output
|
|
52
|
-
*/
|
|
53
|
-
export class BilateralFilter extends RenderStage {
|
|
54
|
-
|
|
55
|
-
constructor( renderer, options = {} ) {
|
|
56
|
-
|
|
57
|
-
super( 'BilateralFiltering', {
|
|
58
|
-
...options,
|
|
59
|
-
executionMode: StageExecutionMode.ALWAYS
|
|
60
|
-
} );
|
|
61
|
-
|
|
62
|
-
this.renderer = renderer;
|
|
63
|
-
this.inputTextureName = options.inputTextureName || 'asvgf:demodulated';
|
|
64
|
-
this.normalDepthTextureName = options.normalDepthTextureName || 'pathtracer:normalDepth';
|
|
65
|
-
// Mapped (normal/bump-perturbed) normal for the normal edge-stop — geometric
|
|
66
|
-
// normals are flat across a normal-mapped surface so normW can't preserve bump detail.
|
|
67
|
-
this.shadingNormalTextureName = options.shadingNormalTextureName || 'pathtracer:shadingNormal';
|
|
68
|
-
this.albedoTextureName = options.albedoTextureName || 'pathtracer:albedo';
|
|
69
|
-
this.varianceTextureName = options.varianceTextureName || 'variance:output';
|
|
70
|
-
this.iterations = options.iterations ?? 4;
|
|
71
|
-
|
|
72
|
-
// All φ are relative tolerances (fractions of mean/depth). Bigger =
|
|
73
|
-
// more permissive blending across edges.
|
|
74
|
-
this.phiColor = uniform( options.phiColor ?? 0.5 );
|
|
75
|
-
this.phiNormal = uniform( options.phiNormal ?? 128.0 );
|
|
76
|
-
this.phiDepth = uniform( options.phiDepth ?? 0.05 );
|
|
77
|
-
this.phiLuminance = uniform( options.phiLuminance ?? 4.0 );
|
|
78
|
-
// Blend Variance's spatial-variance channel into sigma_l (1 = max(temporal, spatial),
|
|
79
|
-
// 0 = temporal-only). Widens the luminance gate where history is thin but the
|
|
80
|
-
// neighbourhood is noisy (disocclusion). Default on — validated −1.7% RMSE @4spp.
|
|
81
|
-
this.spatialVarianceWeight = uniform( options.spatialVarianceWeight ?? 1.0 );
|
|
82
|
-
this.stepSizeU = uniform( 1, 'int' );
|
|
83
|
-
// 1 on the final iteration → multiply by albedo to remodulate.
|
|
84
|
-
this.isLastIterationU = uniform( 0, 'int' );
|
|
85
|
-
// 1 on pass 0 → seed variance from variance:output; later passes read the carried value.
|
|
86
|
-
this.isFirstIterationU = uniform( 1, 'int' );
|
|
87
|
-
this.resW = uniform( options.width || 1 );
|
|
88
|
-
this.resH = uniform( options.height || 1 );
|
|
89
|
-
|
|
90
|
-
this._readTexNode = new TextureNode();
|
|
91
|
-
this._normalDepthTexNode = new TextureNode();
|
|
92
|
-
this._shadingNormalTexNode = new TextureNode();
|
|
93
|
-
this._albedoTexNode = new TextureNode();
|
|
94
|
-
this._varianceTexNode = new TextureNode();
|
|
95
|
-
// pathtracer:color, read only for its alpha (coverage) on the final pass.
|
|
96
|
-
this._srcAlphaTexNode = new TextureNode();
|
|
97
|
-
|
|
98
|
-
const w = options.width || 1;
|
|
99
|
-
const h = options.height || 1;
|
|
100
|
-
|
|
101
|
-
// Pre-allocate StorageTextures at max — defensive against three.js #33061
|
|
102
|
-
// (TSL compute pipeline keeps a stale GPUTextureView after setSize()).
|
|
103
|
-
|
|
104
|
-
// LinearFilter required for textureLoad codegen on StorageTextures.
|
|
105
|
-
this._storageTexA = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
106
|
-
this._storageTexA.type = HalfFloatType;
|
|
107
|
-
this._storageTexA.format = RGBAFormat;
|
|
108
|
-
this._storageTexA.minFilter = LinearFilter;
|
|
109
|
-
this._storageTexA.magFilter = LinearFilter;
|
|
110
|
-
|
|
111
|
-
this._storageTexB = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
112
|
-
this._storageTexB.type = HalfFloatType;
|
|
113
|
-
this._storageTexB.format = RGBAFormat;
|
|
114
|
-
this._storageTexB.minFilter = LinearFilter;
|
|
115
|
-
this._storageTexB.magFilter = LinearFilter;
|
|
116
|
-
|
|
117
|
-
this._srcRegion = new Box2( new Vector2( 0, 0 ), new Vector2( 0, 0 ) );
|
|
118
|
-
|
|
119
|
-
// Active-size RT published downstream; over-allocated storage tex sampled
|
|
120
|
-
// by UV would read the wrong region.
|
|
121
|
-
this._outputTarget = new RenderTarget( w, h, {
|
|
122
|
-
type: HalfFloatType,
|
|
123
|
-
format: RGBAFormat,
|
|
124
|
-
minFilter: LinearFilter,
|
|
125
|
-
magFilter: LinearFilter,
|
|
126
|
-
depthBuffer: false,
|
|
127
|
-
stencilBuffer: false
|
|
128
|
-
} );
|
|
129
|
-
|
|
130
|
-
this._compiled = false;
|
|
131
|
-
|
|
132
|
-
this._dispatchX = Math.ceil( w / 8 );
|
|
133
|
-
this._dispatchY = Math.ceil( h / 8 );
|
|
134
|
-
|
|
135
|
-
this._buildCompute();
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// One compute node per ping-pong write direction.
|
|
140
|
-
_buildCompute() {
|
|
141
|
-
|
|
142
|
-
this._computeNodeA = this._buildComputeForDirection( this._storageTexA );
|
|
143
|
-
this._computeNodeB = this._buildComputeForDirection( this._storageTexB );
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
_buildComputeForDirection( writeStorageTex ) {
|
|
148
|
-
|
|
149
|
-
const readTexNode = this._readTexNode;
|
|
150
|
-
const ndTexNode = this._normalDepthTexNode;
|
|
151
|
-
const snTexNode = this._shadingNormalTexNode;
|
|
152
|
-
const albedoTexNode = this._albedoTexNode;
|
|
153
|
-
const varTexNode = this._varianceTexNode;
|
|
154
|
-
const srcAlphaTexNode = this._srcAlphaTexNode;
|
|
155
|
-
const phiColor = this.phiColor;
|
|
156
|
-
const phiNormal = this.phiNormal;
|
|
157
|
-
const phiDepth = this.phiDepth;
|
|
158
|
-
const phiLuminance = this.phiLuminance;
|
|
159
|
-
const spatialVarianceWeight = this.spatialVarianceWeight;
|
|
160
|
-
const stepSize = this.stepSizeU;
|
|
161
|
-
const isLastIterationU = this.isLastIterationU;
|
|
162
|
-
const isFirstIterationU = this.isFirstIterationU;
|
|
163
|
-
const resW = this.resW;
|
|
164
|
-
const resH = this.resH;
|
|
165
|
-
|
|
166
|
-
// 5×5 A-trous kernel weights (Gaussian approx, sum = 1.0)
|
|
167
|
-
const kernel = [
|
|
168
|
-
1.0 / 256.0, 4.0 / 256.0, 6.0 / 256.0, 4.0 / 256.0, 1.0 / 256.0,
|
|
169
|
-
4.0 / 256.0, 16.0 / 256.0, 24.0 / 256.0, 16.0 / 256.0, 4.0 / 256.0,
|
|
170
|
-
6.0 / 256.0, 24.0 / 256.0, 36.0 / 256.0, 24.0 / 256.0, 6.0 / 256.0,
|
|
171
|
-
4.0 / 256.0, 16.0 / 256.0, 24.0 / 256.0, 16.0 / 256.0, 4.0 / 256.0,
|
|
172
|
-
1.0 / 256.0, 4.0 / 256.0, 6.0 / 256.0, 4.0 / 256.0, 1.0 / 256.0,
|
|
173
|
-
];
|
|
174
|
-
|
|
175
|
-
const WG_SIZE = 8;
|
|
176
|
-
|
|
177
|
-
const computeFn = Fn( () => {
|
|
178
|
-
|
|
179
|
-
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
180
|
-
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
181
|
-
|
|
182
|
-
If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
|
|
183
|
-
|
|
184
|
-
const coord = ivec2( gx, gy );
|
|
185
|
-
const centerSample = textureLoad( readTexNode, coord );
|
|
186
|
-
const centerColor = centerSample.xyz;
|
|
187
|
-
const centerND = textureLoad( ndTexNode, coord );
|
|
188
|
-
// Normal edge-stop reads the mapped (shading) normal; depth gate stays geometric.
|
|
189
|
-
const centerNormal = textureLoad( snTexNode, coord ).xyz.mul( 2.0 ).sub( 1.0 );
|
|
190
|
-
const centerDepth = centerND.w;
|
|
191
|
-
const centerLum = luminance( centerColor );
|
|
192
|
-
const centerSafeAlbedo = max( textureLoad( albedoTexNode, coord ).xyz, vec3( ALBEDO_EPS ) );
|
|
193
|
-
const centerAlbedoLum = max( luminance( centerSafeAlbedo ), float( ALBEDO_EPS ) ).toVar();
|
|
194
|
-
|
|
195
|
-
// Variance is filtered alongside colour and carried between passes in the
|
|
196
|
-
// ping-pong's spare alpha, so sigma_l tightens as the signal smooths. Without
|
|
197
|
-
// it every pass re-read the unfiltered variance:output and filter strength
|
|
198
|
-
// never backed off as the render converged.
|
|
199
|
-
//
|
|
200
|
-
// Stored as the ROOT: converged variance reaches ~1e-6, a half-float subnormal
|
|
201
|
-
// that flushes to zero on some drivers. select() rather than If() — an If
|
|
202
|
-
// around a texture read lets the inactive branch contaminate the result.
|
|
203
|
-
// .z = temporal, .w = spatial 3×3; blending toward max widens sigma_l where
|
|
204
|
-
// history is thin.
|
|
205
|
-
const isFirst = isFirstIterationU.equal( int( 1 ) );
|
|
206
|
-
const seedVariance = ( at ) => {
|
|
207
|
-
|
|
208
|
-
const v = textureLoad( varTexNode, at );
|
|
209
|
-
return mix( v.z, max( v.z, v.w ), spatialVarianceWeight );
|
|
210
|
-
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const centerVariance = isFirst.select(
|
|
214
|
-
seedVariance( coord ),
|
|
215
|
-
centerSample.w.mul( centerSample.w )
|
|
216
|
-
).toVar();
|
|
217
|
-
|
|
218
|
-
// sigma_l = phiLum * √variance / albedoLum + ε. Dividing by
|
|
219
|
-
// albedoLum compensates for the 1/albedo noise amplification
|
|
220
|
-
// from demodulation — otherwise dark materials get an
|
|
221
|
-
// under-estimated sigma → over-strict luminance gate → no
|
|
222
|
-
// blending → silhouette dark-outline artifact.
|
|
223
|
-
const sigmaL = phiLuminance
|
|
224
|
-
.mul( sqrt( max( centerVariance, float( 0.0 ) ) ) )
|
|
225
|
-
.div( centerAlbedoLum )
|
|
226
|
-
.add( float( 0.0001 ) );
|
|
227
|
-
|
|
228
|
-
const colorSum = vec3( 0.0 ).toVar();
|
|
229
|
-
const weightSum = float( 0.0 ).toVar();
|
|
230
|
-
// Squared weights, normalised by weightSum² — the variance of a weighted mean.
|
|
231
|
-
const varianceSum = float( 0.0 ).toVar();
|
|
232
|
-
|
|
233
|
-
// 5×5 à-trous kernel (Gaussian-approx, Σ=1)
|
|
234
|
-
for ( let iy = 0; iy < 5; iy ++ ) {
|
|
235
|
-
|
|
236
|
-
for ( let ix = 0; ix < 5; ix ++ ) {
|
|
237
|
-
|
|
238
|
-
const dx = ix - 2;
|
|
239
|
-
const dy = iy - 2;
|
|
240
|
-
const kw = kernel[ iy * 5 + ix ];
|
|
241
|
-
|
|
242
|
-
const sx = gx.add( stepSize.mul( dx ) )
|
|
243
|
-
.clamp( int( 0 ), int( resW ).sub( 1 ) );
|
|
244
|
-
const sy = gy.add( stepSize.mul( dy ) )
|
|
245
|
-
.clamp( int( 0 ), int( resH ).sub( 1 ) );
|
|
246
|
-
|
|
247
|
-
const sSample = textureLoad( readTexNode, ivec2( sx, sy ) );
|
|
248
|
-
const sColor = sSample.xyz;
|
|
249
|
-
const sND = textureLoad( ndTexNode, ivec2( sx, sy ) );
|
|
250
|
-
const sNormal = textureLoad( snTexNode, ivec2( sx, sy ) ).xyz.mul( 2.0 ).sub( 1.0 );
|
|
251
|
-
const sDepth = sND.w;
|
|
252
|
-
const sLum = luminance( sColor );
|
|
253
|
-
|
|
254
|
-
const w = bilateralWeight(
|
|
255
|
-
centerLum, sLum,
|
|
256
|
-
centerNormal, sNormal,
|
|
257
|
-
centerDepth, sDepth,
|
|
258
|
-
centerColor, sColor,
|
|
259
|
-
float( kw ),
|
|
260
|
-
sigmaL, phiNormal, phiDepth, phiColor
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const sVariance = isFirst.select(
|
|
264
|
-
seedVariance( ivec2( sx, sy ) ),
|
|
265
|
-
sSample.w.mul( sSample.w )
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
colorSum.addAssign( sColor.mul( w ) );
|
|
269
|
-
weightSum.addAssign( w );
|
|
270
|
-
varianceSum.addAssign( w.mul( w ).mul( max( sVariance, float( 0.0 ) ) ) );
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const filtered = colorSum.div( max( weightSum, float( 0.0001 ) ) );
|
|
277
|
-
const filteredVariance = varianceSum
|
|
278
|
-
.div( max( weightSum.mul( weightSum ), float( 1e-8 ) ) );
|
|
279
|
-
|
|
280
|
-
// Remodulate by albedo only on the final iteration so the
|
|
281
|
-
// inner ping-pong stays in demodulated space.
|
|
282
|
-
const isLast = isLastIterationU.equal( int( 1 ) );
|
|
283
|
-
const output = isLast.select( filtered.mul( centerSafeAlbedo ), filtered );
|
|
284
|
-
|
|
285
|
-
// The last pass restores the path tracer's coverage — Compositor reads .w as
|
|
286
|
-
// image alpha when transparentBackground is on.
|
|
287
|
-
const carriedAlpha = isLast.select(
|
|
288
|
-
textureLoad( srcAlphaTexNode, coord ).w,
|
|
289
|
-
sqrt( max( filteredVariance, float( 0.0 ) ) )
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
textureStore(
|
|
293
|
-
writeStorageTex,
|
|
294
|
-
uvec2( uint( gx ), uint( gy ) ),
|
|
295
|
-
vec4( output, carriedAlpha )
|
|
296
|
-
).toWriteOnly();
|
|
297
|
-
|
|
298
|
-
} );
|
|
299
|
-
|
|
300
|
-
} );
|
|
301
|
-
|
|
302
|
-
return computeFn().compute(
|
|
303
|
-
[ this._dispatchX, this._dispatchY, 1 ],
|
|
304
|
-
[ WG_SIZE, WG_SIZE, 1 ]
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
render( context ) {
|
|
310
|
-
|
|
311
|
-
if ( ! this.enabled ) return;
|
|
312
|
-
|
|
313
|
-
const inputTex = context.getTexture( this.inputTextureName )
|
|
314
|
-
|| context.getTexture( 'asvgf:output' )
|
|
315
|
-
|| context.getTexture( 'pathtracer:color' );
|
|
316
|
-
const ndTex = context.getTexture( this.normalDepthTextureName );
|
|
317
|
-
// Fall back to geometric normalDepth if the mapped normal isn't published.
|
|
318
|
-
const snTex = context.getTexture( this.shadingNormalTextureName ) || ndTex;
|
|
319
|
-
const albedoTex = context.getTexture( this.albedoTextureName );
|
|
320
|
-
const varTex = context.getTexture( this.varianceTextureName );
|
|
321
|
-
const srcAlphaTex = context.getTexture( 'pathtracer:color' );
|
|
322
|
-
|
|
323
|
-
if ( ! inputTex ) return;
|
|
324
|
-
|
|
325
|
-
const img = inputTex.image;
|
|
326
|
-
if ( img && img.width > 0 && img.height > 0 ) {
|
|
327
|
-
|
|
328
|
-
// Compare against an active-size RT, not the fixed-2048 StorageTexture.
|
|
329
|
-
if ( img.width !== this._outputTarget.width ||
|
|
330
|
-
img.height !== this._outputTarget.height ) {
|
|
331
|
-
|
|
332
|
-
this.setSize( img.width, img.height );
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// RenderTarget textures — safe to bind before first-compile.
|
|
339
|
-
if ( ndTex ) this._normalDepthTexNode.value = ndTex;
|
|
340
|
-
if ( snTex ) this._shadingNormalTexNode.value = snTex;
|
|
341
|
-
if ( albedoTex ) this._albedoTexNode.value = albedoTex;
|
|
342
|
-
// Bind these BEFORE compile too: two TextureNodes still holding the default empty
|
|
343
|
-
// texture when the node builder runs share one GPU binding, which then resolves to
|
|
344
|
-
// whichever is assigned last. That aliased varTexNode onto the colour input.
|
|
345
|
-
//
|
|
346
|
-
// Safe only because inputTex is a RenderTarget texture, which produces the same
|
|
347
|
-
// `textureLoad(t, coord, level)` codegen as EmptyTexture — the form the StorageTexture
|
|
348
|
-
// ping-pong needs on passes 1+. Binding an actual StorageTexture here would emit the
|
|
349
|
-
// no-level form and later reads would return zero. EdgeFilter must NOT do this: its
|
|
350
|
-
// pass-0 input IS a StorageTexture.
|
|
351
|
-
this._readTexNode.value = inputTex;
|
|
352
|
-
if ( varTex ) this._varianceTexNode.value = varTex;
|
|
353
|
-
if ( srcAlphaTex ) this._srcAlphaTexNode.value = srcAlphaTex;
|
|
354
|
-
|
|
355
|
-
// First-frame compile while StorageTexture-typed nodes still hold
|
|
356
|
-
// EmptyTexture — codegen then emits textureLoad with the level
|
|
357
|
-
// parameter, which the runtime requires for non-zero reads.
|
|
358
|
-
if ( ! this._compiled ) {
|
|
359
|
-
|
|
360
|
-
this.renderer.compute( this._computeNodeA );
|
|
361
|
-
this.renderer.compute( this._computeNodeB );
|
|
362
|
-
this._compiled = true;
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// À-trous iterations: step size 2^i, ping-pong write direction.
|
|
367
|
-
// Last iteration multiplies by albedo to remodulate.
|
|
368
|
-
let readTex = inputTex;
|
|
369
|
-
let writeNode = this._computeNodeA;
|
|
370
|
-
let nextWriteNode = this._computeNodeB;
|
|
371
|
-
|
|
372
|
-
for ( let i = 0; i < this.iterations; i ++ ) {
|
|
373
|
-
|
|
374
|
-
this.stepSizeU.value = 1 << i;
|
|
375
|
-
this._readTexNode.value = readTex;
|
|
376
|
-
this.isLastIterationU.value = ( i === this.iterations - 1 ) ? 1 : 0;
|
|
377
|
-
this.isFirstIterationU.value = i === 0 ? 1 : 0;
|
|
378
|
-
|
|
379
|
-
this.renderer.compute( writeNode );
|
|
380
|
-
|
|
381
|
-
readTex = ( writeNode === this._computeNodeA )
|
|
382
|
-
? this._storageTexA
|
|
383
|
-
: this._storageTexB;
|
|
384
|
-
|
|
385
|
-
const tmp = writeNode;
|
|
386
|
-
writeNode = nextWriteNode;
|
|
387
|
-
nextWriteNode = tmp;
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Copy the final result out of the over-allocated StorageTexture into
|
|
392
|
-
// the active-size RenderTarget; downstream stages UV-sample the latter.
|
|
393
|
-
this._srcRegion.max.set( this._outputTarget.width, this._outputTarget.height );
|
|
394
|
-
this.renderer.copyTextureToTexture( readTex, this._outputTarget.texture, this._srcRegion );
|
|
395
|
-
|
|
396
|
-
context.setTexture( 'bilateralFiltering:output', this._outputTarget.texture );
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Accepts the same keys as ASVGF presets; unknown keys ignored.
|
|
401
|
-
updateParameters( params ) {
|
|
402
|
-
|
|
403
|
-
if ( ! params ) return;
|
|
404
|
-
if ( params.phiColor !== undefined ) this.phiColor.value = params.phiColor;
|
|
405
|
-
if ( params.phiNormal !== undefined ) this.phiNormal.value = params.phiNormal;
|
|
406
|
-
if ( params.phiDepth !== undefined ) this.phiDepth.value = params.phiDepth;
|
|
407
|
-
if ( params.phiLuminance !== undefined ) this.phiLuminance.value = params.phiLuminance;
|
|
408
|
-
if ( params.spatialVarianceWeight !== undefined ) this.spatialVarianceWeight.value = params.spatialVarianceWeight;
|
|
409
|
-
if ( params.atrousIterations !== undefined ) this.iterations = params.atrousIterations;
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
setSize( width, height ) {
|
|
414
|
-
|
|
415
|
-
// StorageTextures stay at their max allocation (see constructor).
|
|
416
|
-
this._outputTarget.setSize( width, height );
|
|
417
|
-
this._outputTarget.texture.needsUpdate = true;
|
|
418
|
-
this.resW.value = width;
|
|
419
|
-
this.resH.value = height;
|
|
420
|
-
|
|
421
|
-
// Update dispatch dimensions
|
|
422
|
-
this._dispatchX = Math.ceil( width / 8 );
|
|
423
|
-
this._dispatchY = Math.ceil( height / 8 );
|
|
424
|
-
this._computeNodeA.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
425
|
-
this._computeNodeB.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Free the 2048² StorageTextures when disabled; three.js re-creates them on the next dispatch
|
|
430
|
-
// after re-enable (no temporal state to re-anchor). See ASVGF.releaseGPUMemory.
|
|
431
|
-
releaseGPUMemory() {
|
|
432
|
-
|
|
433
|
-
this._storageTexA?.dispose();
|
|
434
|
-
this._storageTexB?.dispose();
|
|
435
|
-
// Render-res RT texture (dispose .texture, not the RT — RT.dispose() doesn't free it here).
|
|
436
|
-
this.context?.removeTexture( 'bilateralFiltering:output' );
|
|
437
|
-
this._outputTarget?.texture?.dispose();
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
reset() {
|
|
442
|
-
|
|
443
|
-
// No temporal state to reset
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Reserved-storage change (e.g. 4K toggle): recreate the MAX-preallocated StorageTextures at the new live
|
|
448
|
-
// reserved size + rebuild the compute nodes in place. Same stage object, so refs/wiring stay valid.
|
|
449
|
-
reallocateReservedStorage() {
|
|
450
|
-
|
|
451
|
-
this._computeNodeA?.dispose();
|
|
452
|
-
this._computeNodeB?.dispose();
|
|
453
|
-
this._storageTexA?.dispose();
|
|
454
|
-
this._storageTexB?.dispose();
|
|
455
|
-
const mk = () => {
|
|
456
|
-
|
|
457
|
-
const t = new StorageTexture( MAX_STORAGE_TEXTURE_SIZE, MAX_STORAGE_TEXTURE_SIZE );
|
|
458
|
-
t.type = HalfFloatType; t.format = RGBAFormat; t.minFilter = LinearFilter; t.magFilter = LinearFilter;
|
|
459
|
-
return t;
|
|
460
|
-
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
this._storageTexA = mk();
|
|
464
|
-
this._storageTexB = mk();
|
|
465
|
-
this._buildCompute();
|
|
466
|
-
this._compiled = false;
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
dispose() {
|
|
471
|
-
|
|
472
|
-
this._computeNodeA?.dispose();
|
|
473
|
-
this._computeNodeB?.dispose();
|
|
474
|
-
this._storageTexA?.dispose();
|
|
475
|
-
this._storageTexB?.dispose();
|
|
476
|
-
this._outputTarget?.dispose();
|
|
477
|
-
this._readTexNode?.dispose();
|
|
478
|
-
this._normalDepthTexNode?.dispose();
|
|
479
|
-
this._shadingNormalTexNode?.dispose();
|
|
480
|
-
this._albedoTexNode?.dispose();
|
|
481
|
-
this._varianceTexNode?.dispose();
|
|
482
|
-
this._srcAlphaTexNode?.dispose();
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
}
|