#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