rayzee 6.5.0 → 7.0.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 +7554 -7014
- 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 +12 -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 +265 -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 +76 -20
- 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 +109 -0
- package/src/TSL/LightsSampling.js +2 -2
- package/src/TSL/PathTracerCore.js +43 -1039
- package/src/TSL/ShadeKernel.js +873 -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
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
import { Fn, wgslFn, uniform, int, uint, ivec2, uvec2, If,
|
|
2
|
-
textureLoad, textureStore, localId, workgroupId } from 'three/tsl';
|
|
3
|
-
import { RenderTarget, TextureNode, StorageTexture } from 'three/webgpu';
|
|
4
|
-
import { NearestFilter, LinearFilter, RGBAFormat, HalfFloatType, FloatType } from 'three';
|
|
5
|
-
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
6
|
-
import { ENGINE_DEFAULTS as DEFAULT_STATE } from '../EngineDefaults.js';
|
|
7
|
-
|
|
8
|
-
// ── wgslFn helpers ──────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Map temporal variance to normalised sample count with convergence logic.
|
|
12
|
-
*
|
|
13
|
-
* Uses temporal variance (frame-to-frame pixel change from Variance)
|
|
14
|
-
* as the primary convergence signal, with a small spatial variance boost for noisy
|
|
15
|
-
* neighbourhoods where the temporal EMA may underestimate.
|
|
16
|
-
*
|
|
17
|
-
* Returns vec4f(normalizedSamples, varianceRatio, converged, 1.0).
|
|
18
|
-
*/
|
|
19
|
-
const computeSamplingGuidance = /*@__PURE__*/ wgslFn( `
|
|
20
|
-
fn computeSamplingGuidance(
|
|
21
|
-
temporalVariance: f32,
|
|
22
|
-
spatialVariance: f32,
|
|
23
|
-
meanLuminance: f32,
|
|
24
|
-
threshold: f32,
|
|
25
|
-
frame: i32,
|
|
26
|
-
minFrames: i32,
|
|
27
|
-
convThreshold: f32,
|
|
28
|
-
sensitivity: f32,
|
|
29
|
-
convSpeedScale: f32
|
|
30
|
-
) -> vec4f {
|
|
31
|
-
|
|
32
|
-
// The path tracer accumulates via alpha = 1/(frame+1), so temporal variance
|
|
33
|
-
// of the accumulated output shrinks as ~sigma²/(frame+1)². Scale by (frame+1)
|
|
34
|
-
// to get accumulated image quality ~sigma²/N — decreases as image converges.
|
|
35
|
-
let frameScale = f32( frame + 1 );
|
|
36
|
-
let effectiveVariance = temporalVariance * frameScale;
|
|
37
|
-
|
|
38
|
-
// Normalize by luminance² — converts absolute variance to relative (CV²).
|
|
39
|
-
// Floor of 0.01 prevents noise amplification for near-black pixels
|
|
40
|
-
// (linear luminance < 0.1 → below perceptual visibility threshold).
|
|
41
|
-
let normFactor = max( meanLuminance * meanLuminance, 0.01 );
|
|
42
|
-
let normalizedVariance = effectiveVariance / normFactor;
|
|
43
|
-
|
|
44
|
-
let varianceRatio = clamp( normalizedVariance / threshold, 0.0, 1.0 );
|
|
45
|
-
|
|
46
|
-
// Apply sensitivity — higher values assign more samples to noisy pixels
|
|
47
|
-
var normalizedSamples = clamp( varianceRatio * sensitivity, 0.0, 1.0 );
|
|
48
|
-
|
|
49
|
-
// Small spatial boost for noisy neighbourhoods (un-scaled — provides
|
|
50
|
-
// a minor secondary signal that naturally diminishes as image converges)
|
|
51
|
-
let spatialBoost = clamp( spatialVariance / ( threshold * 4.0 ), 0.0, 0.2 );
|
|
52
|
-
normalizedSamples = clamp( normalizedSamples + spatialBoost, 0.0, 1.0 );
|
|
53
|
-
|
|
54
|
-
// Warm-up: variance estimates need a few frames to stabilise
|
|
55
|
-
if ( frame < minFrames ) {
|
|
56
|
-
|
|
57
|
-
let warmupFactor = f32( frame ) / f32( minFrames );
|
|
58
|
-
normalizedSamples = mix( 1.0, normalizedSamples, warmupFactor * warmupFactor );
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Convergence: mark pixel only when per-frame noise is truly negligible.
|
|
63
|
-
// convSpeedScale controls aggressiveness: higher = easier to converge
|
|
64
|
-
// (scales the threshold up, so more pixels qualify as converged).
|
|
65
|
-
let scaledConvThreshold = convThreshold * convSpeedScale;
|
|
66
|
-
var converged = 0.0;
|
|
67
|
-
if ( normalizedVariance < scaledConvThreshold && frame > minFrames ) {
|
|
68
|
-
|
|
69
|
-
converged = 1.0;
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return vec4f(
|
|
74
|
-
normalizedSamples,
|
|
75
|
-
varianceRatio,
|
|
76
|
-
converged,
|
|
77
|
-
1.0
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
` );
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 5-colour heatmap gradient with convergence desaturation.
|
|
85
|
-
*
|
|
86
|
-
* blue (t=0) → cyan → green → yellow → red (t=1)
|
|
87
|
-
*/
|
|
88
|
-
const heatmapGradient = /*@__PURE__*/ wgslFn( `
|
|
89
|
-
fn heatmapGradient( t: f32, normalizedVariance: f32, converged: f32 ) -> vec4f {
|
|
90
|
-
|
|
91
|
-
let r = clamp( ( t - 0.5 ) * 4.0, 0.0, 1.0 );
|
|
92
|
-
let g = clamp( t * 4.0, 0.0, 1.0 ) - clamp( ( t - 0.75 ) * 4.0, 0.0, 1.0 );
|
|
93
|
-
let b = 1.0 - clamp( ( t - 0.25 ) * 4.0, 0.0, 1.0 );
|
|
94
|
-
|
|
95
|
-
var color = vec3f( r, g, b );
|
|
96
|
-
|
|
97
|
-
// Convergence: desaturate converged pixels
|
|
98
|
-
if ( converged > 0.5 ) {
|
|
99
|
-
|
|
100
|
-
let gray = color.x * 0.299 + color.y * 0.587 + color.z * 0.114;
|
|
101
|
-
color = mix( color, vec3f( gray ), 0.6 );
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Brightness modulation
|
|
106
|
-
color *= 0.7 + normalizedVariance * 0.3;
|
|
107
|
-
|
|
108
|
-
return vec4f( color, 1.0 );
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
` );
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* WebGPU Adaptive Sampling Stage (Compute Shader)
|
|
115
|
-
*
|
|
116
|
-
* Reads per-pixel temporal variance from Variance and
|
|
117
|
-
* produces a guidance texture that tells the path tracer how many
|
|
118
|
-
* samples each pixel needs.
|
|
119
|
-
*
|
|
120
|
-
* Algorithm:
|
|
121
|
-
* 1. Read temporal + spatial variance from variance:output
|
|
122
|
-
* 2. Map temporal variance → normalised sample count (0–1)
|
|
123
|
-
* 3. Apply sensitivity scaling and spatial boost
|
|
124
|
-
* 4. Warm-up ramp for early frames (variance EMA not yet stable)
|
|
125
|
-
* 5. Mark converged pixels (temporal variance below threshold)
|
|
126
|
-
* 6. Write (normalizedSamples, varianceRatio, converged, 1) to StorageTexture
|
|
127
|
-
*
|
|
128
|
-
* Output format (RGBA HalfFloat):
|
|
129
|
-
* R — normalizedSamples (0-1, multiply by adaptiveSamplingMax)
|
|
130
|
-
* G — variance / threshold (debug / convergence weight)
|
|
131
|
-
* B — convergedFlag (1.0 = pixel converged, skip sampling)
|
|
132
|
-
* A — 1.0
|
|
133
|
-
*
|
|
134
|
-
* The path tracer reads this via getRequiredSamples() in TSL/PathTracer.js.
|
|
135
|
-
*
|
|
136
|
-
* Execution: PER_CYCLE — only updates when a full tile cycle completes,
|
|
137
|
-
* ensuring variance is computed from complete frame data.
|
|
138
|
-
*
|
|
139
|
-
* Textures published: adaptiveSampling:output
|
|
140
|
-
* Textures read: variance:output (from Variance)
|
|
141
|
-
*/
|
|
142
|
-
export class AdaptiveSampling extends RenderStage {
|
|
143
|
-
|
|
144
|
-
constructor( renderer, options = {} ) {
|
|
145
|
-
|
|
146
|
-
super( 'AdaptiveSampling', {
|
|
147
|
-
...options,
|
|
148
|
-
executionMode: StageExecutionMode.PER_CYCLE
|
|
149
|
-
} );
|
|
150
|
-
|
|
151
|
-
this.renderer = renderer;
|
|
152
|
-
this.frameNumber = 0;
|
|
153
|
-
this.delayByFrames = options.delayByFrames ?? 2;
|
|
154
|
-
this.showAdaptiveSamplingHelper = false;
|
|
155
|
-
|
|
156
|
-
// Sampling parameters
|
|
157
|
-
this.adaptiveSamplingMax = uniform( options.adaptiveSamplingMax ?? DEFAULT_STATE.adaptiveSamplingMax ?? 32, 'int' );
|
|
158
|
-
this.varianceThreshold = uniform( options.varianceThreshold ?? DEFAULT_STATE.adaptiveSamplingVarianceThreshold ?? 0.01 );
|
|
159
|
-
this.materialBias = uniform( options.materialBias ?? DEFAULT_STATE.adaptiveSamplingMaterialBias ?? 1.2 );
|
|
160
|
-
this.edgeBias = uniform( options.edgeBias ?? DEFAULT_STATE.adaptiveSamplingEdgeBias ?? 1.5 );
|
|
161
|
-
this.convergenceSpeed = uniform( options.convergenceSpeed ?? DEFAULT_STATE.adaptiveSamplingConvergenceSpeed ?? 2.0 );
|
|
162
|
-
this.frameNumberUniform = uniform( 0, 'int' );
|
|
163
|
-
|
|
164
|
-
// Resolution uniforms (int for compute pixel coords)
|
|
165
|
-
this.resolutionWidth = uniform( options.width || 1024 );
|
|
166
|
-
this.resolutionHeight = uniform( options.height || 1024 );
|
|
167
|
-
|
|
168
|
-
// Convergence parameters — temporal variance stabilises after ~10 frames (EMA alpha=0.1)
|
|
169
|
-
this.minConvergenceFrames = uniform( 10 );
|
|
170
|
-
// Must be well below varianceThreshold — convergence means "skip entirely".
|
|
171
|
-
// With (frame+1)² scaling, effective variance ≈ 5×σ², so 0.01 → σ² ≈ 0.002.
|
|
172
|
-
this.convergenceThreshold = uniform( 0.01 );
|
|
173
|
-
|
|
174
|
-
// StorageTexture for compute output (replaces RenderTarget)
|
|
175
|
-
const w = options.width || 1;
|
|
176
|
-
const h = options.height || 1;
|
|
177
|
-
|
|
178
|
-
// LinearFilter for textureLoad codegen compatibility
|
|
179
|
-
this._outputStorageTex = new StorageTexture( w, h );
|
|
180
|
-
this._outputStorageTex.type = HalfFloatType;
|
|
181
|
-
this._outputStorageTex.format = RGBAFormat;
|
|
182
|
-
this._outputStorageTex.minFilter = LinearFilter;
|
|
183
|
-
this._outputStorageTex.magFilter = LinearFilter;
|
|
184
|
-
|
|
185
|
-
// Heatmap StorageTexture for compute output
|
|
186
|
-
this._heatmapStorageTex = new StorageTexture( w, h );
|
|
187
|
-
this._heatmapStorageTex.type = FloatType;
|
|
188
|
-
this._heatmapStorageTex.format = RGBAFormat;
|
|
189
|
-
this._heatmapStorageTex.minFilter = NearestFilter;
|
|
190
|
-
this._heatmapStorageTex.magFilter = NearestFilter;
|
|
191
|
-
|
|
192
|
-
// Heatmap render target — FloatType, exposed as a public field for hosts to
|
|
193
|
-
// display via their own readback helper.
|
|
194
|
-
this.heatmapTarget = new RenderTarget( w, h, {
|
|
195
|
-
format: RGBAFormat,
|
|
196
|
-
type: FloatType,
|
|
197
|
-
minFilter: NearestFilter,
|
|
198
|
-
magFilter: NearestFilter,
|
|
199
|
-
depthBuffer: false,
|
|
200
|
-
stencilBuffer: false
|
|
201
|
-
} );
|
|
202
|
-
|
|
203
|
-
// Dispatch dimensions
|
|
204
|
-
this._dispatchX = Math.ceil( w / 8 );
|
|
205
|
-
this._dispatchY = Math.ceil( h / 8 );
|
|
206
|
-
|
|
207
|
-
// Input: variance texture from Variance
|
|
208
|
-
// Use regular TextureNode (not StorageTexture) as compile-time placeholder so
|
|
209
|
-
// textureLoad codegen includes the required level parameter for texture_2d
|
|
210
|
-
this._varianceTexNode = new TextureNode();
|
|
211
|
-
|
|
212
|
-
// Build compute + heatmap shaders
|
|
213
|
-
this._buildCompute();
|
|
214
|
-
this._buildHeatmapCompute();
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
setupEventListeners() {
|
|
219
|
-
|
|
220
|
-
this.on( 'pathtracer:viewpointChanged', () => this.reset() );
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Build compute shader that maps variance → sampling guidance.
|
|
226
|
-
*
|
|
227
|
-
* Reads per-pixel temporal and spatial variance from Variance
|
|
228
|
-
* output and maps it to a normalised sample count. No shared memory needed —
|
|
229
|
-
* each thread processes one pixel independently.
|
|
230
|
-
*
|
|
231
|
-
* Workgroup: [8,8,1] — 64 threads per workgroup
|
|
232
|
-
*/
|
|
233
|
-
_buildCompute() {
|
|
234
|
-
|
|
235
|
-
const varianceTex = this._varianceTexNode;
|
|
236
|
-
const threshold = this.varianceThreshold;
|
|
237
|
-
const sensitivity = this.materialBias; // "Sensitivity": higher = more samples for noisy pixels
|
|
238
|
-
const convSpeedScale = this.convergenceSpeed; // "Convergence Speed": scales convergence threshold
|
|
239
|
-
const frame = this.frameNumberUniform;
|
|
240
|
-
const minFrames = this.minConvergenceFrames;
|
|
241
|
-
const convThreshold = this.convergenceThreshold;
|
|
242
|
-
const resW = this.resolutionWidth;
|
|
243
|
-
const resH = this.resolutionHeight;
|
|
244
|
-
const outputTex = this._outputStorageTex;
|
|
245
|
-
|
|
246
|
-
const WG_SIZE = 8;
|
|
247
|
-
|
|
248
|
-
const computeFn = Fn( () => {
|
|
249
|
-
|
|
250
|
-
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
251
|
-
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
252
|
-
|
|
253
|
-
If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
|
|
254
|
-
|
|
255
|
-
// Variance texture: R=mean, G=meanSq, B=temporalVariance, A=spatialVariance
|
|
256
|
-
const varianceData = textureLoad( varianceTex, ivec2( gx, gy ) );
|
|
257
|
-
|
|
258
|
-
const result = computeSamplingGuidance(
|
|
259
|
-
varianceData.z, // temporal variance
|
|
260
|
-
varianceData.w, // spatial variance
|
|
261
|
-
varianceData.x, // mean luminance (for HDR normalization)
|
|
262
|
-
threshold,
|
|
263
|
-
int( frame ),
|
|
264
|
-
int( minFrames ),
|
|
265
|
-
convThreshold,
|
|
266
|
-
sensitivity,
|
|
267
|
-
convSpeedScale
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
textureStore(
|
|
271
|
-
outputTex,
|
|
272
|
-
uvec2( uint( gx ), uint( gy ) ),
|
|
273
|
-
result
|
|
274
|
-
).toWriteOnly();
|
|
275
|
-
|
|
276
|
-
} );
|
|
277
|
-
|
|
278
|
-
} );
|
|
279
|
-
|
|
280
|
-
this._computeNode = computeFn().compute(
|
|
281
|
-
[ this._dispatchX, this._dispatchY, 1 ],
|
|
282
|
-
[ WG_SIZE, WG_SIZE, 1 ]
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Build heatmap visualization compute shader.
|
|
289
|
-
*
|
|
290
|
-
* Reads the sampling guidance StorageTexture via textureLoad and maps
|
|
291
|
-
* normalizedSamples to a smooth blue→cyan→green→yellow→red gradient.
|
|
292
|
-
* Converged pixels are desaturated, brightness is modulated by variance.
|
|
293
|
-
* Writes to _heatmapStorageTex, then copied to the public heatmapTarget
|
|
294
|
-
* RenderTarget so the host can display it.
|
|
295
|
-
*/
|
|
296
|
-
_buildHeatmapCompute() {
|
|
297
|
-
|
|
298
|
-
const samplingTex = this._outputStorageTex;
|
|
299
|
-
const heatmapOut = this._heatmapStorageTex;
|
|
300
|
-
const resW = this.resolutionWidth;
|
|
301
|
-
const resH = this.resolutionHeight;
|
|
302
|
-
|
|
303
|
-
const WG_SIZE = 8;
|
|
304
|
-
|
|
305
|
-
const computeFn = Fn( () => {
|
|
306
|
-
|
|
307
|
-
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
308
|
-
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
309
|
-
|
|
310
|
-
If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
|
|
311
|
-
|
|
312
|
-
const data = textureLoad( samplingTex, ivec2( gx, gy ) );
|
|
313
|
-
const result = heatmapGradient( data.x.clamp( 0.0, 1.0 ), data.y, data.z );
|
|
314
|
-
|
|
315
|
-
textureStore(
|
|
316
|
-
heatmapOut,
|
|
317
|
-
uvec2( uint( gx ), uint( gy ) ),
|
|
318
|
-
result
|
|
319
|
-
).toWriteOnly();
|
|
320
|
-
|
|
321
|
-
} );
|
|
322
|
-
|
|
323
|
-
} );
|
|
324
|
-
|
|
325
|
-
this._heatmapComputeNode = computeFn().compute(
|
|
326
|
-
[ this._dispatchX, this._dispatchY, 1 ],
|
|
327
|
-
[ WG_SIZE, WG_SIZE, 1 ]
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Enable or disable the heatmap compute pass. When enabled, the heatmap is
|
|
334
|
-
* rendered each frame to {@link this.heatmapTarget} (a public RenderTarget)
|
|
335
|
-
* for the host to display however it wants.
|
|
336
|
-
*/
|
|
337
|
-
setHeatmapEnabled( enabled ) {
|
|
338
|
-
|
|
339
|
-
this.showAdaptiveSamplingHelper = enabled;
|
|
340
|
-
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
render( context ) {
|
|
344
|
-
|
|
345
|
-
if ( ! this.enabled ) return;
|
|
346
|
-
|
|
347
|
-
// Delay a few frames to let the path tracer accumulate
|
|
348
|
-
this.frameNumber ++;
|
|
349
|
-
if ( this.frameNumber <= this.delayByFrames ) return;
|
|
350
|
-
|
|
351
|
-
this.frameNumberUniform.value = this.frameNumber;
|
|
352
|
-
|
|
353
|
-
// Get temporal/spatial variance from Variance
|
|
354
|
-
const varianceTexture = context.getTexture( 'variance:output' );
|
|
355
|
-
if ( ! varianceTexture ) return;
|
|
356
|
-
|
|
357
|
-
// Auto-match storage texture size to variance output
|
|
358
|
-
const img = varianceTexture.image;
|
|
359
|
-
if ( img && img.width > 0 && img.height > 0 &&
|
|
360
|
-
( img.width !== this._outputStorageTex.image.width ||
|
|
361
|
-
img.height !== this._outputStorageTex.image.height ) ) {
|
|
362
|
-
|
|
363
|
-
this.setSize( img.width, img.height );
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Update input texture (no shader recompile, just swap value)
|
|
368
|
-
this._varianceTexNode.value = varianceTexture;
|
|
369
|
-
|
|
370
|
-
// Compute dispatch — map variance → sampling guidance
|
|
371
|
-
this.renderer.compute( this._computeNode );
|
|
372
|
-
|
|
373
|
-
// Publish guidance texture for PathTracer to consume
|
|
374
|
-
// (StorageTexture extends Texture, works as regular texture for sampling)
|
|
375
|
-
context.setTexture( 'adaptiveSampling:output', this._outputStorageTex );
|
|
376
|
-
|
|
377
|
-
// Render heatmap into public heatmapTarget when enabled
|
|
378
|
-
if ( this.showAdaptiveSamplingHelper ) {
|
|
379
|
-
|
|
380
|
-
this.renderer.compute( this._heatmapComputeNode );
|
|
381
|
-
this.renderer.copyTextureToTexture( this._heatmapStorageTex, this.heatmapTarget.texture );
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
reset() {
|
|
388
|
-
|
|
389
|
-
this.frameNumber = 0;
|
|
390
|
-
this.frameNumberUniform.value = 0;
|
|
391
|
-
|
|
392
|
-
// Remove stale guidance from context so PathTracer (which runs
|
|
393
|
-
// before us) doesn't read converged-pixel data from the old viewpoint
|
|
394
|
-
// during the delay frames before we publish fresh guidance.
|
|
395
|
-
if ( this.context ) this.context.removeTexture( 'adaptiveSampling:output' );
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
setSize( width, height ) {
|
|
400
|
-
|
|
401
|
-
this._outputStorageTex.setSize( width, height );
|
|
402
|
-
this._heatmapStorageTex.setSize( width, height );
|
|
403
|
-
this.heatmapTarget.setSize( width, height );
|
|
404
|
-
this.heatmapTarget.texture.needsUpdate = true;
|
|
405
|
-
this.resolutionWidth.value = width;
|
|
406
|
-
this.resolutionHeight.value = height;
|
|
407
|
-
|
|
408
|
-
// Update dispatch dimensions
|
|
409
|
-
this._dispatchX = Math.ceil( width / 8 );
|
|
410
|
-
this._dispatchY = Math.ceil( height / 8 );
|
|
411
|
-
this._computeNode.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
412
|
-
this._heatmapComputeNode.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
setAdaptiveSamplingMax( value ) {
|
|
417
|
-
|
|
418
|
-
this.adaptiveSamplingMax.value = value;
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
setVarianceThreshold( value ) {
|
|
423
|
-
|
|
424
|
-
this.varianceThreshold.value = value;
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
setMaterialBias( value ) {
|
|
429
|
-
|
|
430
|
-
this.materialBias.value = value;
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
setEdgeBias( value ) {
|
|
435
|
-
|
|
436
|
-
this.edgeBias.value = value;
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
setConvergenceSpeed( value ) {
|
|
441
|
-
|
|
442
|
-
this.convergenceSpeed.value = value;
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Unified setter for multiple adaptive sampling parameters.
|
|
448
|
-
* @param {Object} params
|
|
449
|
-
* @param {number} [params.threshold] - Variance threshold
|
|
450
|
-
* @param {number} [params.materialBias] - Material bias multiplier
|
|
451
|
-
* @param {number} [params.edgeBias] - Edge bias multiplier
|
|
452
|
-
* @param {number} [params.convergenceSpeedUp] - Convergence speed
|
|
453
|
-
* @param {number} [params.adaptiveSamplingMax] - Max samples
|
|
454
|
-
*/
|
|
455
|
-
setAdaptiveSamplingParameters( params ) {
|
|
456
|
-
|
|
457
|
-
if ( params.threshold !== undefined ) this.setVarianceThreshold( params.threshold );
|
|
458
|
-
if ( params.materialBias !== undefined ) this.setMaterialBias( params.materialBias );
|
|
459
|
-
if ( params.edgeBias !== undefined ) this.setEdgeBias( params.edgeBias );
|
|
460
|
-
if ( params.convergenceSpeedUp !== undefined ) this.setConvergenceSpeed( params.convergenceSpeedUp );
|
|
461
|
-
if ( params.adaptiveSamplingMax !== undefined ) this.setAdaptiveSamplingMax( params.adaptiveSamplingMax );
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
dispose() {
|
|
466
|
-
|
|
467
|
-
this._computeNode?.dispose();
|
|
468
|
-
this._heatmapComputeNode?.dispose();
|
|
469
|
-
this._heatmapStorageTex?.dispose();
|
|
470
|
-
this._outputStorageTex?.dispose();
|
|
471
|
-
this.heatmapTarget?.dispose();
|
|
472
|
-
this._varianceTexNode?.dispose();
|
|
473
|
-
|
|
474
|
-
this._computeNode = null;
|
|
475
|
-
this._heatmapComputeNode = null;
|
|
476
|
-
this._heatmapStorageTex = null;
|
|
477
|
-
this._outputStorageTex = null;
|
|
478
|
-
this.heatmapTarget = null;
|
|
479
|
-
this._varianceTexNode = null;
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
}
|