rayzee 7.9.0 → 7.9.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rayzee",
3
- "version": "7.9.0",
3
+ "version": "7.9.2",
4
4
  "type": "module",
5
5
  "description": "Real-time WebGPU path tracing engine built on Three.js",
6
6
  "main": "dist/rayzee.umd.js",
@@ -1,4 +1,4 @@
1
- import { Fn, vec3, vec4, float, int, uint, uvec2, uniform, normalize, mat3, storage, If,
1
+ import { Fn, vec3, vec4, float, int, uint, uvec2, uniform, normalize, mat3, min, storage, If,
2
2
  textureStore, workgroupId, localId } from 'three/tsl';
3
3
  import { RenderTarget, StorageTexture } from 'three/webgpu';
4
4
  import { HalfFloatType, RGBAFormat, NearestFilter, Matrix4, Box2, Vector2 } from 'three';
@@ -234,7 +234,10 @@ export class NormalDepth extends RenderStage {
234
234
  const hit = HitInfo.wrap( traverseBVH( ray, bvhStorage, triStorage ) );
235
235
 
236
236
  const encodedNormal = hit.normal.mul( 0.5 ).add( 0.5 );
237
- const depth = hit.dst;
237
+ // Clamp to the max finite HalfFloat: a hit farther than 65504 units would otherwise
238
+ // round to +Inf in the f16 texture (on backends that don't clamp), reintroducing the
239
+ // Inf-Inf=NaN in the denoiser depth weights that the finite miss-sentinel avoids.
240
+ const depth = min( hit.dst, float( 65504.0 ) );
238
241
 
239
242
  const result = hit.didHit.select(
240
243
  vec4( encodedNormal, depth ),
@@ -66,6 +66,11 @@ export class PathTracer extends PathTracerStage {
66
66
  this._readbackFrameCounter = 0;
67
67
  // Bumped on resolution change; a readback that resolves with a stale generation is dropped.
68
68
  this._readbackGeneration = 0;
69
+ // Whether the survivor curve may be trusted to SIZE the dispatch. False during/after camera motion
70
+ // (or resize) until a readback measured at the settled view re-validates it — full-size until then so
71
+ // a stale/mid-motion curve can't under-size the row-major list and drop the bottom rows. The curve is
72
+ // still used for the per-bounce early-exit regardless (a stale early-exit only trims empty deep bounces).
73
+ this._curveSizingValid = false;
69
74
  // 0.1% of primary ray count, floored at 100; -1 to disable. Updated per-scene in _buildWavefrontKernels.
70
75
  this._bounceEarlyExitThreshold = 100;
71
76
 
@@ -193,6 +198,18 @@ export class PathTracer extends PathTracerStage {
193
198
 
194
199
  this.cameraChanged = this._updateCameraUniforms();
195
200
  this.cameraOptimizer?.updateInteractionMode( this.cameraChanged );
201
+ // While the camera moves, the survivor curve reflects a pose we're leaving (async readback lag), so
202
+ // trusting it to SIZE the dispatch would under-size the row-major list and drop the tail (bottom
203
+ // rows) — the streaks during motion and the flash when it stops. Mark the curve untrusted-for-sizing
204
+ // (→ full-size those frames) until a readback measured at the settled view re-validates it; bump the
205
+ // generation so any in-flight readback is discarded. The curve itself is kept for the early-exit.
206
+ if ( this.cameraChanged ) {
207
+
208
+ this._curveSizingValid = false;
209
+ this._readbackGeneration ++;
210
+
211
+ }
212
+
196
213
  this._updateAccumulationUniforms( frameValue, renderMode );
197
214
  this.frame.value = frameValue;
198
215
 
@@ -266,8 +283,11 @@ export class PathTracer extends PathTracerStage {
266
283
  if ( useFunctionalCompaction ) {
267
284
 
268
285
  // ENTERING_COUNT already set (bounce 0 by initActiveIndices, N>0 by snapshotBounceCount); size from last frame's survivor curve with a 1.5×+1024 margin.
286
+ // Only trust the curve for sizing when it was measured at the CURRENT (settled) view; while
287
+ // the camera moves / before a settled readback, full-size (entering = maxRays) so the tail is
288
+ // never dropped. Early-exit below still uses the curve regardless (a stale one only trims empty deep bounces).
269
289
  let entering = maxRays;
270
- if ( bounce > 0 ) {
290
+ if ( bounce > 0 && this._curveSizingValid ) {
271
291
 
272
292
  const idx = bounce - 1;
273
293
  let prev;
@@ -405,6 +425,15 @@ export class PathTracer extends PathTracerStage {
405
425
  // Async readback of the per-bounce snapshot every N frames; never awaited, so the early-exit uses past-frame data.
406
426
  _maybeReadbackCounters() {
407
427
 
428
+ // Never sample the survivor curve mid-motion — those counts belong to a pose we're leaving and
429
+ // would mis-size the first settled frame. Prime the counter so the settled view re-measures promptly.
430
+ if ( this.cameraChanged ) {
431
+
432
+ this._readbackFrameCounter = this._readbackEveryNFrames;
433
+ return;
434
+
435
+ }
436
+
408
437
  if ( this._readbackPending ) return;
409
438
 
410
439
  this._readbackFrameCounter ++;
@@ -419,11 +448,14 @@ export class PathTracer extends PathTracerStage {
419
448
  const budget = this.maxBounces.value;
420
449
  this.renderer.getArrayBufferAsync( attr ).then( ( buf ) => {
421
450
 
422
- // Drop counts measured at a now-stale resolution (a resize happened mid-flight).
451
+ // Drop counts measured at a now-stale generation (a resize or camera move happened mid-flight).
452
+ // A surviving readback was initiated while settled (init is skipped mid-motion) and no motion
453
+ // happened before it resolved, so its counts match the current view — safe to size from.
423
454
  if ( gen === this._readbackGeneration ) {
424
455
 
425
456
  this._lastBounceCounts = new Uint32Array( buf.slice( 0 ) );
426
457
  this._lastBounceCountsBudget = budget;
458
+ this._curveSizingValid = true;
427
459
 
428
460
  }
429
461
 
@@ -468,6 +500,7 @@ export class PathTracer extends PathTracerStage {
468
500
  // in flight (carrying the old-resolution counts) is discarded when it resolves.
469
501
  this._lastBounceCounts = null;
470
502
  this._lastBounceCountsBudget = - 1;
503
+ this._curveSizingValid = false;
471
504
  this._readbackFrameCounter = 0;
472
505
  this._readbackGeneration ++;
473
506