rayzee 7.0.0 → 7.1.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 +1 -1
- package/dist/rayzee.es.js +2020 -1999
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +45 -45
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +14 -0
- package/src/Processor/PackedRayBuffer.js +33 -7
- package/src/Stages/ASVGF.js +88 -71
- package/src/TSL/GenerateKernel.js +3 -1
- package/src/TSL/ShadeKernel.js +5 -2
package/package.json
CHANGED
package/src/EngineDefaults.js
CHANGED
|
@@ -127,6 +127,9 @@ export const ENGINE_DEFAULTS = {
|
|
|
127
127
|
asvgfPhiDepth: 1.0,
|
|
128
128
|
asvgfVarianceBoost: 1.0,
|
|
129
129
|
asvgfMaxAccumFrames: 32,
|
|
130
|
+
asvgfGradientStrength: 0.0,
|
|
131
|
+
asvgfGradientSigmaScale: 2.0,
|
|
132
|
+
asvgfGradientNoiseFloor: 0.0,
|
|
130
133
|
asvgfDebugMode: 0,
|
|
131
134
|
asvgfQualityPreset: 'medium',
|
|
132
135
|
showAsvgfHeatmap: false,
|
|
@@ -195,6 +198,17 @@ export const ASVGF_QUALITY_PRESETS = {
|
|
|
195
198
|
}
|
|
196
199
|
};
|
|
197
200
|
|
|
201
|
+
// Adaptive variants — same SVGF quality as the base preset plus the temporal-
|
|
202
|
+
// gradient anti-lag enabled (gradientStrength > 0). The gradient measures real
|
|
203
|
+
// change in units of noise σ (gradientSigmaScale), so a static scene reads ~0
|
|
204
|
+
// and convergence is unaffected; only moving lights/anim/disocclusion drop
|
|
205
|
+
// history. Mutating the exported object is fine (const binding, mutable object);
|
|
206
|
+
// spreading lets each inherit its base. See ASVGF._buildGradientCompute.
|
|
207
|
+
const ADAPTIVE_GRADIENT = { gradientSigmaScale: 2.5, gradientNoiseFloor: 0.05 };
|
|
208
|
+
ASVGF_QUALITY_PRESETS.low_adaptive = { ...ASVGF_QUALITY_PRESETS.low, gradientStrength: 0.8, ...ADAPTIVE_GRADIENT };
|
|
209
|
+
ASVGF_QUALITY_PRESETS.medium_adaptive = { ...ASVGF_QUALITY_PRESETS.medium, gradientStrength: 1.0, ...ADAPTIVE_GRADIENT };
|
|
210
|
+
ASVGF_QUALITY_PRESETS.high_adaptive = { ...ASVGF_QUALITY_PRESETS.high, gradientStrength: 1.0, ...ADAPTIVE_GRADIENT };
|
|
211
|
+
|
|
198
212
|
export const CAMERA_RANGES = {
|
|
199
213
|
fov: {
|
|
200
214
|
min: 10,
|
|
@@ -11,10 +11,13 @@ import { StorageInstancedBufferAttribute } from 'three/webgpu';
|
|
|
11
11
|
|
|
12
12
|
export const RAY_STRIDE = 7;
|
|
13
13
|
export const HIT_STRIDE = 2;
|
|
14
|
-
// Per-pixel G-buffer (first-hit MRT staging):
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
14
|
+
// Per-pixel G-buffer (first-hit MRT staging): 2 uvec4/pixel (AoS, element p*GBUFFER_STRIDE + lane).
|
|
15
|
+
// lane 0 — half-packed normal/depth/albedo (pack2x16, no f32 bitcast); read by FinalWrite:
|
|
16
|
+
// .x=packSnorm2x16(normal.xy) .y=packSnorm2x16(normal.z, depth) .z=packUnorm2x16(albedo.rg) .w=packUnorm2x16(albedo.b, 0)
|
|
17
|
+
// lane 1 — primary-hit surface ID for A-SVGF correlated-gradient re-projection (Tier 1); written at the
|
|
18
|
+
// bounce-0 hit, valid=0 on miss (Generate inits): .x=triIndex .y=meshIndex .z=packUnorm2x16(bary.u,bary.v) .w=valid
|
|
19
|
+
// Separate buffer from RAY (per-pixel, not per-ray×S) — written by Generate/Shade bounce-0.
|
|
20
|
+
export const GBUFFER_STRIDE = 2;
|
|
18
21
|
|
|
19
22
|
export const RAY = {
|
|
20
23
|
ORIGIN_META: 0, // vec4(origin.xyz, uintBitsToFloat(perRayBounces | sssSteps<<8)); pixelIndex+sampleIndex derived from rayID
|
|
@@ -146,17 +149,40 @@ export const readRayPdf = ( buf, id ) =>
|
|
|
146
149
|
export const readRayRadiance = ( buf, id ) =>
|
|
147
150
|
buf.element( soa( id, RAY.RADIANCE_ALPHA ) );
|
|
148
151
|
|
|
149
|
-
// ── Per-pixel G-buffer (first-hit MRT).
|
|
152
|
+
// ── Per-pixel G-buffer (first-hit MRT). 2 uvec4/pixel (AoS), pack2x16 lanes. ──
|
|
150
153
|
// normal: raw unit vec3; depth: linear [0,1]; albedo: vec3 [0,1]. Packed values live in u32 lanes
|
|
151
154
|
// verbatim (no f32 bitcast) so NaN-range bit patterns (snorm ±1 → 0x7FFF) survive store/load intact.
|
|
155
|
+
// gbLane resolves the AoS slot for a pixel (lane 0 = MRT, lane 1 = surface ID).
|
|
156
|
+
const gbLane = ( pixelIndex, lane ) => {
|
|
157
|
+
|
|
158
|
+
const base = uint( pixelIndex ).mul( GBUFFER_STRIDE );
|
|
159
|
+
return lane === 0 ? base : base.add( lane );
|
|
160
|
+
|
|
161
|
+
};
|
|
162
|
+
|
|
152
163
|
export const writeGBuffer = ( buf, pixelIndex, normal, depth, albedo ) =>
|
|
153
|
-
buf.element( pixelIndex ).assign( uvec4(
|
|
164
|
+
buf.element( gbLane( pixelIndex, 0 ) ).assign( uvec4(
|
|
154
165
|
packSnorm2x16( vec2( normal.x, normal.y ) ),
|
|
155
166
|
packSnorm2x16( vec2( normal.z, depth ) ),
|
|
156
167
|
packUnorm2x16( vec2( albedo.x, albedo.y ) ),
|
|
157
168
|
packUnorm2x16( vec2( albedo.z, 0.0 ) ),
|
|
158
169
|
) );
|
|
159
|
-
export const readGBuffer = ( buf, pixelIndex ) => buf.element( pixelIndex );
|
|
170
|
+
export const readGBuffer = ( buf, pixelIndex ) => buf.element( gbLane( pixelIndex, 0 ) );
|
|
171
|
+
|
|
172
|
+
// Lane 1 — primary-hit surface ID for A-SVGF correlated gradient re-projection (Tier 1).
|
|
173
|
+
// valid=0 marks a miss (no primary surface); bary packed unorm (both in [0,1]).
|
|
174
|
+
export const writeGBufferSurfaceID = ( buf, pixelIndex, triIndex, meshIndex, baryU, baryV, valid ) =>
|
|
175
|
+
buf.element( gbLane( pixelIndex, 1 ) ).assign( uvec4(
|
|
176
|
+
uint( triIndex ), uint( meshIndex ), packUnorm2x16( vec2( baryU, baryV ) ), uint( valid ),
|
|
177
|
+
) );
|
|
178
|
+
export const readGBufferSurfaceID = ( buf, pixelIndex ) => {
|
|
179
|
+
|
|
180
|
+
const p = buf.element( gbLane( pixelIndex, 1 ) );
|
|
181
|
+
const bary = unpackUnorm2x16( p.z );
|
|
182
|
+
return { triIndex: p.x, meshIndex: p.y, baryU: bary.x, baryV: bary.y, valid: p.w };
|
|
183
|
+
|
|
184
|
+
};
|
|
185
|
+
|
|
160
186
|
// Decode for FinalWrite. normalDepth.xyz matches the prior path (normal*0.5+0.5), .w = raw depth.
|
|
161
187
|
export const gbDecodeNormalDepth = ( packed ) => {
|
|
162
188
|
|
package/src/Stages/ASVGF.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fn, vec3, vec4, float, int, uint, ivec2, uvec2, uniform,
|
|
2
|
-
If, dot, max, min, abs, mix, pow, exp,
|
|
2
|
+
If, dot, max, min, abs, mix, pow, exp, sqrt,
|
|
3
3
|
textureLoad, textureStore, workgroupArray, workgroupBarrier, localId, workgroupId } from 'three/tsl';
|
|
4
4
|
import { RenderTarget, TextureNode, StorageTexture } from 'three/webgpu';
|
|
5
5
|
import { HalfFloatType, FloatType, RGBAFormat, NearestFilter, LinearFilter, Box2, Vector2 } from 'three';
|
|
@@ -8,13 +8,15 @@ import { luminance } from '../TSL/Common.js';
|
|
|
8
8
|
import { ALBEDO_EPS, MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* ASVGF — SVGF temporal
|
|
11
|
+
* ASVGF — SVGF temporal denoising with albedo demodulation + adaptive-α.
|
|
12
12
|
*
|
|
13
|
-
* Adaptive
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* 1
|
|
13
|
+
* Adaptive temporal gradient (the "A"): the gradient kernel compares this
|
|
14
|
+
* frame's demodulated lighting against the reprojected accumulated history,
|
|
15
|
+
* floored by a per-pixel σ (max(Δ − k·σ, 0)), max-normalised and squared
|
|
16
|
+
* (Q2RTX get_gradient). gradientStrength scales it into effectiveAlpha =
|
|
17
|
+
* mix(baseAlpha, 1, gradient·gradientStrength) — high change ⇒ drop history
|
|
18
|
+
* (anti-lag). gradientStrength=0 (base presets) keeps pure EMA; the
|
|
19
|
+
* *_adaptive presets enable it.
|
|
18
20
|
*
|
|
19
21
|
* Reads: pathtracer:color, pathtracer:albedo, pathtracer:normalDepth,
|
|
20
22
|
* pathtracer:prevNormalDepth, motionVector:screenSpace
|
|
@@ -34,7 +36,10 @@ export class ASVGF extends RenderStage {
|
|
|
34
36
|
|
|
35
37
|
this.temporalAlpha = uniform( options.temporalAlpha ?? 0.0 );
|
|
36
38
|
this.gradientStrength = uniform( options.gradientStrength ?? 0.0 );
|
|
37
|
-
|
|
39
|
+
// σ multiplier for the per-pixel noise floor (NRD luminanceSigmaScale ≈ 2).
|
|
40
|
+
this.gradientSigmaScale = uniform( options.gradientSigmaScale ?? 2.0 );
|
|
41
|
+
// Secondary relative floor on the normalised gradient (0 = rely on σ alone).
|
|
42
|
+
this.gradientNoiseFloor = uniform( options.gradientNoiseFloor ?? 0.0 );
|
|
38
43
|
this.maxAccumFrames = uniform( options.maxAccumFrames ?? 32.0 );
|
|
39
44
|
|
|
40
45
|
this.resW = uniform( options.width || 1 );
|
|
@@ -43,7 +48,6 @@ export class ASVGF extends RenderStage {
|
|
|
43
48
|
this.temporalEnabledU = uniform( 1.0 );
|
|
44
49
|
|
|
45
50
|
this._colorTexNode = new TextureNode();
|
|
46
|
-
this._prevColorTexNode = new TextureNode();
|
|
47
51
|
this._albedoTexNode = new TextureNode();
|
|
48
52
|
this._motionTexNode = new TextureNode();
|
|
49
53
|
this._normalDepthTexNode = new TextureNode();
|
|
@@ -112,18 +116,6 @@ export class ASVGF extends RenderStage {
|
|
|
112
116
|
stencilBuffer: false
|
|
113
117
|
} );
|
|
114
118
|
|
|
115
|
-
// FloatType to match pathtracer:color (PT MRT). copyTextureToTexture
|
|
116
|
-
// requires identical formats.
|
|
117
|
-
this._prevColorRT = new RenderTarget( w, h, {
|
|
118
|
-
type: FloatType,
|
|
119
|
-
format: RGBAFormat,
|
|
120
|
-
minFilter: NearestFilter,
|
|
121
|
-
magFilter: NearestFilter,
|
|
122
|
-
depthBuffer: false,
|
|
123
|
-
stencilBuffer: false
|
|
124
|
-
} );
|
|
125
|
-
this._prevColorReady = false;
|
|
126
|
-
|
|
127
119
|
this.currentMoments = 0; // 0 = write A, read B; 1 = write B, read A
|
|
128
120
|
this._compiled = false;
|
|
129
121
|
|
|
@@ -162,17 +154,24 @@ export class ASVGF extends RenderStage {
|
|
|
162
154
|
|
|
163
155
|
}
|
|
164
156
|
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
157
|
+
// Adaptive temporal gradient: per pixel, compare this frame's DEMODULATED
|
|
158
|
+
// lighting against the motion-reprojected accumulated history (low-noise),
|
|
159
|
+
// floored by a per-pixel σ from the 5×5 spatial neighbourhood. The σ floor
|
|
160
|
+
// is what the old fixed 0.15 constant couldn't give — on a static scene the
|
|
161
|
+
// 1-SPP sample sits within ±k·σ of the converged estimate so the gradient
|
|
162
|
+
// reads ~0 (no convergence penalty); only real change (moving light, anim,
|
|
163
|
+
// disocclusion) exceeds it. Demodulation (vs raw color) keeps albedo/texture
|
|
164
|
+
// edges from false-firing. Output .x feeds effectiveAlpha in the temporal pass.
|
|
170
165
|
_buildGradientCompute() {
|
|
171
166
|
|
|
172
167
|
const colorTex = this._colorTexNode;
|
|
173
|
-
const
|
|
168
|
+
const albedoTex = this._albedoTexNode;
|
|
174
169
|
const motionTex = this._motionTexNode;
|
|
170
|
+
// Accumulated history (demodulated lighting in .xyz, history count in .w).
|
|
171
|
+
// Same node the temporal pass reads; set to readTemporal before dispatch.
|
|
172
|
+
const histTex = this._readTemporalTexNode;
|
|
175
173
|
const noiseFloor = this.gradientNoiseFloor;
|
|
174
|
+
const sigmaScale = this.gradientSigmaScale;
|
|
176
175
|
const gradientStorageTex = this._gradientStorageTex;
|
|
177
176
|
const resW = this.resW;
|
|
178
177
|
const resH = this.resH;
|
|
@@ -185,7 +184,7 @@ export class ASVGF extends RenderStage {
|
|
|
185
184
|
const WG_THREADS = WG_SIZE * WG_SIZE; // 64
|
|
186
185
|
|
|
187
186
|
const sharedCurLum = workgroupArray( 'float', TILE_TOTAL );
|
|
188
|
-
const
|
|
187
|
+
const sharedHistLum = workgroupArray( 'float', TILE_TOTAL );
|
|
189
188
|
|
|
190
189
|
const computeFn = Fn( () => {
|
|
191
190
|
|
|
@@ -204,20 +203,26 @@ export class ASVGF extends RenderStage {
|
|
|
204
203
|
const gxL = tileOriginX.add( int( sx ) ).clamp( int( 0 ), int( resW ).sub( 1 ) );
|
|
205
204
|
const gyL = tileOriginY.add( int( sy ) ).clamp( int( 0 ), int( resH ).sub( 1 ) );
|
|
206
205
|
|
|
206
|
+
// Demodulated current lighting luminance (matches the temporal pass:
|
|
207
|
+
// safeAlbedo = max(albedo, ALBEDO_EPS) keeps sky/dark-material round-trip).
|
|
207
208
|
const curColor = textureLoad( colorTex, ivec2( gxL, gyL ) ).xyz;
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
const curAlbedo = textureLoad( albedoTex, ivec2( gxL, gyL ) ).xyz;
|
|
210
|
+
const curLighting = curColor.div( max( curAlbedo, vec3( ALBEDO_EPS ) ) );
|
|
211
|
+
const curLum = luminance( curLighting ).toVar();
|
|
212
|
+
sharedCurLum.element( k ).assign( curLum );
|
|
213
|
+
|
|
214
|
+
// Reproject the accumulated history to this pixel; read its lighting
|
|
215
|
+
// luminance. Invalid motion → mirror current so the delta is 0
|
|
216
|
+
// (disocclusion handled by the temporal pass's geometric gate).
|
|
210
217
|
const motion = textureLoad( motionTex, ivec2( gxL, gyL ) );
|
|
211
218
|
const prevXf = float( gxL ).sub( motion.x.mul( resW ) );
|
|
212
219
|
const prevYf = float( gyL ).sub( motion.y.mul( resH ) );
|
|
213
220
|
const prevX = int( prevXf ).clamp( int( 0 ), int( resW ).sub( 1 ) );
|
|
214
221
|
const prevY = int( prevYf ).clamp( int( 0 ), int( resH ).sub( 1 ) );
|
|
215
|
-
// Invalid prev → mirror current so the diff contributes 0;
|
|
216
|
-
// disocclusion is handled by the geometric gate downstream.
|
|
217
222
|
const motionValid = motion.w.greaterThan( 0.5 );
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
223
|
+
const histLighting = textureLoad( histTex, ivec2( prevX, prevY ) ).xyz;
|
|
224
|
+
const histLum = motionValid.select( luminance( histLighting ), curLum );
|
|
225
|
+
sharedHistLum.element( k ).assign( histLum );
|
|
221
226
|
|
|
222
227
|
};
|
|
223
228
|
|
|
@@ -245,8 +250,9 @@ export class ASVGF extends RenderStage {
|
|
|
245
250
|
|
|
246
251
|
If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
|
|
247
252
|
|
|
248
|
-
|
|
249
|
-
const
|
|
253
|
+
// Pass 1 — per-pixel noise σ from the 5×5 (demodulated) neighbourhood.
|
|
254
|
+
const sumLum = float( 0.0 ).toVar();
|
|
255
|
+
const sumLumSq = float( 0.0 ).toVar();
|
|
250
256
|
|
|
251
257
|
for ( let dy = - TILE_BORDER; dy <= TILE_BORDER; dy ++ ) {
|
|
252
258
|
|
|
@@ -256,26 +262,59 @@ export class ASVGF extends RenderStage {
|
|
|
256
262
|
.mul( uint( TILE_W ) )
|
|
257
263
|
.add( lx.add( uint( TILE_BORDER + dx ) ) );
|
|
258
264
|
const cL = sharedCurLum.element( idx );
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
sumMean.addAssign( cL.add( pL ).mul( 0.5 ) );
|
|
265
|
+
sumLum.addAssign( cL );
|
|
266
|
+
sumLumSq.addAssign( cL.mul( cL ) );
|
|
262
267
|
|
|
263
268
|
}
|
|
264
269
|
|
|
265
270
|
}
|
|
266
271
|
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
const meanLum = sumLum.div( 25.0 );
|
|
273
|
+
const variance = max( sumLumSq.div( 25.0 ).sub( meanLum.mul( meanLum ) ), float( 0.0 ) );
|
|
274
|
+
const sigmaFloor = sqrt( variance ).mul( sigmaScale ).toVar();
|
|
275
|
+
|
|
276
|
+
// Pass 2 — 5×5 average of the squared, σ-floored, max-normalised
|
|
277
|
+
// temporal change. Squaring (Q2RTX get_gradient) suppresses residual
|
|
278
|
+
// MC noise while staying reactive in high contrast.
|
|
279
|
+
const sumG = float( 0.0 ).toVar();
|
|
280
|
+
|
|
281
|
+
for ( let dy = - TILE_BORDER; dy <= TILE_BORDER; dy ++ ) {
|
|
282
|
+
|
|
283
|
+
for ( let dx = - TILE_BORDER; dx <= TILE_BORDER; dx ++ ) {
|
|
284
|
+
|
|
285
|
+
const idx = ly.add( uint( TILE_BORDER + dy ) )
|
|
286
|
+
.mul( uint( TILE_W ) )
|
|
287
|
+
.add( lx.add( uint( TILE_BORDER + dx ) ) );
|
|
288
|
+
const cL = sharedCurLum.element( idx );
|
|
289
|
+
const hL = sharedHistLum.element( idx );
|
|
290
|
+
const floored = max( abs( cL.sub( hL ) ).sub( sigmaFloor ), float( 0.0 ) );
|
|
291
|
+
const g = floored.div( max( max( cL, hL ), float( 0.001 ) ) );
|
|
292
|
+
// Optional secondary relative floor (noiseFloor=0 → no-op).
|
|
293
|
+
const gf = max( g.sub( noiseFloor ), float( 0.0 ) )
|
|
294
|
+
.div( max( float( 1.0 ).sub( noiseFloor ), float( 0.0001 ) ) );
|
|
295
|
+
sumG.addAssign( gf.mul( gf ) );
|
|
296
|
+
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const gradientRaw = sumG.div( 25.0 ).clamp( 0.0, 1.0 ).toVar();
|
|
302
|
+
|
|
303
|
+
// Trust the gradient only once enough history has accumulated at the
|
|
304
|
+
// reprojected centre — early frames have noisy history → false fires.
|
|
305
|
+
const cMotion = textureLoad( motionTex, ivec2( gx, gy ) );
|
|
306
|
+
const cPrevX = int( float( gx ).sub( cMotion.x.mul( resW ) ) ).clamp( int( 0 ), int( resW ).sub( 1 ) );
|
|
307
|
+
const cPrevY = int( float( gy ).sub( cMotion.y.mul( resH ) ) ).clamp( int( 0 ), int( resH ).sub( 1 ) );
|
|
308
|
+
const histLen = cMotion.w.greaterThan( 0.5 )
|
|
309
|
+
.select( textureLoad( histTex, ivec2( cPrevX, cPrevY ) ).w, float( 0.0 ) );
|
|
310
|
+
const confidence = histLen.div( 4.0 ).clamp( 0.0, 1.0 );
|
|
311
|
+
|
|
312
|
+
const gradient = gradientRaw.mul( confidence );
|
|
274
313
|
|
|
275
314
|
textureStore(
|
|
276
315
|
gradientStorageTex,
|
|
277
316
|
uvec2( uint( gx ), uint( gy ) ),
|
|
278
|
-
vec4( gradient,
|
|
317
|
+
vec4( gradient, gradientRaw, sigmaFloor, 1.0 )
|
|
279
318
|
).toWriteOnly();
|
|
280
319
|
|
|
281
320
|
} );
|
|
@@ -635,12 +674,6 @@ export class ASVGF extends RenderStage {
|
|
|
635
674
|
const writeTemporal = this.currentMoments === 0
|
|
636
675
|
? this._temporalTexA : this._temporalTexB;
|
|
637
676
|
|
|
638
|
-
// Before first copy seeds the cache, alias current so the gradient
|
|
639
|
-
// sees zero diff (no false boost).
|
|
640
|
-
this._prevColorTexNode.value = this._prevColorReady
|
|
641
|
-
? this._prevColorRT.texture
|
|
642
|
-
: colorTex;
|
|
643
|
-
|
|
644
677
|
// First-frame compile while StorageTexture-typed nodes still hold
|
|
645
678
|
// EmptyTexture, so textureLoad codegen emits the required `level`
|
|
646
679
|
// parameter. Binding StorageTextures only AFTER compile keeps the
|
|
@@ -670,15 +703,6 @@ export class ASVGF extends RenderStage {
|
|
|
670
703
|
|
|
671
704
|
this.renderer.compute( writeNode );
|
|
672
705
|
|
|
673
|
-
// Cache this frame's pathtracer:color for next frame's gradient if it's
|
|
674
|
-
// active. Copy AFTER reads so we don't clobber the prev view.
|
|
675
|
-
if ( needsGradient ) {
|
|
676
|
-
|
|
677
|
-
this.renderer.copyTextureToTexture( colorTex, this._prevColorRT.texture );
|
|
678
|
-
this._prevColorReady = true;
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
|
|
682
706
|
// Copy active region out of the over-allocated StorageTextures into
|
|
683
707
|
// right-sized RTs; downstream stages UV-sample these.
|
|
684
708
|
this._srcRegion.max.set( this.resW.value, this.resH.value );
|
|
@@ -733,6 +757,7 @@ export class ASVGF extends RenderStage {
|
|
|
733
757
|
if ( ! params ) return;
|
|
734
758
|
if ( params.temporalAlpha !== undefined ) this.temporalAlpha.value = params.temporalAlpha;
|
|
735
759
|
if ( params.gradientStrength !== undefined ) this.gradientStrength.value = params.gradientStrength;
|
|
760
|
+
if ( params.gradientSigmaScale !== undefined ) this.gradientSigmaScale.value = params.gradientSigmaScale;
|
|
736
761
|
if ( params.gradientNoiseFloor !== undefined ) this.gradientNoiseFloor.value = params.gradientNoiseFloor;
|
|
737
762
|
if ( params.maxAccumFrames !== undefined ) this.maxAccumFrames.value = params.maxAccumFrames;
|
|
738
763
|
if ( params.debugMode !== undefined ) this.debugMode.value = params.debugMode;
|
|
@@ -742,8 +767,6 @@ export class ASVGF extends RenderStage {
|
|
|
742
767
|
resetTemporalData() {
|
|
743
768
|
|
|
744
769
|
this.currentMoments = 0;
|
|
745
|
-
// Drop cache so post-reset frames don't see pre-reset prev color.
|
|
746
|
-
this._prevColorReady = false;
|
|
747
770
|
|
|
748
771
|
}
|
|
749
772
|
|
|
@@ -756,8 +779,6 @@ export class ASVGF extends RenderStage {
|
|
|
756
779
|
this._outputRT.texture.needsUpdate = true;
|
|
757
780
|
this._gradientRT.setSize( width, height );
|
|
758
781
|
this._gradientRT.texture.needsUpdate = true;
|
|
759
|
-
this._prevColorRT.setSize( width, height );
|
|
760
|
-
this._prevColorRT.texture.needsUpdate = true;
|
|
761
782
|
this.heatmapTarget.setSize( width, height );
|
|
762
783
|
this.heatmapTarget.texture.needsUpdate = true;
|
|
763
784
|
this.resW.value = width;
|
|
@@ -775,8 +796,6 @@ export class ASVGF extends RenderStage {
|
|
|
775
796
|
// would dispatch both temporal ping-pong nodes while _readTemporalTexNode still
|
|
776
797
|
// aliases one node's write target, producing a "write-only storage +
|
|
777
798
|
// TextureBinding in same synchronization scope" validation error.
|
|
778
|
-
// Only the size-dependent prev-color cache needs re-seeding.
|
|
779
|
-
this._prevColorReady = false;
|
|
780
799
|
|
|
781
800
|
}
|
|
782
801
|
|
|
@@ -799,13 +818,11 @@ export class ASVGF extends RenderStage {
|
|
|
799
818
|
this._demodulatedRT?.dispose();
|
|
800
819
|
this._outputRT?.dispose();
|
|
801
820
|
this._gradientRT?.dispose();
|
|
802
|
-
this._prevColorRT?.dispose();
|
|
803
821
|
this._heatmapComputeNode?.dispose();
|
|
804
822
|
this._heatmapStorageTex?.dispose();
|
|
805
823
|
this.heatmapTarget?.dispose();
|
|
806
824
|
|
|
807
825
|
this._colorTexNode?.dispose();
|
|
808
|
-
this._prevColorTexNode?.dispose();
|
|
809
826
|
this._albedoTexNode?.dispose();
|
|
810
827
|
this._motionTexNode?.dispose();
|
|
811
828
|
this._normalDepthTexNode?.dispose();
|
|
@@ -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, writeGBufferSurfaceID,
|
|
23
23
|
writeMediumStack,
|
|
24
24
|
} from '../Processor/PackedRayBuffer.js';
|
|
25
25
|
|
|
@@ -91,6 +91,8 @@ export function buildGenerateKernel( params ) {
|
|
|
91
91
|
|
|
92
92
|
// default: normal +Z, depth 1 (far), black albedo (background/miss)
|
|
93
93
|
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 ) );
|
|
94
96
|
|
|
95
97
|
} );
|
|
96
98
|
|
package/src/TSL/ShadeKernel.js
CHANGED
|
@@ -48,9 +48,9 @@ import {
|
|
|
48
48
|
readMediumStack, writeMediumStack, readMediumSigmaA, writeMediumSigmaA,
|
|
49
49
|
readPathBounces, readSssSteps, readSSSMedium, writeSSSMedium,
|
|
50
50
|
readHitDistance, readHitBarycentrics, readHitNormal,
|
|
51
|
-
readHitMaterialIndex, readHitTriangleIndex,
|
|
51
|
+
readHitMaterialIndex, readHitTriangleIndex, readHitMeshIndex,
|
|
52
52
|
writeRayOriginMeta, writeRayDirFlags, writeRayThroughputPdf, writeRayRadiance,
|
|
53
|
-
writeGBuffer, readGBuffer, gbDecodeNormalDepth,
|
|
53
|
+
writeGBuffer, writeGBufferSurfaceID, readGBuffer, gbDecodeNormalDepth,
|
|
54
54
|
readRayRadiance,
|
|
55
55
|
} from '../Processor/PackedRayBuffer.js';
|
|
56
56
|
|
|
@@ -374,6 +374,9 @@ export function buildShadeKernel( params ) {
|
|
|
374
374
|
If( sampleIndex.equal( int( 0 ) ), () => {
|
|
375
375
|
|
|
376
376
|
writeGBuffer( gBufferRW, pixelIndex, vec3( 0.0, 0.0, 1.0 ), linearDepth, vec3( 0.0 ) );
|
|
377
|
+
// Persist the primary-hit surface ID (Tier-1 A-SVGF correlated re-projection). Hit-only
|
|
378
|
+
// branch (misses Return above), so this marks the pixel valid; bary from the bounce-0 hit.
|
|
379
|
+
writeGBufferSurfaceID( gBufferRW, pixelIndex, hitTriIdx, readHitMeshIndex( hitBufferRO, rayID ), hitUV.x, hitUV.y, uint( 1 ) );
|
|
377
380
|
|
|
378
381
|
} );
|
|
379
382
|
|