@@ -1401,6 +1401,7 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
14011401 }
14021402
14031403 var n_hist = 0.0;
1404+ var seeded = false;
14041405 if (wsum > 1e-3) {
14051406 hist_rgb /= wsum;
14061407 hist_m1 /= wsum;
@@ -1430,8 +1431,11 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
14301431 var seed_m2 = 0.0;
14311432 var seed_n = 0.0;
14321433 var seed_w = 0.0;
1433- for (var by = -2; by <= 2; by = by + 1) {
1434- for (var bx = -2; bx <= 2; bx = bx + 1) {
1434+ // 7x7: under TRANSLATION a whole COLUMN of texels streams in per
1435+ // frame (rotation only trickles a few px), so a 5x5 often found
1436+ // nothing but fellow newborns and the band stayed raw.
1437+ for (var by = -3; by <= 3; by = by + 1) {
1438+ for (var bx = -3; bx <= 3; bx = bx + 1) {
14351439 if (bx == 0 && by == 0) { continue; }
14361440 let q = vec2<i32>(i32(gid.x) + bx, i32(gid.y) + by);
14371441 if (q.x < 0 || q.y < 0 || q.x >= i32(u.size.x) || q.y >= i32(u.size.y)) {
@@ -1454,6 +1458,7 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
14541458 hist_m1 = seed_m1 / seed_w;
14551459 hist_m2 = seed_m2 / seed_w;
14561460 n_hist = min((seed_n / seed_w) * 0.5, 8.0);
1461+ seeded = true;
14571462 }
14581463 }
14591464 // Canonical blend: cumulative average while the history is
@@ -1472,7 +1477,18 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
14721477 // wavelet filter's luminance sigma. Young history makes this
14731478 // unreliable; the first à-trous iteration substitutes a
14741479 // spatial estimate when n < 4 (accum.w carries n via moments).
1475- let variance = max(m2 - m1 * m1, 0.0);
1480+ var variance = max(m2 - m1 * m1, 0.0);
1481+ // A newborn with NOTHING to borrow is a 1-sample estimate whose true
1482+ // variance is unknown — not zero, which is what m2 - m1² of a single
1483+ // value degenerates to. Zero variance tells the wavelet the pixel is
1484+ // CONVERGED, so the raw outlier survived every iteration: that was
1485+ // the residual noise band on wide stream-ins under camera
1486+ // translation. Write a frank variance instead so the à-trous blurs
1487+ // these pixels hard; one converged frame later the real statistics
1488+ // take over.
1489+ if (n_new <= 1.5 && !seeded) {
1490+ variance = max(l_new * l_new, 0.25);
1491+ }
14761492 accum_out[idx] = vec4<f32>(out_irr, variance);
14771493 moments_out[idx] = vec4<f32>(m1, m2, n_new, depth);
14781494 if (debug == 22.0) {
0 commit comments