rayzee 5.6.0 → 5.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 +3 -0
- package/dist/assets/CDFWorker-BFQUr3By.js +2 -0
- package/dist/assets/CDFWorker-BFQUr3By.js.map +1 -0
- package/dist/rayzee.es.js +965 -903
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +49 -43
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineEvents.js +3 -0
- package/src/Passes/AIUpscaler.js +22 -0
- package/src/Passes/OIDNDenoiser.js +93 -28
- package/src/PathTracerApp.js +17 -0
- package/src/Pipeline/RenderPipeline.js +3 -0
- package/src/Processor/EquirectHDRInfo.js +76 -16
- package/src/Processor/ShaderBuilder.js +1 -0
- package/src/Processor/Workers/CDFWorker.js +72 -11
- package/src/Stages/ASVGF.js +18 -0
- package/src/Stages/AdaptiveSampling.js +2 -0
- package/src/Stages/AutoExposure.js +2 -0
- package/src/Stages/BilateralFilter.js +2 -0
- package/src/Stages/Display.js +1 -0
- package/src/Stages/EdgeFilter.js +1 -0
- package/src/Stages/MotionVector.js +1 -0
- package/src/Stages/SSRC.js +6 -0
- package/src/Stages/Variance.js +3 -0
- package/src/TSL/Common.js +9 -0
- package/src/TSL/Environment.js +15 -6
- package/src/TSL/LightsIndirect.js +1 -1
- package/src/TSL/LightsSampling.js +5 -4
- package/src/TSL/PathTracer.js +2 -2
- package/src/TSL/PathTracerCore.js +6 -5
- package/src/managers/DenoisingManager.js +68 -14
- package/src/managers/EnvironmentManager.js +17 -0
- package/src/managers/TransformManager.js +9 -0
- package/src/managers/UniformManager.js +1 -0
- package/dist/assets/CDFWorker-2MoynL4F.js +0 -2
- package/dist/assets/CDFWorker-2MoynL4F.js.map +0 -1
package/src/Stages/EdgeFilter.js
CHANGED
package/src/Stages/SSRC.js
CHANGED
|
@@ -194,6 +194,12 @@ export class SSRC extends RenderStage {
|
|
|
194
194
|
this._prevNDTexA.dispose();
|
|
195
195
|
this._prevNDTexB.dispose();
|
|
196
196
|
this._outputTex.dispose();
|
|
197
|
+
this._colorTexNode?.dispose();
|
|
198
|
+
this._ndTexNode?.dispose();
|
|
199
|
+
this._motionTexNode?.dispose();
|
|
200
|
+
this._readCacheTexNode?.dispose();
|
|
201
|
+
this._readPrevNDTexNode?.dispose();
|
|
202
|
+
this._readPass1CacheTexNode?.dispose();
|
|
197
203
|
|
|
198
204
|
}
|
|
199
205
|
|
package/src/Stages/Variance.js
CHANGED
|
@@ -371,6 +371,9 @@ export class Variance extends RenderStage {
|
|
|
371
371
|
this._computeNodeB?.dispose();
|
|
372
372
|
this._storageTexA?.dispose();
|
|
373
373
|
this._storageTexB?.dispose();
|
|
374
|
+
this._colorTexNode?.dispose();
|
|
375
|
+
this._readTexNodeA?.dispose();
|
|
376
|
+
this._readTexNodeB?.dispose();
|
|
374
377
|
|
|
375
378
|
}
|
|
376
379
|
|
package/src/TSL/Common.js
CHANGED
|
@@ -99,6 +99,15 @@ export const powerHeuristic = wgslFn( `
|
|
|
99
99
|
}
|
|
100
100
|
` );
|
|
101
101
|
|
|
102
|
+
// Balance heuristic — optimal for MIS-compensated env map sampling (Karlík et al. 2019)
|
|
103
|
+
export const balanceHeuristic = wgslFn( `
|
|
104
|
+
fn balanceHeuristic( pdf1: f32, pdf2: f32 ) -> f32 {
|
|
105
|
+
|
|
106
|
+
return pdf1 / max( pdf1 + pdf2, ${MIN_PDF} );
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
` );
|
|
110
|
+
|
|
102
111
|
// Bayer matrix 4x4 dithering — exact port of GLSL
|
|
103
112
|
export const applyDithering = wgslFn( `
|
|
104
113
|
fn applyDithering( color: vec3f, uv: vec2f, ditheringAmount: f32, resolution: vec2f ) -> vec3f {
|
package/src/TSL/Environment.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fn, wgslFn, vec2, vec4, float, int, If, texture, sampler, dot, floor, fract, min, mix, clamp } from 'three/tsl';
|
|
1
|
+
import { Fn, wgslFn, vec2, vec4, float, int, If, texture, sampler, dot, sin, floor, fract, min, max, mix, clamp } from 'three/tsl';
|
|
2
2
|
|
|
3
3
|
import { REC709_LUMINANCE_COEFFICIENTS } from './Common.js';
|
|
4
4
|
|
|
@@ -47,9 +47,9 @@ export const equirectDirectionPdf = /*@__PURE__*/ wgslFn( `
|
|
|
47
47
|
`, [ equirectDirectionToUv ] );
|
|
48
48
|
|
|
49
49
|
// Evaluate PDF for a given direction (for MIS)
|
|
50
|
-
// Exact implementation from three-gpu-pathtracer
|
|
51
50
|
// Returns vec4(color.rgb, pdf) since TSL cannot use inout params
|
|
52
|
-
|
|
51
|
+
// Uses MIS-compensated PDF (Karlík et al. 2019): max(0, lum - delta) / compensatedTotalSum
|
|
52
|
+
export const sampleEquirect = Fn( ( [ environment, direction, environmentMatrix, envTotalSum, envCompensationDelta, envResolution ] ) => {
|
|
53
53
|
|
|
54
54
|
const result = vec4( 0.0 ).toVar();
|
|
55
55
|
|
|
@@ -63,8 +63,13 @@ export const sampleEquirect = Fn( ( [ environment, direction, environmentMatrix,
|
|
|
63
63
|
const uv = equirectDirectionToUv( { direction, environmentMatrix } ).toVar();
|
|
64
64
|
const color = texture( environment, uv, 0 ).rgb.toVar();
|
|
65
65
|
|
|
66
|
+
// sin(theta) matches the CDF's solid-angle weighting (lum * sinTheta)
|
|
67
|
+
const sinTheta = sin( uv.y.mul( Math.PI ) ).toVar();
|
|
66
68
|
const lum = dot( color, REC709_LUMINANCE_COEFFICIENTS ).toVar();
|
|
67
|
-
const
|
|
69
|
+
const weightedLum = lum.mul( sinTheta ).toVar();
|
|
70
|
+
// MIS Compensation: subtract delta to match the sharpened CDF
|
|
71
|
+
const compensatedWeight = max( float( 0.0 ), weightedLum.sub( envCompensationDelta ) ).toVar();
|
|
72
|
+
const pdf = compensatedWeight.div( envTotalSum ).toVar();
|
|
68
73
|
|
|
69
74
|
const dirPdf = equirectDirectionPdf( { direction, environmentMatrix } ).toVar();
|
|
70
75
|
const finalPdf = float( envResolution.x ).mul( float( envResolution.y ) ).mul( pdf ).mul( dirPdf ).toVar();
|
|
@@ -86,6 +91,7 @@ export const sampleEquirectProbability = Fn( ( [
|
|
|
86
91
|
environmentMatrix,
|
|
87
92
|
environmentIntensity,
|
|
88
93
|
envTotalSum,
|
|
94
|
+
envCompensationDelta,
|
|
89
95
|
envResolution,
|
|
90
96
|
r,
|
|
91
97
|
colorOutput
|
|
@@ -132,9 +138,12 @@ export const sampleEquirectProbability = Fn( ( [
|
|
|
132
138
|
// Write color to output parameter (avoids redundant CDF texture lookups)
|
|
133
139
|
colorOutput.assign( color );
|
|
134
140
|
|
|
135
|
-
// Calculate PDF
|
|
141
|
+
// Calculate PDF — sin(theta) weighting + MIS Compensation (Karlík et al. 2019)
|
|
142
|
+
const sinTheta = sin( uv.y.mul( Math.PI ) ).toVar();
|
|
136
143
|
const lum = dot( color.div( environmentIntensity ), REC709_LUMINANCE_COEFFICIENTS ).toVar();
|
|
137
|
-
const
|
|
144
|
+
const weightedLum = lum.mul( sinTheta ).toVar();
|
|
145
|
+
const compensatedWeight = max( float( 0.0 ), weightedLum.sub( envCompensationDelta ) ).toVar();
|
|
146
|
+
const pdf = compensatedWeight.div( envTotalSum ).toVar();
|
|
138
147
|
|
|
139
148
|
const dirPdf = equirectDirectionPdf( { direction, environmentMatrix } ).toVar();
|
|
140
149
|
const finalPdf = float( envResolution.x ).mul( float( envResolution.y ) ).mul( pdf ).mul( dirPdf ).toVar();
|
|
@@ -273,7 +273,7 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
273
273
|
samplingInfo,
|
|
274
274
|
// Environment resources
|
|
275
275
|
envTexture, environmentIntensity, envMatrix,
|
|
276
|
-
envTotalSum, envResolution,
|
|
276
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
277
277
|
enableEnvironmentLight, useEnvMapIS,
|
|
278
278
|
] ) => {
|
|
279
279
|
|
|
@@ -84,6 +84,7 @@ import {
|
|
|
84
84
|
EPSILON,
|
|
85
85
|
MIN_PDF,
|
|
86
86
|
powerHeuristic,
|
|
87
|
+
balanceHeuristic,
|
|
87
88
|
} from './Common.js';
|
|
88
89
|
import {
|
|
89
90
|
sampleEquirectProbability,
|
|
@@ -935,7 +936,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
935
936
|
// Environment resources
|
|
936
937
|
envTexture, environmentIntensity, envMatrix,
|
|
937
938
|
envCDFBuffer,
|
|
938
|
-
envTotalSum, envResolution,
|
|
939
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
939
940
|
enableEnvironmentLight,
|
|
940
941
|
] ) => {
|
|
941
942
|
|
|
@@ -1204,7 +1205,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1204
1205
|
// Sample direction + PDF + color from importance-sampled environment
|
|
1205
1206
|
const envSampleResult = sampleEquirectProbability(
|
|
1206
1207
|
envTexture, envCDFBuffer,
|
|
1207
|
-
envMatrix, environmentIntensity, envTotalSum, envResolution, envRandom, envColor
|
|
1208
|
+
envMatrix, environmentIntensity, envTotalSum, envCompensationDelta, envResolution, envRandom, envColor
|
|
1208
1209
|
).toVar();
|
|
1209
1210
|
|
|
1210
1211
|
const envDirection = envSampleResult.xyz.toVar();
|
|
@@ -1229,11 +1230,11 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1229
1230
|
const brdfValue = evaluateMaterialResponse( viewDir, envDirection, hitNormal, material );
|
|
1230
1231
|
const bPdf = calculateMaterialPDF( viewDir, envDirection, hitNormal, material ).toVar();
|
|
1231
1232
|
|
|
1232
|
-
//
|
|
1233
|
+
// Balance heuristic for env MIS — optimal for MIS-compensated PDFs (Karlík et al. 2019).
|
|
1233
1234
|
// The implicit path uses material combinedPdf as prevBouncePdf at the miss check.
|
|
1234
1235
|
const misW = select(
|
|
1235
1236
|
bPdf.greaterThan( 0.0 ),
|
|
1236
|
-
|
|
1237
|
+
balanceHeuristic( { pdf1: envPdf, pdf2: bPdf } ),
|
|
1237
1238
|
float( 1.0 )
|
|
1238
1239
|
).toVar();
|
|
1239
1240
|
|
package/src/TSL/PathTracer.js
CHANGED
|
@@ -138,7 +138,7 @@ export const pathTracerMain = ( params ) => {
|
|
|
138
138
|
spotLightsBuffer, numSpotLights,
|
|
139
139
|
envTexture, environmentIntensity, envMatrix,
|
|
140
140
|
envCDFBuffer,
|
|
141
|
-
envTotalSum, envResolution,
|
|
141
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
142
142
|
enableEnvironmentLight, useEnvMapIS,
|
|
143
143
|
maxBounceCount, transmissiveBounces,
|
|
144
144
|
showBackground, transparentBackground, backgroundIntensity,
|
|
@@ -285,7 +285,7 @@ export const pathTracerMain = ( params ) => {
|
|
|
285
285
|
spotLightsBuffer, numSpotLights,
|
|
286
286
|
envTexture, environmentIntensity, envMatrix,
|
|
287
287
|
envCDFBuffer,
|
|
288
|
-
envTotalSum, envResolution,
|
|
288
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
289
289
|
enableEnvironmentLight, useEnvMapIS,
|
|
290
290
|
maxBounceCount, transmissiveBounces,
|
|
291
291
|
backgroundIntensity, showBackground, transparentBackground,
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
applySoftSuppressionRGB,
|
|
57
57
|
getMaterial,
|
|
58
58
|
powerHeuristic,
|
|
59
|
+
balanceHeuristic,
|
|
59
60
|
} from './Common.js';
|
|
60
61
|
import {
|
|
61
62
|
DirectionSample,
|
|
@@ -582,7 +583,7 @@ export const Trace = Fn( ( [
|
|
|
582
583
|
// Environment
|
|
583
584
|
envTexture, environmentIntensity, envMatrix,
|
|
584
585
|
envCDFBuffer,
|
|
585
|
-
envTotalSum, envResolution,
|
|
586
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
586
587
|
enableEnvironmentLight, useEnvMapIS,
|
|
587
588
|
// Rendering parameters
|
|
588
589
|
maxBounceCount, transmissiveBounces,
|
|
@@ -703,12 +704,12 @@ export const Trace = Fn( ( [
|
|
|
703
704
|
If( prevBouncePdf.greaterThan( 0.0 ).and( enableEnvironmentLight ).and( useEnvMapIS ), () => {
|
|
704
705
|
|
|
705
706
|
const envEval = sampleEquirect(
|
|
706
|
-
envTexture, rayDirection, envMatrix, envTotalSum, envResolution,
|
|
707
|
+
envTexture, rayDirection, envMatrix, envTotalSum, envCompensationDelta, envResolution,
|
|
707
708
|
);
|
|
708
709
|
const envPdf = envEval.w.toVar();
|
|
709
710
|
If( envPdf.greaterThan( 0.0 ), () => {
|
|
710
711
|
|
|
711
|
-
envMisWeight.assign(
|
|
712
|
+
envMisWeight.assign( balanceHeuristic( { pdf1: prevBouncePdf, pdf2: envPdf } ) );
|
|
712
713
|
|
|
713
714
|
} );
|
|
714
715
|
|
|
@@ -1001,7 +1002,7 @@ export const Trace = Fn( ( [
|
|
|
1001
1002
|
materialBuffer,
|
|
1002
1003
|
envTexture, environmentIntensity, envMatrix,
|
|
1003
1004
|
envCDFBuffer,
|
|
1004
|
-
envTotalSum, envResolution,
|
|
1005
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
1005
1006
|
enableEnvironmentLight,
|
|
1006
1007
|
);
|
|
1007
1008
|
|
|
@@ -1120,7 +1121,7 @@ export const Trace = Fn( ( [
|
|
|
1120
1121
|
rngState,
|
|
1121
1122
|
samplingInfo,
|
|
1122
1123
|
envTexture, environmentIntensity, envMatrix,
|
|
1123
|
-
envTotalSum, envResolution,
|
|
1124
|
+
envTotalSum, envCompensationDelta, envResolution,
|
|
1124
1125
|
enableEnvironmentLight, useEnvMapIS,
|
|
1125
1126
|
) );
|
|
1126
1127
|
throughput.mulAssign( indirectResult.throughput );
|
|
@@ -54,6 +54,17 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
54
54
|
this._lastRenderWidth = 0;
|
|
55
55
|
this._lastRenderHeight = 0;
|
|
56
56
|
|
|
57
|
+
// Track the current completion-chain listener so it can be removed on re-trigger
|
|
58
|
+
this._pendingStartUpscaler = null;
|
|
59
|
+
|
|
60
|
+
// Bound event forwarding handlers (stored for removal on re-setup / dispose)
|
|
61
|
+
this._denoiserStartHandler = null;
|
|
62
|
+
this._denoiserEndHandler = null;
|
|
63
|
+
this._upscalerResChangedHandler = null;
|
|
64
|
+
this._upscalerStartHandler = null;
|
|
65
|
+
this._upscalerProgressHandler = null;
|
|
66
|
+
this._upscalerEndHandler = null;
|
|
67
|
+
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
_createDenoiserCanvas( mainCanvas ) {
|
|
@@ -146,11 +157,13 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
146
157
|
|
|
147
158
|
this.denoiser.enabled = DEFAULT_STATE.enableOIDN;
|
|
148
159
|
|
|
149
|
-
// Forward lifecycle events
|
|
150
|
-
this.
|
|
151
|
-
this.dispatchEvent( { type: EngineEvents.DENOISING_START } )
|
|
152
|
-
this.
|
|
153
|
-
this.dispatchEvent( { type: EngineEvents.DENOISING_END } )
|
|
160
|
+
// Forward lifecycle events (store refs for removal on re-setup / dispose)
|
|
161
|
+
this._denoiserStartHandler = () =>
|
|
162
|
+
this.dispatchEvent( { type: EngineEvents.DENOISING_START } );
|
|
163
|
+
this._denoiserEndHandler = () =>
|
|
164
|
+
this.dispatchEvent( { type: EngineEvents.DENOISING_END } );
|
|
165
|
+
this.denoiser.addEventListener( 'start', this._denoiserStartHandler );
|
|
166
|
+
this.denoiser.addEventListener( 'end', this._denoiserEndHandler );
|
|
154
167
|
|
|
155
168
|
}
|
|
156
169
|
|
|
@@ -189,15 +202,19 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
189
202
|
|
|
190
203
|
this.upscaler.enabled = DEFAULT_STATE.enableUpscaler || false;
|
|
191
204
|
|
|
192
|
-
// Forward lifecycle events
|
|
193
|
-
this.
|
|
194
|
-
this.dispatchEvent( { type: 'resolution_changed', width: e.width, height: e.height } )
|
|
195
|
-
this.
|
|
196
|
-
this.dispatchEvent( { type: EngineEvents.UPSCALING_START } )
|
|
197
|
-
this.
|
|
198
|
-
this.dispatchEvent( { type: EngineEvents.UPSCALING_PROGRESS, progress: e.progress } )
|
|
199
|
-
this.
|
|
200
|
-
this.dispatchEvent( { type: EngineEvents.UPSCALING_END } )
|
|
205
|
+
// Forward lifecycle events (store refs for removal on re-setup / dispose)
|
|
206
|
+
this._upscalerResChangedHandler = ( e ) =>
|
|
207
|
+
this.dispatchEvent( { type: 'resolution_changed', width: e.width, height: e.height } );
|
|
208
|
+
this._upscalerStartHandler = () =>
|
|
209
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_START } );
|
|
210
|
+
this._upscalerProgressHandler = ( e ) =>
|
|
211
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_PROGRESS, progress: e.progress } );
|
|
212
|
+
this._upscalerEndHandler = () =>
|
|
213
|
+
this.dispatchEvent( { type: EngineEvents.UPSCALING_END } );
|
|
214
|
+
this.upscaler.addEventListener( 'resolution_changed', this._upscalerResChangedHandler );
|
|
215
|
+
this.upscaler.addEventListener( 'start', this._upscalerStartHandler );
|
|
216
|
+
this.upscaler.addEventListener( 'progress', this._upscalerProgressHandler );
|
|
217
|
+
this.upscaler.addEventListener( 'end', this._upscalerEndHandler );
|
|
201
218
|
|
|
202
219
|
}
|
|
203
220
|
|
|
@@ -353,8 +370,23 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
353
370
|
* @param {Function} params.isStillComplete - () => boolean, guard for async race
|
|
354
371
|
* @param {import('../Pipeline/PipelineContext.js').PipelineContext} params.context
|
|
355
372
|
*/
|
|
373
|
+
_cleanupCompletionListener() {
|
|
374
|
+
|
|
375
|
+
if ( this._pendingStartUpscaler && this.denoiser ) {
|
|
376
|
+
|
|
377
|
+
this.denoiser.removeEventListener( 'end', this._pendingStartUpscaler );
|
|
378
|
+
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
this._pendingStartUpscaler = null;
|
|
382
|
+
|
|
383
|
+
}
|
|
384
|
+
|
|
356
385
|
onRenderComplete( { isStillComplete, context } ) {
|
|
357
386
|
|
|
387
|
+
// Remove any stale completion-chain listener from a previous render cycle
|
|
388
|
+
this._cleanupCompletionListener();
|
|
389
|
+
|
|
358
390
|
// Show post-process canvas if any post-process is enabled
|
|
359
391
|
if ( ( this.denoiser?.enabled || this.upscaler?.enabled ) && this.denoiserCanvas ) {
|
|
360
392
|
|
|
@@ -365,6 +397,8 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
365
397
|
// Chain: denoise first (if enabled), then upscale (if enabled)
|
|
366
398
|
const startUpscaler = () => {
|
|
367
399
|
|
|
400
|
+
this._pendingStartUpscaler = null;
|
|
401
|
+
|
|
368
402
|
if ( ! isStillComplete() ) return;
|
|
369
403
|
|
|
370
404
|
if ( this.upscaler?.enabled ) {
|
|
@@ -377,6 +411,7 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
377
411
|
|
|
378
412
|
if ( this.denoiser?.enabled ) {
|
|
379
413
|
|
|
414
|
+
this._pendingStartUpscaler = startUpscaler;
|
|
380
415
|
this.denoiser.addEventListener( 'end', startUpscaler, { once: true } );
|
|
381
416
|
this.denoiser.start();
|
|
382
417
|
|
|
@@ -401,6 +436,9 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
401
436
|
*/
|
|
402
437
|
abort( mainCanvas ) {
|
|
403
438
|
|
|
439
|
+
// Remove stale completion-chain listener before aborting
|
|
440
|
+
this._cleanupCompletionListener();
|
|
441
|
+
|
|
404
442
|
if ( mainCanvas ) mainCanvas.style.opacity = '1';
|
|
405
443
|
|
|
406
444
|
if ( this.upscaler ) this.upscaler.abort();
|
|
@@ -416,8 +454,13 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
416
454
|
|
|
417
455
|
dispose() {
|
|
418
456
|
|
|
457
|
+
// Remove pending completion-chain listener
|
|
458
|
+
this._cleanupCompletionListener();
|
|
459
|
+
|
|
419
460
|
if ( this.denoiser ) {
|
|
420
461
|
|
|
462
|
+
if ( this._denoiserStartHandler ) this.denoiser.removeEventListener( 'start', this._denoiserStartHandler );
|
|
463
|
+
if ( this._denoiserEndHandler ) this.denoiser.removeEventListener( 'end', this._denoiserEndHandler );
|
|
421
464
|
this.denoiser.dispose();
|
|
422
465
|
this.denoiser = null;
|
|
423
466
|
|
|
@@ -425,11 +468,22 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
425
468
|
|
|
426
469
|
if ( this.upscaler ) {
|
|
427
470
|
|
|
471
|
+
if ( this._upscalerResChangedHandler ) this.upscaler.removeEventListener( 'resolution_changed', this._upscalerResChangedHandler );
|
|
472
|
+
if ( this._upscalerStartHandler ) this.upscaler.removeEventListener( 'start', this._upscalerStartHandler );
|
|
473
|
+
if ( this._upscalerProgressHandler ) this.upscaler.removeEventListener( 'progress', this._upscalerProgressHandler );
|
|
474
|
+
if ( this._upscalerEndHandler ) this.upscaler.removeEventListener( 'end', this._upscalerEndHandler );
|
|
428
475
|
this.upscaler.dispose();
|
|
429
476
|
this.upscaler = null;
|
|
430
477
|
|
|
431
478
|
}
|
|
432
479
|
|
|
480
|
+
this._denoiserStartHandler = null;
|
|
481
|
+
this._denoiserEndHandler = null;
|
|
482
|
+
this._upscalerResChangedHandler = null;
|
|
483
|
+
this._upscalerStartHandler = null;
|
|
484
|
+
this._upscalerProgressHandler = null;
|
|
485
|
+
this._upscalerEndHandler = null;
|
|
486
|
+
|
|
433
487
|
if ( this.denoiserCanvas?.parentNode ) {
|
|
434
488
|
|
|
435
489
|
this.denoiserCanvas.parentNode.removeChild( this.denoiserCanvas );
|
|
@@ -280,6 +280,7 @@ export class EnvironmentManager {
|
|
|
280
280
|
|
|
281
281
|
this._updateCDFStorageBuffers();
|
|
282
282
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
283
|
+
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
283
284
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
284
285
|
return;
|
|
285
286
|
|
|
@@ -294,6 +295,7 @@ export class EnvironmentManager {
|
|
|
294
295
|
|
|
295
296
|
this._updateCDFStorageBuffers();
|
|
296
297
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
298
|
+
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
297
299
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
298
300
|
return;
|
|
299
301
|
|
|
@@ -313,6 +315,7 @@ export class EnvironmentManager {
|
|
|
313
315
|
|
|
314
316
|
this._updateCDFStorageBuffers();
|
|
315
317
|
this.uniforms.set( 'envTotalSum', this.equirectHdrInfo.totalSum );
|
|
318
|
+
this.uniforms.set( 'envCompensationDelta', this.equirectHdrInfo.compensationDelta );
|
|
316
319
|
this.uniforms.set( 'useEnvMapIS', 1 );
|
|
317
320
|
|
|
318
321
|
const { width, height } = this.equirectHdrInfo;
|
|
@@ -329,6 +332,7 @@ export class EnvironmentManager {
|
|
|
329
332
|
console.error( 'Error building environment CDF:', error );
|
|
330
333
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
331
334
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
335
|
+
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
332
336
|
|
|
333
337
|
}
|
|
334
338
|
|
|
@@ -375,6 +379,7 @@ export class EnvironmentManager {
|
|
|
375
379
|
|
|
376
380
|
this._updateCDFStorageBuffers();
|
|
377
381
|
this.uniforms.set( 'envTotalSum', 0.0 );
|
|
382
|
+
this.uniforms.set( 'envCompensationDelta', 0.0 );
|
|
378
383
|
this.uniforms.set( 'useEnvMapIS', 0 );
|
|
379
384
|
|
|
380
385
|
}
|
|
@@ -537,11 +542,23 @@ export class EnvironmentManager {
|
|
|
537
542
|
|
|
538
543
|
this.proceduralSkyRenderer = null;
|
|
539
544
|
this.simpleSkyRenderer = null;
|
|
545
|
+
|
|
546
|
+
this.envCDFStorageAttr?.dispose?.();
|
|
540
547
|
this.envCDFStorageAttr = null;
|
|
541
548
|
this.envCDFStorageNode = null;
|
|
549
|
+
|
|
550
|
+
// Dispose the HDRI environment texture unless it's the shared placeholder
|
|
551
|
+
// (the placeholder is handled separately just below).
|
|
552
|
+
if ( this.environmentTexture && this.environmentTexture !== this._envPlaceholder ) {
|
|
553
|
+
|
|
554
|
+
this.environmentTexture.dispose?.();
|
|
555
|
+
|
|
556
|
+
}
|
|
557
|
+
|
|
542
558
|
this._envPlaceholder?.dispose();
|
|
543
559
|
this._envPlaceholder = null;
|
|
544
560
|
this.environmentTexture = null;
|
|
561
|
+
this._previousHDRI = null;
|
|
545
562
|
|
|
546
563
|
}
|
|
547
564
|
|
|
@@ -437,6 +437,15 @@ export class TransformManager {
|
|
|
437
437
|
this._normalCache = null;
|
|
438
438
|
this._baselineComputed = false;
|
|
439
439
|
|
|
440
|
+
// Drop back-references to the owning app and shared resources so the
|
|
441
|
+
// PathTracerApp graph can be GC'd. Without this, _app pinned the entire
|
|
442
|
+
// engine (verified via heap snapshot retainer chain).
|
|
443
|
+
this._app = null;
|
|
444
|
+
this._orbitControls = null;
|
|
445
|
+
this._camera = null;
|
|
446
|
+
this._controls = null;
|
|
447
|
+
this._gizmoScene = null;
|
|
448
|
+
|
|
440
449
|
}
|
|
441
450
|
|
|
442
451
|
}
|
|
@@ -182,6 +182,7 @@ export class UniformManager {
|
|
|
182
182
|
u( 'environmentMatrix', new Matrix4(), 'mat4' );
|
|
183
183
|
ub( 'useEnvMapIS', DEFAULT_STATE.useImportanceSampledEnvironment );
|
|
184
184
|
u( 'envTotalSum', 0.0, 'float' );
|
|
185
|
+
u( 'envCompensationDelta', 0.0, 'float' );
|
|
185
186
|
u( 'envResolution', new Vector2( 1, 1 ), 'vec2' );
|
|
186
187
|
|
|
187
188
|
// Sun parameters
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
(function(){function e(e,t,n,r){let i=n,a=n+r-1;for(;i<a;){let n=i+a>>1;e[n]<t?i=n+1:a=n}return i-n}function t(t,n,r){let i=new Float32Array(n*r),a=new Float32Array(r),o=0,s=0;for(let e=0;e<r;e++){let r=0;for(let a=0;a<n;a++){let s=e*n+a,c=t[4*s],l=t[4*s+1],u=t[4*s+2],d=.2126*c+.7152*l+.0722*u;r+=d,o+=d,i[s]=r}if(r!==0)for(let t=e*n,a=e*n+n;t<a;t++)i[t]/=r;s+=r,a[e]=s}if(s!==0)for(let e=0,t=a.length;e<t;e++)a[e]/=s;let c=new Float32Array(r);for(let t=0;t<r;t++)c[t]=(e(a,(t+1)/r,0,r)+.5)/r;let l=new Float32Array(n*r);for(let t=0;t<r;t++)for(let r=0;r<n;r++){let a=t*n+r;l[a]=(e(i,(r+1)/n,t*n,n)+.5)/n}return{marginalData:c,conditionalData:l,totalSum:o}}self.onmessage=function(e){let{floatData:n,width:r,height:i}=e.data;try{let e=t(n,r,i);self.postMessage({marginalData:e.marginalData,conditionalData:e.conditionalData,totalSum:e.totalSum,width:r,height:i},[e.marginalData.buffer,e.conditionalData.buffer])}catch(e){self.postMessage({error:e.message})}}})();
|
|
2
|
-
//# sourceMappingURL=CDFWorker-2MoynL4F.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CDFWorker-2MoynL4F.js","names":[],"sources":["../../src/Processor/Workers/CDFWorker.js"],"sourcesContent":["/**\n * Web Worker for computing environment map CDF (Cumulative Distribution Function)\n * for importance sampling. Pure math — no Three.js dependencies.\n *\n * Input: { floatData: Float32Array, width, height }\n * Output: { marginalData: Float32Array, conditionalData: Float32Array, totalSum, width, height }\n */\n\nfunction binarySearchFindClosestIndexOf( array, targetValue, offset, count ) {\n\n\tlet lower = offset;\n\tlet upper = offset + count - 1;\n\n\twhile ( lower < upper ) {\n\n\t\tconst mid = ( lower + upper ) >> 1;\n\n\t\tif ( array[ mid ] < targetValue ) {\n\n\t\t\tlower = mid + 1;\n\n\t\t} else {\n\n\t\t\tupper = mid;\n\n\t\t}\n\n\t}\n\n\treturn lower - offset;\n\n}\n\nfunction buildCDF( floatData, width, height ) {\n\n\tconst cdfConditional = new Float32Array( width * height );\n\tconst cdfMarginal = new Float32Array( height );\n\n\tlet totalSumValue = 0.0;\n\tlet cumulativeWeightMarginal = 0.0;\n\n\t// Build conditional CDFs (per-row distribution)\n\tfor ( let y = 0; y < height; y ++ ) {\n\n\t\tlet cumulativeRowWeight = 0.0;\n\t\tfor ( let x = 0; x < width; x ++ ) {\n\n\t\t\tconst i = y * width + x;\n\t\t\tconst r = floatData[ 4 * i ];\n\t\t\tconst g = floatData[ 4 * i + 1 ];\n\t\t\tconst b = floatData[ 4 * i + 2 ];\n\n\t\t\t// Luminance (Rec. 709)\n\t\t\tconst weight = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n\t\t\tcumulativeRowWeight += weight;\n\t\t\ttotalSumValue += weight;\n\n\t\t\tcdfConditional[ i ] = cumulativeRowWeight;\n\n\t\t}\n\n\t\t// Normalize row CDF to [0, 1]\n\t\tif ( cumulativeRowWeight !== 0 ) {\n\n\t\t\tfor ( let i = y * width, l = y * width + width; i < l; i ++ ) {\n\n\t\t\t\tcdfConditional[ i ] /= cumulativeRowWeight;\n\n\t\t\t}\n\n\t\t}\n\n\t\tcumulativeWeightMarginal += cumulativeRowWeight;\n\t\tcdfMarginal[ y ] = cumulativeWeightMarginal;\n\n\t}\n\n\t// Normalize marginal CDF to [0, 1]\n\tif ( cumulativeWeightMarginal !== 0 ) {\n\n\t\tfor ( let i = 0, l = cdfMarginal.length; i < l; i ++ ) {\n\n\t\t\tcdfMarginal[ i ] /= cumulativeWeightMarginal;\n\n\t\t}\n\n\t}\n\n\t// Invert marginal CDF\n\tconst marginalData = new Float32Array( height );\n\tfor ( let i = 0; i < height; i ++ ) {\n\n\t\tconst dist = ( i + 1 ) / height;\n\t\tconst row = binarySearchFindClosestIndexOf( cdfMarginal, dist, 0, height );\n\t\tmarginalData[ i ] = ( row + 0.5 ) / height;\n\n\t}\n\n\t// Invert conditional CDFs\n\tconst conditionalData = new Float32Array( width * height );\n\tfor ( let y = 0; y < height; y ++ ) {\n\n\t\tfor ( let x = 0; x < width; x ++ ) {\n\n\t\t\tconst i = y * width + x;\n\t\t\tconst dist = ( x + 1 ) / width;\n\t\t\tconst col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );\n\t\t\tconditionalData[ i ] = ( col + 0.5 ) / width;\n\n\t\t}\n\n\t}\n\n\treturn { marginalData, conditionalData, totalSum: totalSumValue };\n\n}\n\nself.onmessage = function ( e ) {\n\n\tconst { floatData, width, height } = e.data;\n\n\ttry {\n\n\t\tconst result = buildCDF( floatData, width, height );\n\n\t\t// Transfer arrays back zero-copy\n\t\tself.postMessage(\n\t\t\t{\n\t\t\t\tmarginalData: result.marginalData,\n\t\t\t\tconditionalData: result.conditionalData,\n\t\t\t\ttotalSum: result.totalSum,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t},\n\t\t\t[ result.marginalData.buffer, result.conditionalData.buffer ]\n\t\t);\n\n\t} catch ( error ) {\n\n\t\tself.postMessage( { error: error.message } );\n\n\t}\n\n};\n"],"mappings":"YAQA,SAAS,EAAgC,EAAO,EAAa,EAAQ,EAAQ,CAE5E,IAAI,EAAQ,EACR,EAAQ,EAAS,EAAQ,EAE7B,KAAQ,EAAQ,GAAQ,CAEvB,IAAM,EAAQ,EAAQ,GAAW,EAE5B,EAAO,GAAQ,EAEnB,EAAQ,EAAM,EAId,EAAQ,EAMV,OAAO,EAAQ,EAIhB,SAAS,EAAU,EAAW,EAAO,EAAS,CAE7C,IAAM,EAAiB,IAAI,aAAc,EAAQ,EAAQ,CACnD,EAAc,IAAI,aAAc,EAAQ,CAE1C,EAAgB,EAChB,EAA2B,EAG/B,IAAM,IAAI,EAAI,EAAG,EAAI,EAAQ,IAAO,CAEnC,IAAI,EAAsB,EAC1B,IAAM,IAAI,EAAI,EAAG,EAAI,EAAO,IAAO,CAElC,IAAM,EAAI,EAAI,EAAQ,EAChB,EAAI,EAAW,EAAI,GACnB,EAAI,EAAW,EAAI,EAAI,GACvB,EAAI,EAAW,EAAI,EAAI,GAGvB,EAAS,MAAS,EAAI,MAAS,EAAI,MAAS,EAClD,GAAuB,EACvB,GAAiB,EAEjB,EAAgB,GAAM,EAKvB,GAAK,IAAwB,EAE5B,IAAM,IAAI,EAAI,EAAI,EAAO,EAAI,EAAI,EAAQ,EAAO,EAAI,EAAG,IAEtD,EAAgB,IAAO,EAMzB,GAA4B,EAC5B,EAAa,GAAM,EAKpB,GAAK,IAA6B,EAEjC,IAAM,IAAI,EAAI,EAAG,EAAI,EAAY,OAAQ,EAAI,EAAG,IAE/C,EAAa,IAAO,EAOtB,IAAM,EAAe,IAAI,aAAc,EAAQ,CAC/C,IAAM,IAAI,EAAI,EAAG,EAAI,EAAQ,IAI5B,EAAc,IADF,EAAgC,GAD7B,EAAI,GAAM,EACsC,EAAG,EAAQ,CAC9C,IAAQ,EAKrC,IAAM,EAAkB,IAAI,aAAc,EAAQ,EAAQ,CAC1D,IAAM,IAAI,EAAI,EAAG,EAAI,EAAQ,IAE5B,IAAM,IAAI,EAAI,EAAG,EAAI,EAAO,IAAO,CAElC,IAAM,EAAI,EAAI,EAAQ,EAGtB,EAAiB,IADL,EAAgC,GAD7B,EAAI,GAAM,EACyC,EAAI,EAAO,EAAO,CACrD,IAAQ,EAMzC,MAAO,CAAE,eAAc,kBAAiB,SAAU,EAAe,CAIlE,KAAK,UAAY,SAAW,EAAI,CAE/B,GAAM,CAAE,YAAW,QAAO,UAAW,EAAE,KAEvC,GAAI,CAEH,IAAM,EAAS,EAAU,EAAW,EAAO,EAAQ,CAGnD,KAAK,YACJ,CACC,aAAc,EAAO,aACrB,gBAAiB,EAAO,gBACxB,SAAU,EAAO,SACjB,QACA,SACA,CACD,CAAE,EAAO,aAAa,OAAQ,EAAO,gBAAgB,OAAQ,CAC7D,OAEQ,EAAQ,CAEjB,KAAK,YAAa,CAAE,MAAO,EAAM,QAAS,CAAE"}
|