Skip to content

Commit 5a5c6b8

Browse files
committed
Fix precision issues, use operators
1 parent ee7f087 commit 5a5c6b8

15 files changed

Lines changed: 872 additions & 202 deletions

File tree

apps/typegpu-docs/src/examples/image-processing/camera-fft/index.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ const filterParamsType = d.struct({
146146
padHMask: d.u32,
147147
/** Normalized low-pass radius vs max toroidal √(hw²+hh²); keep r ≤ cutoff. */
148148
lowPassCutoff: d.f32,
149-
/** Normalized high-pass inner radius; keep r > cutoff (DC at r = 0 is removed when cutoff = 0). */
149+
/** Normalized high-pass inner radius; keep `r > cutoff`. `0` = high-pass **off** (do not attenuate DC or low r). */
150150
highPassCutoff: d.f32,
151151
/** 1 when `Fft2d.skipFinalTranspose`: spectrum is `W×H` row-major with stride `padH` (`r*padH+c`), not `y*padW+x`. */
152152
swapSpectrumAxes: d.u32,
@@ -199,7 +199,9 @@ const filterKernel = tgpu.computeFn({
199199
const lowC = filterLayout.$.params.lowPassCutoff * rMax;
200200
const highC = filterLayout.$.params.highPassCutoff * rMax;
201201
const lowMask = std.select(d.f32(0), d.f32(1), r <= lowC);
202-
const highMask = std.select(d.f32(0), d.f32(1), r > highC);
202+
const highPassOff = filterLayout.$.params.highPassCutoff <= d.f32(0);
203+
const highMaskInner = std.select(d.f32(0), d.f32(1), r > highC);
204+
const highMask = std.select(highMaskInner, d.f32(1), highPassOff);
203205
const mask = lowMask * highMask;
204206
const c = filterLayout.$.spectrum[tid];
205207
filterLayout.$.spectrum[tid] = d.vec2f(c.x * mask, c.y * mask);
@@ -255,16 +257,15 @@ const magKernel = tgpu.computeFn({
255257
magLayout.$.params.swapSpectrumAxes !== d.u32(0),
256258
);
257259
const cShift = magLayout.$.spectrum[srcTid];
258-
const len = std.sqrt(cShift.x * cShift.x + cShift.y * cShift.y);
259-
/** Neutral at `exposure = 0` matches the former default `log(1+|·|) * 0.2` scale. */
260-
const logv =
261-
std.log(1.0 + len) * 0.2 * std.exp2(magLayout.$.params.exposure);
260+
const lenRaw = std.sqrt(cShift.x * cShift.x + cShift.y * cShift.y);
261+
/** Log stretch tuned on these magnitudes (`log(1+|·|) * 0.2`). */
262+
const logv = std.log(1.0 + lenRaw) * 0.2 * std.exp2(magLayout.$.params.exposure);
262263
const cv = std.clamp(logv, 0.0, 1.0);
263264
/** `cv` from log-magnitude; L ∈ [0.04, 1]; chroma → 0 at max `cv` so peaks go neutral white. */
264265
const L = 0.04 + cv * (1.0 - 0.04);
265266
const chroma = cv * (1.0 - cv) * 0.32;
266267
/** Oklab a,b = chroma × unit phase — same as chroma·(cos θ, sin θ) with θ = atan2(im, re), without trig. */
267-
const invLen = 1.0 / std.max(len, 1e-20);
268+
const invLen = 1.0 / std.max(lenRaw, 1e-20);
268269
const lab = d.vec3f(L, chroma * invLen * cShift.x, chroma * invLen * cShift.y);
269270
const rgb = oklabToLinearRgb(oklabGamutClip.adaptiveL05(lab));
270271
std.textureStore(magLayout.$.outTex, d.vec2u(xLin, yLin), d.vec4f(rgb, 1));
@@ -275,7 +276,7 @@ const spatialParamsType = d.struct({
275276
padH: d.u32,
276277
padWLog2: d.u32,
277278
padWMask: d.u32,
278-
/** `1 / (padW * padH)` — unnormalized inverse scaling. */
279+
/** Linear gain on real part after inverse FFT (`1` = full amplitude in buffer). */
279280
invSize: d.f32,
280281
/** Same EV as spectrum: linear sample is multiplied by `exp2(exposure)` before clamp. */
281282
exposure: d.f32,
@@ -490,7 +491,7 @@ let applyInverseFft = false;
490491
let exposureEv = 0;
491492
/** Normalized low-pass radius vs max toroidal radius (1 = no attenuation). */
492493
let lowPassCutoffNorm = 1;
493-
/** Normalized high-pass inner radius (0 = no AC attenuation from this term). */
494+
/** Normalized high-pass inner radius (`0` = high-pass off; spectrum passes through to low-pass only). */
494495
let highPassCutoffNorm = 0;
495496
/** Separable Hann window on camera ROI before FFT (reduces periodic-boundary cross in spectrum). */
496497
let applyEdgeWindow = false;
@@ -757,7 +758,7 @@ function processVideoFrame(_: number, metadata: VideoFrameCallbackMetadata) {
757758
padH,
758759
padWLog2,
759760
padWMask: padW - 1,
760-
invSize: 1 / (padW * padH),
761+
invSize: 1,
761762
exposure: exposureEv,
762763
});
763764
}
@@ -868,8 +869,8 @@ export const controls = defineControls({
868869
},
869870
exposure: {
870871
initial: exposureEv,
871-
min: -4,
872-
max: 4,
872+
min: -8,
873+
max: 8,
873874
step: 0.05,
874875
onSliderChange: (value) => {
875876
exposureEv = value;

0 commit comments

Comments
 (0)