rayzee 7.3.0 → 7.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/dist/rayzee.es.js +1294 -1220
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +64 -50
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/EngineDefaults.js +9 -7
- package/src/Passes/OIDNDenoiser.js +27 -3
- package/src/PathTracerApp.js +4 -1
- package/src/Processor/CameraOptimizer.js +0 -5
- package/src/Processor/EmissiveTriangleBuilder.js +4 -3
- package/src/Processor/PackedRayBuffer.js +12 -33
- package/src/Processor/QueueManager.js +1 -0
- package/src/Processor/StorageTexturePool.js +11 -3
- package/src/Processor/VRAMTracker.js +50 -8
- package/src/RenderSettings.js +13 -2
- package/src/Stages/ASVGF.js +27 -0
- package/src/Stages/BilateralFilter.js +12 -0
- package/src/Stages/EdgeFilter.js +12 -0
- package/src/Stages/MotionVector.js +10 -0
- package/src/Stages/NormalDepth.js +10 -0
- package/src/Stages/PathTracer.js +35 -54
- package/src/Stages/PathTracerStage.js +0 -13
- package/src/Stages/SSRC.js +16 -0
- package/src/Stages/Variance.js +13 -0
- package/src/TSL/DebugKernel.js +2 -3
- package/src/TSL/FinalWriteKernel.js +28 -29
- package/src/TSL/GenerateKernel.js +12 -19
- package/src/TSL/ShadeKernel.js +160 -61
- package/src/managers/DenoisingManager.js +17 -0
- package/src/managers/UniformManager.js +4 -2
- package/src/managers/VideoRenderManager.js +0 -2
package/src/Stages/PathTracer.js
CHANGED
|
@@ -17,7 +17,6 @@ import { buildShadeKernel, SHADE_WG_SIZE } from '../TSL/ShadeKernel.js';
|
|
|
17
17
|
import { buildCompactKernel, buildCompactSubgroupKernel, COMPACT_WG_SIZE } from '../TSL/CompactKernel.js';
|
|
18
18
|
import { buildFinalWriteKernel, FINALWRITE_WG_SIZE } from '../TSL/FinalWriteKernel.js';
|
|
19
19
|
import { buildDebugKernel, DEBUG_WG_SIZE } from '../TSL/DebugKernel.js';
|
|
20
|
-
import { ENGINE_DEFAULTS } from '../EngineDefaults.js';
|
|
21
20
|
import {
|
|
22
21
|
Fn, uint, atomicStore, atomicLoad, instanceIndex, If, Return,
|
|
23
22
|
} from 'three/tsl';
|
|
@@ -35,16 +34,18 @@ export class PathTracer extends PathTracerStage {
|
|
|
35
34
|
this._gBufferAttr = null; // per-pixel first-hit MRT (ND + albedo); see _buildWavefrontKernels
|
|
36
35
|
this._wavefrontReady = false;
|
|
37
36
|
|
|
37
|
+
// Aux MRT (normalDepth + albedo) feeds only the denoiser/OIDN. When no denoiser is active the
|
|
38
|
+
// wavefront skips those writes (Generate/Shade G-buffer + FinalWrite stores). Gated by a live
|
|
39
|
+
// uniform — NOT baked — so DenoisingManager can toggle it without a (UI-freezing) kernel rebuild.
|
|
40
|
+
this._auxGBufferEnabled = false;
|
|
41
|
+
this._auxGBufferUniform = uniform( 0, 'uint' );
|
|
42
|
+
|
|
38
43
|
// CPU sizes per-bounce kernels from last frame's survivor curve; kernels bound on ENTERING_COUNT so over-sizing is safe. (indirect dispatch not viable — three.js doesn't sync compute-written indirect buffers across submissions)
|
|
39
44
|
this._useDynamicDispatch = true;
|
|
40
45
|
|
|
41
46
|
// Flag-gated off: perf-neutral vs atomic-append and adds a 'subgroups' feature dependency.
|
|
42
47
|
this._useSubgroupCompact = false;
|
|
43
48
|
|
|
44
|
-
// Multi-sample pool: S=samplesPerPixel primary rays/pixel/frame (interactive-only, ≤ the pixel cap; else S=1). FinalWrite averages the S slots. Baked into kernels; _ensureSamplesPerPass() rebuilds on change.
|
|
45
|
-
this._multiSampleMaxPixels = ENGINE_DEFAULTS.wavefrontMultiSampleMaxPixels ?? 589824; // 768²
|
|
46
|
-
this._samplesPerPass = 1;
|
|
47
|
-
|
|
48
49
|
this._lastBounceCounts = null;
|
|
49
50
|
// maxBounces the curve was measured at; the curve is ignored once this no longer matches (-1 = none).
|
|
50
51
|
this._lastBounceCountsBudget = - 1;
|
|
@@ -63,7 +64,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
63
64
|
|
|
64
65
|
// VRAM accounting — providers are thunks reading CURRENT live resources,
|
|
65
66
|
// so they survive buffer/texture reallocation (resize, scene/material reload).
|
|
66
|
-
this.vramTracker = new VRAMTracker();
|
|
67
|
+
this.vramTracker = new VRAMTracker( this.renderer );
|
|
67
68
|
this._registerVRAMProviders();
|
|
68
69
|
|
|
69
70
|
console.log( 'PathTracer: initialized (wavefront)' );
|
|
@@ -164,19 +165,15 @@ export class PathTracer extends PathTracerStage {
|
|
|
164
165
|
const renderMode = this.renderMode.value;
|
|
165
166
|
|
|
166
167
|
let originalMaxBounces = null;
|
|
167
|
-
let originalSamplesPerPixel = null;
|
|
168
168
|
|
|
169
169
|
if ( renderMode === 1 && frameValue === 0 ) {
|
|
170
170
|
|
|
171
171
|
originalMaxBounces = this.maxBounces.value;
|
|
172
|
-
originalSamplesPerPixel = this.samplesPerPixel.value;
|
|
173
172
|
this.maxBounces.value = 1;
|
|
174
|
-
this.samplesPerPixel.value = 1;
|
|
175
173
|
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
this._handleResize();
|
|
179
|
-
this._ensureSamplesPerPass();
|
|
180
177
|
this.manageASVGFForRenderMode( renderMode );
|
|
181
178
|
|
|
182
179
|
// Full-frame render is always a complete cycle (PER_CYCLE stages gate on this).
|
|
@@ -218,7 +215,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
218
215
|
if ( ! this.cameraOptimizer?.isInInteractionMode() ) this.frameCount ++;
|
|
219
216
|
|
|
220
217
|
if ( originalMaxBounces !== null ) this.maxBounces.value = originalMaxBounces;
|
|
221
|
-
if ( originalSamplesPerPixel !== null ) this.samplesPerPixel.value = originalSamplesPerPixel;
|
|
222
218
|
|
|
223
219
|
this.performanceMonitor?.end();
|
|
224
220
|
return;
|
|
@@ -326,7 +322,9 @@ export class PathTracer extends PathTracerStage {
|
|
|
326
322
|
|
|
327
323
|
this._maybeReadbackCounters();
|
|
328
324
|
|
|
329
|
-
|
|
325
|
+
// Skip the normalDepth/albedo copies when aux is off — the wavefront didn't write them and
|
|
326
|
+
// no stage reads them; saves two full-res GPU copies/frame in the default interactive path.
|
|
327
|
+
this.storageTextures.copyToReadTargets( this.renderer, this._auxGBufferEnabled );
|
|
330
328
|
|
|
331
329
|
const readTex = this.storageTextures.getReadTextures();
|
|
332
330
|
if ( context ) this._publishTexturesToContext( context, readTex );
|
|
@@ -336,7 +334,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
336
334
|
if ( ! this.cameraOptimizer?.isInInteractionMode() ) this.frameCount ++;
|
|
337
335
|
|
|
338
336
|
if ( originalMaxBounces !== null ) this.maxBounces.value = originalMaxBounces;
|
|
339
|
-
if ( originalSamplesPerPixel !== null ) this.samplesPerPixel.value = originalSamplesPerPixel;
|
|
340
337
|
|
|
341
338
|
this.performanceMonitor?.end();
|
|
342
339
|
|
|
@@ -354,27 +351,16 @@ export class PathTracer extends PathTracerStage {
|
|
|
354
351
|
|
|
355
352
|
}
|
|
356
353
|
|
|
357
|
-
//
|
|
358
|
-
|
|
354
|
+
// Aux MRT (normalDepth/albedo) is needed only by the denoiser/OIDN; DenoisingManager calls this to
|
|
355
|
+
// turn the wavefront's aux writes on/off. It's a live uniform, so toggling is just a value flip +
|
|
356
|
+
// accumulation reset — no kernel rebuild, no UI freeze.
|
|
357
|
+
setAuxGBufferEnabled( enabled ) {
|
|
359
358
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// S is baked at build but samplesPerPixel/mode can change without a resize; rebuild when the implied S differs.
|
|
367
|
-
_ensureSamplesPerPass() {
|
|
368
|
-
|
|
369
|
-
if ( ! this._wavefrontReady ) return;
|
|
370
|
-
const w = this.storageTextures.renderWidth;
|
|
371
|
-
const h = this.storageTextures.renderHeight;
|
|
372
|
-
if ( this._resolveSamplesPerPass( w, h ) !== this._samplesPerPass ) {
|
|
373
|
-
|
|
374
|
-
this._wavefrontReady = false;
|
|
375
|
-
this._buildWavefrontKernels();
|
|
376
|
-
|
|
377
|
-
}
|
|
359
|
+
enabled = !! enabled;
|
|
360
|
+
if ( this._auxGBufferEnabled === enabled ) return;
|
|
361
|
+
this._auxGBufferEnabled = enabled;
|
|
362
|
+
this._auxGBufferUniform.value = enabled ? 1 : 0;
|
|
363
|
+
this.reset();
|
|
378
364
|
|
|
379
365
|
}
|
|
380
366
|
|
|
@@ -464,12 +450,10 @@ export class PathTracer extends PathTracerStage {
|
|
|
464
450
|
this._readbackFrameCounter = 0;
|
|
465
451
|
this._readbackGeneration ++;
|
|
466
452
|
|
|
467
|
-
// Recompile only when buffers reallocate (capacity grows)
|
|
468
|
-
const
|
|
469
|
-
const neededCap = PackedRayBuffer.requiredCapacity( newW * newH * newS );
|
|
453
|
+
// Recompile only when buffers reallocate (capacity grows); otherwise resize uniforms in place.
|
|
454
|
+
const neededCap = PackedRayBuffer.requiredCapacity( newW * newH );
|
|
470
455
|
const mustRebuild = ! this._packedBuffers
|
|
471
|
-
|| neededCap > this._packedBuffers.capacity
|
|
472
|
-
|| newS !== this._samplesPerPass;
|
|
456
|
+
|| neededCap > this._packedBuffers.capacity;
|
|
473
457
|
|
|
474
458
|
if ( mustRebuild ) {
|
|
475
459
|
|
|
@@ -485,10 +469,10 @@ export class PathTracer extends PathTracerStage {
|
|
|
485
469
|
|
|
486
470
|
}
|
|
487
471
|
|
|
488
|
-
// Same-capacity
|
|
472
|
+
// Same-capacity resize: update render-size uniforms + early-exit threshold, no recompile.
|
|
489
473
|
_resizeWavefrontInPlace( w, h ) {
|
|
490
474
|
|
|
491
|
-
const maxRays = w * h
|
|
475
|
+
const maxRays = w * h;
|
|
492
476
|
this._wfRenderWidth.value = w;
|
|
493
477
|
this._wfRenderHeight.value = h;
|
|
494
478
|
this._wfMaxRayCount.value = maxRays;
|
|
@@ -509,11 +493,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
509
493
|
|
|
510
494
|
const w = this.storageTextures.renderWidth;
|
|
511
495
|
const h = this.storageTextures.renderHeight;
|
|
512
|
-
|
|
513
|
-
this._samplesPerPass = this._resolveSamplesPerPass( w, h );
|
|
514
|
-
const S = this._samplesPerPass | 0;
|
|
515
|
-
const maxRaysPerSample = w * h;
|
|
516
|
-
const maxRays = maxRaysPerSample * S;
|
|
496
|
+
const maxRays = w * h;
|
|
517
497
|
|
|
518
498
|
if ( this._bounceEarlyExitThreshold !== - 1 ) {
|
|
519
499
|
|
|
@@ -534,9 +514,9 @@ export class PathTracer extends PathTracerStage {
|
|
|
534
514
|
// Per-pixel G-buffer (first-hit MRT: ND + albedo), 1 uvec4/pixel — half-precision packed (pack2x16).
|
|
535
515
|
// uint (not f32) buffer: packed lanes can hit the NaN exponent range (e.g. snorm 1.0 → 0x7FFF), which a
|
|
536
516
|
// GPU may canonicalize through f32 storage; u32 stores the bits verbatim. Separate from RAY — it's
|
|
537
|
-
// per-pixel
|
|
517
|
+
// per-pixel, written by Generate/Shade bounce-0 and read only by FinalWrite.
|
|
538
518
|
// 1.25× margin (same as the per-ray buffers) so it survives the in-place-resize range.
|
|
539
|
-
const gBufferVec4s = PackedRayBuffer.requiredCapacity(
|
|
519
|
+
const gBufferVec4s = PackedRayBuffer.requiredCapacity( maxRays ) * GBUFFER_STRIDE;
|
|
540
520
|
this._gBufferAttr = new StorageInstancedBufferAttribute( new Uint32Array( gBufferVec4s * 4 ), 4 );
|
|
541
521
|
const gBufferRW = storage( this._gBufferAttr, 'uvec4' );
|
|
542
522
|
const gBufferRO = storage( this._gBufferAttr, 'uvec4' ).toReadOnly();
|
|
@@ -639,14 +619,13 @@ export class PathTracer extends PathTracerStage {
|
|
|
639
619
|
anamorphicRatio: this.anamorphicRatio,
|
|
640
620
|
renderWidth: this._wfRenderWidth,
|
|
641
621
|
renderHeight: this._wfRenderHeight,
|
|
642
|
-
samplesPerPass: S,
|
|
643
622
|
transmissiveBounces: this.transmissiveBounces,
|
|
644
623
|
transparentBackground: this.transparentBackground,
|
|
624
|
+
auxGBufferEnabled: this._auxGBufferUniform,
|
|
645
625
|
} );
|
|
646
626
|
this._kernelManager.register( 'generate',
|
|
647
627
|
genFn().compute(
|
|
648
|
-
|
|
649
|
-
[ Math.ceil( w / GENERATE_WG_SIZE ), Math.ceil( ( h * S ) / GENERATE_WG_SIZE ), 1 ],
|
|
628
|
+
[ Math.ceil( w / GENERATE_WG_SIZE ), Math.ceil( h / GENERATE_WG_SIZE ), 1 ],
|
|
650
629
|
[ GENERATE_WG_SIZE, GENERATE_WG_SIZE, 1 ]
|
|
651
630
|
)
|
|
652
631
|
);
|
|
@@ -743,6 +722,9 @@ export class PathTracer extends PathTracerStage {
|
|
|
743
722
|
maxSubsurfaceSteps: this.maxSubsurfaceSteps,
|
|
744
723
|
transparentBackground: this.transparentBackground,
|
|
745
724
|
backgroundIntensity: this.backgroundIntensity,
|
|
725
|
+
backgroundColor: this.backgroundColor,
|
|
726
|
+
backgroundBlurriness: this.backgroundBlurriness,
|
|
727
|
+
backgroundBlurSamples: this.backgroundBlurSamples,
|
|
746
728
|
showBackground: this.showBackground,
|
|
747
729
|
globalIlluminationIntensity: this.globalIlluminationIntensity,
|
|
748
730
|
cameraProjectionMatrix: this.cameraProjectionMatrix,
|
|
@@ -760,6 +742,7 @@ export class PathTracer extends PathTracerStage {
|
|
|
760
742
|
reverseMapVec4Offset: this.reverseMapVec4Offset,
|
|
761
743
|
currentBounce: this._wfCurrentBounce,
|
|
762
744
|
maxRayCount: this._wfMaxRayCount,
|
|
745
|
+
auxGBufferEnabled: this._auxGBufferUniform,
|
|
763
746
|
} );
|
|
764
747
|
this._kernelManager.register( 'shade',
|
|
765
748
|
shadeFn().compute(
|
|
@@ -833,8 +816,8 @@ export class PathTracer extends PathTracerStage {
|
|
|
833
816
|
prevAlbedoTexture: prevAlbedo,
|
|
834
817
|
renderWidth: this._wfRenderWidth,
|
|
835
818
|
renderHeight: this._wfRenderHeight,
|
|
836
|
-
samplesPerPass: S,
|
|
837
819
|
visMode: this.visMode,
|
|
820
|
+
auxGBufferEnabled: this._auxGBufferUniform,
|
|
838
821
|
} );
|
|
839
822
|
this._kernelManager.register( 'finalWrite',
|
|
840
823
|
// Per-pixel (w×h) — kernel averages the S sample-slots internally.
|
|
@@ -873,7 +856,6 @@ export class PathTracer extends PathTracerStage {
|
|
|
873
856
|
enableEnvironmentLight: this.enableEnvironment,
|
|
874
857
|
visMode: this.visMode,
|
|
875
858
|
debugVisScale: this.debugVisScale,
|
|
876
|
-
samplesPerPass: this._samplesPerPass,
|
|
877
859
|
albedoMaps: freshAlbedoMaps,
|
|
878
860
|
normalMaps: freshNormalMaps,
|
|
879
861
|
bumpMaps: freshBumpMaps,
|
|
@@ -898,11 +880,10 @@ export class PathTracer extends PathTracerStage {
|
|
|
898
880
|
|
|
899
881
|
const w = this._wfRenderWidth.value;
|
|
900
882
|
const h = this._wfRenderHeight.value;
|
|
901
|
-
const S = this._samplesPerPass | 0;
|
|
902
883
|
|
|
903
884
|
this._kernelManager.setDispatchCount( 'generate', [
|
|
904
885
|
Math.ceil( w / GENERATE_WG_SIZE ),
|
|
905
|
-
Math.ceil(
|
|
886
|
+
Math.ceil( h / GENERATE_WG_SIZE ), 1
|
|
906
887
|
] );
|
|
907
888
|
this._kernelManager.setDispatchCount( 'finalWrite', [
|
|
908
889
|
Math.ceil( w / FINALWRITE_WG_SIZE ),
|
|
@@ -279,18 +279,6 @@ export class PathTracerStage extends RenderStage {
|
|
|
279
279
|
|
|
280
280
|
}
|
|
281
281
|
},
|
|
282
|
-
numRaysPerPixel: {
|
|
283
|
-
get value() {
|
|
284
|
-
|
|
285
|
-
return self.samplesPerPixel.value;
|
|
286
|
-
|
|
287
|
-
},
|
|
288
|
-
set value( v ) {
|
|
289
|
-
|
|
290
|
-
self.samplesPerPixel.value = v;
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
282
|
useEnvMapIS: {
|
|
295
283
|
get value() {
|
|
296
284
|
|
|
@@ -346,7 +334,6 @@ export class PathTracerStage extends RenderStage {
|
|
|
346
334
|
enabled: DEFAULT_STATE.interactionModeEnabled,
|
|
347
335
|
qualitySettings: {
|
|
348
336
|
maxBounceCount: 1,
|
|
349
|
-
numRaysPerPixel: 1,
|
|
350
337
|
useEnvMapIS: false,
|
|
351
338
|
enableAccumulation: false,
|
|
352
339
|
enableEmissiveTriangleSampling: false,
|
package/src/Stages/SSRC.js
CHANGED
|
@@ -171,6 +171,22 @@ export class SSRC extends RenderStage {
|
|
|
171
171
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
// Free the 2048² StorageTextures when disabled; three.js re-creates them on the next dispatch
|
|
175
|
+
// after re-enable, and reset() clears the temporal cache. See ASVGF.releaseGPUMemory.
|
|
176
|
+
releaseGPUMemory() {
|
|
177
|
+
|
|
178
|
+
this._cacheTexA?.dispose();
|
|
179
|
+
this._cacheTexB?.dispose();
|
|
180
|
+
this._prevNDTexA?.dispose();
|
|
181
|
+
this._prevNDTexB?.dispose();
|
|
182
|
+
this._outputTex?.dispose();
|
|
183
|
+
// Render-res RT texture (dispose .texture, not the RT — RT.dispose() doesn't free it here).
|
|
184
|
+
this.context?.removeTexture( 'ssrc:output' );
|
|
185
|
+
this.outputTarget?.texture?.dispose();
|
|
186
|
+
this.reset();
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
|
|
174
190
|
reset() {
|
|
175
191
|
|
|
176
192
|
this._resetCache();
|
package/src/Stages/Variance.js
CHANGED
|
@@ -363,6 +363,19 @@ export class Variance extends RenderStage {
|
|
|
363
363
|
|
|
364
364
|
}
|
|
365
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
|
+
|
|
366
379
|
reset() {
|
|
367
380
|
|
|
368
381
|
this.currentMoments = 0;
|
package/src/TSL/DebugKernel.js
CHANGED
|
@@ -32,7 +32,6 @@ export function buildDebugKernel( params ) {
|
|
|
32
32
|
visMode, debugVisScale,
|
|
33
33
|
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
34
34
|
frame,
|
|
35
|
-
samplesPerPass = 1,
|
|
36
35
|
} = params;
|
|
37
36
|
|
|
38
37
|
const computeFn = Fn( () => {
|
|
@@ -61,8 +60,8 @@ export function buildDebugKernel( params ) {
|
|
|
61
60
|
// Mode 9: visualize the stratified AA-jitter pattern (R,G = jitter).
|
|
62
61
|
If( visMode.equal( int( 9 ) ), () => {
|
|
63
62
|
|
|
64
|
-
//
|
|
65
|
-
const jitter = getStratifiedSample( pixelCoord, int( 0 ), int(
|
|
63
|
+
// One ray per pixel — plain per-frame jitter (totalRays = 1 → random, no stratified lattice).
|
|
64
|
+
const jitter = getStratifiedSample( pixelCoord, int( 0 ), int( 1 ), seed, resolution, frame );
|
|
66
65
|
color.assign( vec4( jitter, 1.0, 1.0 ) );
|
|
67
66
|
|
|
68
67
|
} ).Else( () => {
|
|
@@ -34,12 +34,13 @@ export function buildFinalWriteKernel( params ) {
|
|
|
34
34
|
transparentBackground,
|
|
35
35
|
prevAccumTexture, prevNormalDepthTexture, prevAlbedoTexture,
|
|
36
36
|
renderWidth, renderHeight,
|
|
37
|
-
// Multi-sample: average S sample-slots per pixel (slot = pixel + k*w*h, w*h from the resolution uniform).
|
|
38
|
-
samplesPerPass = 1,
|
|
39
37
|
visMode,
|
|
38
|
+
// Aux MRT (normalDepth + albedo) feeds only the denoiser/OIDN. Gated by a live uniform (1 = denoiser
|
|
39
|
+
// on): when off, skip the G-buffer decode, the prev-frame aux mix, and the two aux stores.
|
|
40
|
+
auxGBufferEnabled,
|
|
40
41
|
} = params;
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
+
const auxOn = auxGBufferEnabled.greaterThan( uint( 0 ) );
|
|
43
44
|
|
|
44
45
|
const computeFn = Fn( () => {
|
|
45
46
|
|
|
@@ -51,31 +52,21 @@ export function buildFinalWriteKernel( params ) {
|
|
|
51
52
|
const pixelIndex = gy.mul( int( resolution.x ) ).add( gx );
|
|
52
53
|
const rayID = uint( pixelIndex );
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
if ( S <= 1 ) return readRayRadiance( rayBufferRO, rayID );
|
|
58
|
-
const acc = readRayRadiance( rayBufferRO, rayID ).toVar();
|
|
59
|
-
const mrps = uint( resolution.x ).mul( uint( resolution.y ) ).toVar(); // w*h from the resolution uniform, not baked
|
|
60
|
-
for ( let k = 1; k < S; k ++ ) {
|
|
61
|
-
|
|
62
|
-
acc.addAssign( readRayRadiance( rayBufferRO, rayID.add( uint( k ).mul( mrps ) ) ) );
|
|
63
|
-
|
|
64
|
-
}
|
|
55
|
+
const sampleColor = readRayRadiance( rayBufferRO, rayID );
|
|
56
|
+
const finalColor = sampleColor.xyz.toVar();
|
|
57
|
+
const outputAlpha = select( transparentBackground, sampleColor.w, float( 1.0 ) ).toVar();
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
// MRT comes from the per-pixel G-buffer (rayID == pixelIndex). Half-packed: decode.
|
|
60
|
+
// auxOn gates the decode + stores so a no-denoiser frame does no G-buffer read and no aux writes.
|
|
61
|
+
const finalNormalDepth = vec4( 0.0 ).toVar();
|
|
62
|
+
const finalAlbedo = vec4( 0.0 ).xyz.toVar();
|
|
63
|
+
If( auxOn, () => {
|
|
68
64
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const normalDepth = gbDecodeNormalDepth( gbuf );
|
|
73
|
-
const albedoID = vec4( gbDecodeAlbedo( gbuf ), 0.0 );
|
|
65
|
+
const gbuf = readGBuffer( gBufferRO, rayID );
|
|
66
|
+
finalNormalDepth.assign( gbDecodeNormalDepth( gbuf ) );
|
|
67
|
+
finalAlbedo.assign( vec4( gbDecodeAlbedo( gbuf ), 0.0 ).xyz );
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
const finalNormalDepth = normalDepth.toVar();
|
|
77
|
-
const finalAlbedo = albedoID.xyz.toVar();
|
|
78
|
-
const outputAlpha = select( transparentBackground, sampleColor.w, float( 1.0 ) ).toVar();
|
|
69
|
+
} );
|
|
79
70
|
|
|
80
71
|
const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
|
|
81
72
|
const prevUV = pixelCoord.div( resolution );
|
|
@@ -87,8 +78,12 @@ export function buildFinalWriteKernel( params ) {
|
|
|
87
78
|
const prevAccumSample = texture( prevAccumTexture, prevUV, 0 ).toVar();
|
|
88
79
|
|
|
89
80
|
finalColor.assign( mix( prevAccumSample.xyz, sampleColor.xyz, accumulationAlpha ) );
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
If( auxOn, () => {
|
|
82
|
+
|
|
83
|
+
finalNormalDepth.assign( mix( texture( prevNormalDepthTexture, prevUV, 0 ), finalNormalDepth, accumulationAlpha ) );
|
|
84
|
+
finalAlbedo.assign( mix( texture( prevAlbedoTexture, prevUV, 0 ).xyz, finalAlbedo, accumulationAlpha ) );
|
|
85
|
+
|
|
86
|
+
} );
|
|
92
87
|
|
|
93
88
|
If( transparentBackground, () => {
|
|
94
89
|
|
|
@@ -107,8 +102,12 @@ export function buildFinalWriteKernel( params ) {
|
|
|
107
102
|
|
|
108
103
|
const uintCoord = uvec2( uint( gx ), uint( gy ) );
|
|
109
104
|
textureStore( writeColorTex, uintCoord, vec4( finalColor, outputAlpha ) ).toWriteOnly();
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
If( auxOn, () => {
|
|
106
|
+
|
|
107
|
+
textureStore( writeNDTex, uintCoord, finalNormalDepth ).toWriteOnly();
|
|
108
|
+
textureStore( writeAlbedoTex, uintCoord, vec4( finalAlbedo, 1.0 ) ).toWriteOnly();
|
|
109
|
+
|
|
110
|
+
} );
|
|
112
111
|
|
|
113
112
|
} );
|
|
114
113
|
|
|
@@ -19,7 +19,7 @@ import { Ray } from './Struct.js';
|
|
|
19
19
|
import { RAY_FLAG } from '../Processor/QueueManager.js';
|
|
20
20
|
import {
|
|
21
21
|
writeRayOriginMeta, writeRayDirFlags, writeRayThroughputPdf,
|
|
22
|
-
writeRayRadiance, writeGBuffer,
|
|
22
|
+
writeRayRadiance, writeGBuffer,
|
|
23
23
|
writeMediumStack,
|
|
24
24
|
} from '../Processor/PackedRayBuffer.js';
|
|
25
25
|
|
|
@@ -33,39 +33,32 @@ export function buildGenerateKernel( params ) {
|
|
|
33
33
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
34
34
|
enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
|
|
35
35
|
renderWidth, renderHeight,
|
|
36
|
-
// Multi-sample: S primary rays/pixel/frame; S>1 dispatch covers h*S rows, ray lands in slot subSample*(w*h) + pixelIndex.
|
|
37
|
-
samplesPerPass = 1,
|
|
38
36
|
transmissiveBounces, // per-ray refraction budget (megakernel parity: PathTracerCore.js:606)
|
|
39
37
|
transparentBackground, // alpha inits to 1 here (megakernel parity: PathTracerCore.js:554) — env-escape-without-opaque zeroes it in Shade
|
|
38
|
+
auxGBufferEnabled, // live uniform: 1 = init the per-pixel G-buffer (denoiser on), 0 = skip it
|
|
40
39
|
} = params;
|
|
41
40
|
|
|
42
|
-
const
|
|
41
|
+
const auxOn = auxGBufferEnabled.greaterThan( uint( 0 ) );
|
|
43
42
|
|
|
44
43
|
const computeFn = Fn( () => {
|
|
45
44
|
|
|
46
45
|
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
47
|
-
const
|
|
46
|
+
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
const gy = S > 1 ? gyRaw.sub( subSample.mul( renderHeight ) ).toVar() : gyRaw;
|
|
51
|
-
const yBound = S > 1 ? renderHeight.mul( int( S ) ) : renderHeight;
|
|
52
|
-
|
|
53
|
-
If( gx.lessThan( renderWidth ).and( gyRaw.lessThan( yBound ) ), () => {
|
|
48
|
+
If( gx.lessThan( renderWidth ).and( gy.lessThan( renderHeight ) ), () => {
|
|
54
49
|
|
|
55
50
|
const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
|
|
56
51
|
const pixelIndex = gy.mul( int( resolution.x ) ).add( gx );
|
|
57
|
-
//
|
|
58
|
-
const rayID =
|
|
59
|
-
? uint( pixelIndex ).add( uint( subSample ).mul( uint( resolution.x ).mul( uint( resolution.y ) ) ) )
|
|
60
|
-
: uint( pixelIndex );
|
|
52
|
+
// One ray per pixel: rayID is the pixel index.
|
|
53
|
+
const rayID = uint( pixelIndex );
|
|
61
54
|
|
|
62
55
|
const screenPosition = pixelCoord.div( resolution ).mul( 2.0 ).sub( 1.0 ).toVar();
|
|
63
56
|
screenPosition.y.assign( screenPosition.y.negate() );
|
|
64
57
|
|
|
65
|
-
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex:
|
|
58
|
+
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: int( 0 ), frame } ).toVar();
|
|
66
59
|
const seed = pcgHash( { state: baseSeed } ).toVar();
|
|
67
60
|
|
|
68
|
-
const stratifiedJitter = getStratifiedSample( pixelCoord,
|
|
61
|
+
const stratifiedJitter = getStratifiedSample( pixelCoord, int( 0 ), int( 1 ), seed, resolution, frame ).toVar();
|
|
69
62
|
|
|
70
63
|
const jitterScale = vec2( 2.0 ).div( resolution );
|
|
71
64
|
const jitter = stratifiedJitter.sub( 0.5 ).mul( jitterScale );
|
|
@@ -78,6 +71,8 @@ export function buildGenerateKernel( params ) {
|
|
|
78
71
|
) );
|
|
79
72
|
|
|
80
73
|
writeRayOriginMeta( rayBufferRW, rayID, ray.origin, int( 0 ), int( 0 ) );
|
|
74
|
+
// A fresh camera ray is NOT redirected — REDIRECTED stays clear so it sees the direct backdrop;
|
|
75
|
+
// ShadeKernel sets REDIRECTED on the first direction-changing interaction (see RAY_FLAG.REDIRECTED).
|
|
81
76
|
writeRayDirFlags( rayBufferRW, rayID, ray.direction, uint( RAY_FLAG.ACTIVE ) );
|
|
82
77
|
// pdf inits to 0 = prevBouncePdf (megakernel parity PathTracerCore.js:556). The bounce>0 env/emissive
|
|
83
78
|
// MIS gate skips until an opaque scatter writes a real combinedPdf; free bounces preserve it.
|
|
@@ -87,12 +82,10 @@ export function buildGenerateKernel( params ) {
|
|
|
87
82
|
// keeps alpha 1 → solid. Non-transparent mode is inert (FinalWrite forces alpha 1).
|
|
88
83
|
writeRayRadiance( rayBufferRW, rayID, vec4( vec3( 0.0 ), select( transparentBackground, float( 1.0 ), float( 0.0 ) ) ) );
|
|
89
84
|
|
|
90
|
-
If(
|
|
85
|
+
If( auxOn, () => {
|
|
91
86
|
|
|
92
87
|
// default: normal +Z, depth 1 (far), black albedo (background/miss)
|
|
93
88
|
writeGBuffer( gBufferRW, uint( pixelIndex ), vec3( 0.0, 0.0, 1.0 ), float( 1.0 ), vec3( 0.0 ) );
|
|
94
|
-
// surface-ID lane defaults to invalid (valid=0); Shade overwrites it at the bounce-0 hit.
|
|
95
|
-
writeGBufferSurfaceID( gBufferRW, uint( pixelIndex ), uint( 0 ), uint( 0 ), float( 0.0 ), float( 0.0 ), uint( 0 ) );
|
|
96
89
|
|
|
97
90
|
} );
|
|
98
91
|
|