summaryrefslogtreecommitdiff
path: root/src/effects
diff options
context:
space:
mode:
Diffstat (limited to 'src/effects')
-rw-r--r--src/effects/ntsc.wgsl65
1 files changed, 36 insertions, 29 deletions
diff --git a/src/effects/ntsc.wgsl b/src/effects/ntsc.wgsl
index a701a25..ea2fb59 100644
--- a/src/effects/ntsc.wgsl
+++ b/src/effects/ntsc.wgsl
@@ -57,28 +57,43 @@ fn peak(y: f32, ypos: f32, scale: f32) -> f32 {
return clamp((y - 1.) * scale * log(abs(y - ypos)), 0.0, 1.0);
}
-// 12-taps horizontal filtering
-const luma_filter = array<f32, 2 * 12 + 1>(
- 0.0105, 0.0134, 0.0057,-0.0242,-0.0824,
- -0.1562,-0.2078,-0.1850,-0.0546, 0.1626,
- 0.3852, 0.5095, 0.5163, 0.4678, 0.2844,
- 0.0515,-0.1308,-0.2082,-0.1891,-0.1206,
- -0.0511,-0.0065, 0.0114, 0.0127, 0.008
+// 6-taps Luma horizontal filtering
+// fs = 3.84 MHz (Nyquist 1.92 MHz)
+// Passband: 0–2.8 MHz
+// Stopband: 3.4–3.84 MHz (>20 dB atten.)
+// => firpm(12, [0 2.8/3.842 3.4/3.842 1], [1 1 0 0])
+const luma_filter = array<f32, 2 * 6 + 1>(
+ 0.0102, 0.0214, 0.0387, -0.0018, -0.0785, -0.1572,
+ -0.1698,
+ 0.1275, 0.4924, 0.5381, 0.4924, 0.1275, -0.1698
);
-const chroma_filter = array<f32, 2 * 12 + 1>(
- 0.001, 0.0010, 0.0001, 0.0002, -0.0003,
- 0.0062, 0.0120,-0.0079, 0.0978, 0.1059,
- -0.0394, 0.2732, 0.2941, 0.1529, -0.021,
- 0.1347, 0.0415,-0.0032, 0.0115, 0.002,
- -0.0001, 0.0002, 0.001, 0.001, 0.001
+// Chroma:
+// fs = 3.84 MHz
+// Passband: 3.3–3.7 MHz (around fsc)
+// Stopbands: 0–2.5 MHz (>40 dB) and 3.9+ MHz
+// => firpm(12, [0 2.5/1.92 3.3/1.92 3.7/1.92 1], [0 0 1 1 0])
+const chroma_filter = array<f32, 2 * 6 + 1>(
+ -0.0123, -0.0456, -0.0892, 0.0234, 0.1678, 0.2984,
+ 0.3456,
+ 0.0000, 0.3456, 0.2984, 0.1678, 0.0234, -0.0892
);
+fn get_signal(uv: vec2f, d: f32) -> vec4f {
+ var signal = vec4f(0.0);
+ for (var i = 0; i <= 12; i += 1) {
+ let offset = f32(i) - 6.0;
+ let suml = luma_filter[i] * get_value(uv, offset * d, 0.67);
+ let sumc = chroma_filter[i] * get_value(uv, offset * d * CHROMA_SIZE, 0.67);
+ signal += vec4f(suml.x, sumc.y, sumc.z, suml.a);
+ }
+ let base = get_luma_chroma_phase_a(uv);
+ return mix(signal, base, vec4f(LUMA_BLUR, CHROMA_BLUR, CHROMA_BLUR, 1.));
+}
+
fn randomized_f32(p: vec2f, t: f32) -> f32 {
return hash_2f_alt(vec2f(p * 0.152 + t * 1500. + 50.0));
}
-
-
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let t = uniforms.time;
let bt = uniforms.beat_phase;
@@ -99,25 +114,14 @@ fn randomized_f32(p: vec2f, t: f32) -> f32 {
// luma fringing
let d = (BLUR_SIZE + y_interf * 100.0) / XSIZE;
- var lc_signal = vec4f(0.0);
- for (var i = 0; i < 25; i += 1) {
- let offset = f32(i) - 12.0;
- let suml = luma_filter[i] * get_value(uv, offset * d, 0.67);
- let sumc = chroma_filter[i] * get_value(uv, offset * d * CHROMA_SIZE, 0.67);
- lc_signal += vec4f(suml.x, sumc.y, sumc.z, suml.a);
- }
- let base = get_luma_chroma_phase_a(uv);
- var signal = mix(base, lc_signal, vec4f(LUMA_BLUR, CHROMA_BLUR, CHROMA_BLUR, 1.));
+ var signal = get_signal(uv, d);
// luma / chroma saturation
let lchroma = signal.y * CHROMA_SATURATION;
let phase = signal.z * TAU;
-
signal.x *= LUMA_BRIGHTNESS;
signal.y = lchroma * sin(phase);
signal.z = lchroma * cos(phase);
- // Slight NTSC warm tint (boost red/green, attenuate blue)
- signal *= vec4f(1.04, 1.01, .94, 1.);
// color subcarrier signal, crosstalk
let chroma_phase = t * 60.0 * PI * 0.6667;
@@ -130,15 +134,18 @@ fn randomized_f32(p: vec2f, t: f32) -> f32 {
signal.y *= 1.0 + scarrier * i_mod;
signal.z *= 1.0 + scarrier * q_mod;
+ // convert back to rgb
var col = yiqa_to_rgba(signal);
- col = Dither(col, uv, XSIZE, YSIZE);
+ // Slight NTSC warm tint (boost red/green, attenuate blue)
+ col *= vec4f(1.04, 1.01, .94, 1.);
+// col = Dither(col, uv, XSIZE, YSIZE);
let border_col = get_border_c64(uv, uniforms.beat_time, YSIZE);
let v_strength = vignette(uv);
let scanl = 0.82 + 0.5 * sin(PI * uv.y * uniforms.resolution.y / 2.);
col = scanl * mix(border_col, col, v_strength);
- col = clamp(col, vec4f(0., 0., 0., 1.), vec4f(1.0));
+ col = clamp(col, vec4f(0.), vec4f(1.0));
// Black outside screen edges
if (uv.x <= 0.0 || uv.x >= 1.0 || uv.y <= 0.0 || uv.y >= 1.0) {