diff --git a/ntsc/ntsc-xot.slangp b/ntsc/ntsc-xot.slangp new file mode 100644 index 00000000..4293d1f5 --- /dev/null +++ b/ntsc/ntsc-xot.slangp @@ -0,0 +1,9 @@ +shaders = 2 + +shader0 = ../stock.slang +scale_type_x0 = absolute +scale_x0 = "640.0" + +shader1 = shaders/ntsc-xot.slang +scale1 = 1.0 +scale_type1 = source diff --git a/ntsc/shaders/ntsc-xot.slang b/ntsc/shaders/ntsc-xot.slang new file mode 100644 index 00000000..d15cddeb --- /dev/null +++ b/ntsc/shaders/ntsc-xot.slang @@ -0,0 +1,228 @@ +#version 450 + +// NTSC Decoder +// +// Decodes composite video signal generated in Buffer A. +// Move mouse to display the original signal. +// +// This is an intensive shader with a lot of sampling and +// iterated filtering. Reduce filter width N to trade quality +// for performance. N should be an integer multiple of four, +// plus one (4n+1). Apologies to owners of melted phones. +// +// hunterk made the shader work in RGB instead of just a single +// channel, though there's probably better ways to do it than +// just tripling all of the operations. Improvements are welcome! +// +// copyright (c) 2017, John Leffingwell +// license CC BY-SA Attribution-ShareAlike +// adapted for RetroAch by hunterk from this shadertoy: +// https://www.shadertoy.com/view/Mdffz7 + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + +#pragma stage vertex +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; + +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +#define PI 3.14159265358979323846 +#define TAU 6.28318530717958647693 + +// TV adjustments +const float SAT = 1.0; // Saturation / "Color" (normally 1.0) +const float HUE = 1.0; // Hue / "Tint" (normally 0.0) +const float BRI = 1.0; // Brightness (normally 1.0) + +// Filter parameters +const int N = 21; // Filter Width (4n+1) +const float FC = 0.125; // Frequency Cutoff + + +const mat3 YIQ2RGB = mat3(1.000, 1.000, 1.000, + 0.956,-0.272,-1.106, + 0.621,-0.647, 1.703); + +vec3 adjust(vec3 YIQ, float H, float S, float B) { + mat3 M = mat3( B, 0.0, 0.0, + 0.0, S*cos(H), -sin(H), + 0.0, sin(H), S*cos(H) ); + return M * YIQ; +} + +float sinc(float n) { + if (n == 0.0) return 1.0; + return sin(PI*n) / (PI*n); +} + +float window_blackman(float n, float N) { + return 0.42 - 0.5 * cos((2.0*PI*n)/(N-1.0)) + 0.08 * cos((4.0*PI*n)/(N-1.0)); +} + +float pulse(float a, float b, float x) { + return step(a, x) * step(x, b); +} + +void main() +{ + vec2 size = params.SourceSize.xy; + vec2 uv = vTexCoord.xy; + + // Compute sampling offsets and weights + float sumR = 0.0; + float sumG = 0.0; + float sumB = 0.0; + vec4 offsetR[N]; + vec4 offsetG[N]; + vec4 offsetB[N]; + + // R + for (int i=0; i