When you apply a Gaussian “blur” (i.e. convolution with a Gaussian kernel) to a signal, you’re effectively doing low-pass filtering that is symmetric in time. That symmetry is what gives it a zero phase shift—but it also means the filter is non-causal, since it depends on future as well as past samples.
In a truly real‑time (causal) system like Unity’s, you can only work with past (and current) data. If you want to exactly cancel phase shifts by filtering both forward and backward (as in MATLAB’s filtfilt), you must buffer data and introduce a delay (roughly half your filter window). In other words, there’s a trade‑off:
- Zero‑phase filtering: Achieved by applying the filter forward and then backward. This cancels phase shift but requires you to wait for future data (or use a delay buffer), which adds latency.
- Causal filtering: You only use past data. This avoids buffering but will introduce some phase delay (and sometimes distortion) unless you use very clever predictive techniques.
Options to Consider in Unity/C#:
-
Forward–Backward Filtering (Non-Causal Zero-Phase):
If a small, fixed delay is acceptable, you can buffer your signal (say, a few frames or samples) and apply the filter in both directions. This will yield a zero-phase result at the cost of a delay of roughly half the window size.
-
Causal Low-Pass Filter with Minimal Delay:
You might design or use an IIR or FIR filter that approximates a Gaussian response. Although it won’t be perfectly zero‑phase, you can often tune it so the delay is minimal. For instance, a well-tuned exponential moving average (EMA) filter is simple and fast, though it inherently lags the input.
-
Predictive Filtering (e.g. Kalman Filter):
If you’re filtering noisy sensor data or similar, a Kalman filter can help “predict” the true signal while keeping the delay minimal. It won’t be the same as a Gaussian blur, but it can be very effective for real‑time applications.
Summary:
A true zero‑phase (symmetric Gaussian) filter isn’t possible in a strictly causal, real‑time system without buffering—and that buffering introduces a delay. So you must decide on the acceptable trade‑off between phase accuracy and latency.
If you’d like, I can help sketch out a C# example for one of these approaches. Which direction would you like to explore?