/*
	VHS Pro by Vladmir Storm
	
	Ported by Marty McFly and Matsilagi

	Updated for ReShade 4+ by Marot Satil and Bapho
*/

#include "ReShade.fxh"

uniform int screenLinesNum <
	ui_type = "slider";
	ui_min = 1;
	ui_max = BUFFER_HEIGHT;
	ui_step = 1;
	ui_label = "Screen Resolution [VHSPro]";
	ui_tooltip = "Screen Resolution (in lines).";
> = BUFFER_HEIGHT;

uniform bool VHS_Bleed <
	ui_label = "Bleeding [VHSPro]";
	ui_tooltip = "Enables beam screen bleeding (makes the image blurry).";
> = true;

uniform int VHS_BleedMode <
	ui_type = "combo";
	ui_items = "Three Phase\0Old Three Phase\0Two Phase (slow)\0Three-Phase (RetroArch)\0Two Phase (RetroArch)\0";
	ui_label = "Bleeding Mode [VHSPro]";
	ui_tooltip = "Toggles between different bleeding modes.";
> = 0;

uniform float bleedAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 15.0;
	ui_label = "Bleed Stretch [VHSPro]";
	ui_tooltip = "Length of the bleeding.";
> = 1.0;

uniform bool VHS_FishEye <
	ui_label = "Fisheye [VHSPro]";
	ui_tooltip = "Enables a CRT Curvature.";
> = true;

uniform bool VHS_FishEye_Hyperspace <
	ui_label = "Fisheye Hyperspace [VHSPro]";
	ui_tooltip = "Changes the curvature to look like some sort of hyperspace warping.";
> = false;

uniform float fisheyeBend <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 50.0;
	ui_label = "Fisheye Bend [VHSPro]";
	ui_tooltip = "Curvature of the CRT.";
> = 2.0;

uniform float cutoffX <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 50.0;
	ui_label = "Fisheye Cutoff X [VHSPro]";
	ui_tooltip = "Cutoff of the Horizontal Borders.";
> = 2.0;

uniform float cutoffY <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 50.0;
	ui_label = "Fisheye Cutoff Y [VHSPro]";
	ui_tooltip = "Cutoff of the Vertical Borders.";
> = 3.0;

uniform float cutoffFadeX <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 50.0;
	ui_label = "Fisheye Cutoff Fade X [VHSPro]";
	ui_tooltip = "Size of the Horizontal gradient cutoff.";
> = 25.0;

uniform float cutoffFadeY <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 50.0;
	ui_label = "Fisheye Cutoff Fade Y [VHSPro]";
	ui_tooltip = "Size of the Vertical gradient cutoff.";
> = 25.0;

uniform bool VHS_Vignette <
	ui_type = "bool";
	ui_label = "Vignette [VHSPro]";
	ui_tooltip = "Enables screen vignetting";
> = false;

uniform float vignetteAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 5.0;
	ui_label = "Vignette Amount [VHSPro]";
	ui_tooltip = "Strength of the vignette.";
> = 1.0;

uniform float vignetteSpeed <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 5.0;
	ui_label = "Vignette Pulse Speed [VHSPro]";
	ui_tooltip = "Speed of the vignette pulsing. (Setting it to 0 makes it stop pulsing)";
> = 1.0;

uniform int noiseLinesNum <
	ui_type = "slider";
	ui_min = 1;
	ui_max = BUFFER_HEIGHT;
	ui_label = "Vertical Resolution [VHSPro]";
	ui_tooltip = "Noise Resolution (in lines).\n\nIn order to use this setting as shown, please install the \"UIBind\" addon created by cot6.\n\nAlternatively, you must also set this value in the Preprocessor Definitions below by adjusting noiseLinesRes";
	ui_bind = "noiseLinesRes";
> = 240;

#ifndef noiseLinesRes
    #define noiseLinesRes 240    //Vertical Resolution (to use in _TapeTex, has to be the same as noiseLinesNum)
#endif

uniform float noiseQuantizeX <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.0;
	ui_label = "Quantize Noise X [VHSPro]";
	ui_tooltip = "Makes the noise longer or shorter.";
> = 0.0;

uniform bool VHS_FilmGrain <
	ui_label = "Film Grain [VHSPro]";
	ui_tooltip = "Enables a Film Grain on the screen.";
> = false;

uniform float filmGrainAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 0.1;
	ui_label = "Film Grain Alpha [VHSPro]";
	ui_tooltip = "Intensity of the Film Grain.";
> = 0.016;

uniform bool VHS_YIQNoise <
	ui_label = "Signal Noise [VHSPro]";
	ui_tooltip = "Adds noise to the YIQ Signal, causing a Pink (or green) noise.";
> = true;

uniform int signalNoiseType <
	ui_type = "combo";
	ui_items = "Type 1\0Type 2\0Type 3\0";
	ui_label = "Signal Noise Type [VHS Pro]";
> = 0;

uniform float signalNoiseAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.0;
	ui_label = "Signal Noise Amount [VHSPro]";
	ui_tooltip = "Amount of the signal noise.";
> = 0.15;

uniform float signalNoisePower <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.0;
	ui_label = "Signal Noise Power [VHSPro]";
	ui_tooltip = "Power of the signal noise. Higher values will make it green, lower values will make it more pink.";
> =  0.83;

uniform bool VHS_LineNoise <
	ui_label = "Line Noise [VHSPro]";
	ui_tooltip = "Enables blinking line noise in the image.";
> = true;

uniform float lineNoiseAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 10.0;
	ui_label = "Line Noise Amount [VHSPro]";
	ui_tooltip = "Intensity of the line noise.";
> = 1.0;

uniform float lineNoiseSpeed <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 10.0;
	ui_label = "Line Noise Speed [VHSPro]";
	ui_tooltip = "Speed of the line noise blinking delay.";
> = 5.0;

uniform bool VHS_TapeNoise <
	ui_label = "Tape Noise [VHSPro]";
	ui_tooltip = "Adds scrolling noise like in old VHS Tapes.";
> = true;

uniform float tapeNoiseTH <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.5;
	ui_label = "Tape Noise Amount [VHSPro]";
	ui_tooltip = "Intensity of Tape Noise in the image.";
> = 0.63;

uniform float tapeNoiseAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.5;
	ui_label = "Tape Noise Alpha [VHSPro]";
	ui_tooltip = "Amount of Tape Noise in the image.";
> = 0.05;

uniform float tapeNoiseSpeed <
	ui_type = "slider";
	ui_min = -1.5;
	ui_max = 1.5;
	ui_label = "Tape Noise Speed [VHSPro]";
	ui_tooltip = "Scrolling speed of the Tape Noise.";
> = 1.0;

uniform bool VHS_ScanLines <
	ui_label = "Scanlines [VHSPro]";
	ui_tooltip = "Enables TV/CRT Scanlines";
> = false;

uniform float scanLineWidth <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 20.0;
	ui_label = "Scanlines Width [VHSPro]";
	ui_tooltip = "Width of the Scanlines";
> = 10.0;

uniform bool VHS_LinesFloat	<
	ui_label = "Lines Float [VHSPro]";
	ui_tooltip = "Makes the lines of the screen floats up or down. Works best with low Screen Lines resolutions.";
> = false;

uniform float linesFloatSpeed <
	ui_type = "slider";
	ui_min = -3.0;
	ui_max = 3.0;
	ui_label = "Lines Float Speed [VHSPro]";
	ui_tooltip = "Speed (and direction) of the floating lines.";
> = 1.0;

uniform bool VHS_Stretch <
	ui_label = "Stretch Noise [VHSPro]";
	ui_tooltip = "Enables a stretching noise that scrolls up and down on the Image, simulating magnetic interference of VHS tapes.";
> = true;

uniform bool VHS_Jitter_H <
	ui_type = "bool";
	ui_label = "Interlacing [VHSPro]";
	ui_tooltip = "Enables Interlacing.";
> = true;

uniform float jitterHAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 5.0;
	ui_label = "Interlacing Amount [VHSPro]";
	ui_tooltip = "Strength of the Interlacing.";
> = 0.5;

uniform bool VHS_Jitter_V <
	ui_label = "Jitter [VHSPro]";
	ui_tooltip = "Adds vertical jittering noise.";
> = false;

uniform float jitterVAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 15.0;
	ui_label = "Jitter Amount [VHSPro]";
	ui_tooltip = "Amount of the vertical jittering noise.";
> = 1.0; 

uniform float jitterVSpeed <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 5.0;
	ui_label = "Jitter Speed [VHSPro]";
	ui_tooltip = "Speed of the vertical jittering noise.";
> = 1.0;

uniform bool VHS_Twitch_H <
	ui_label = "Horizontal Twitch [VHSPro]";
	ui_tooltip = "Makes the image twitches horizontally in certain timed intervals.";
> = false;

uniform float twitchHFreq <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 5.0;
	ui_label = "Horizontal Twitch Frequency [VHSPro]";
	ui_tooltip = "Frequency of time in which the image twitches horizontally.";
> = 1.0;

uniform bool VHS_Twitch_V <
	ui_label = "Vertical Twitch [VHSPro]";
	ui_tooltip = "Makes the image twitches vertically in certain timed intervals.";
> = false;

uniform float twitchVFreq <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 5.0;
	ui_label = "Vertical Twitch Frequency [VHSPro]";
	ui_tooltip = "Frequency of time in which the image twitches vertically.";
> = 1.0;

uniform bool VHS_SignalTweak <
	ui_label = "Signal Tweak [VHSPro]";
	ui_tooltip = "Tweak the values of the YIQ signal.";
> = false;

uniform float signalAdjustY <
	ui_type = "slider";
	ui_min = -0.25;
	ui_max = 0.25;
	ui_label = "Signal Shift Y [VHSPro]";
	ui_tooltip = "Shifts/Tweaks the Luma part of the signal.";
> = 0.0;
uniform float signalAdjustI <
	ui_type = "slider";
	ui_min = -0.25;
	ui_max = 0.25;
	ui_label = "Signal Shift I [VHSPro]";
	ui_tooltip = "Shifts/Tweaks the Chroma part of the signal.";
> = 0.0;

uniform float signalAdjustQ <
	ui_type = "slider";
	ui_min = -0.25;
	ui_max = 0.25;
	ui_label = "Signal Shift Q [VHSPro]";
	ui_tooltip = "Shifts/Tweaks the Chroma part of the signal.";
> = 0.0;

uniform float signalShiftY <
	ui_type = "slider";
	ui_min = -2.0;
	ui_max = 2.0;
	ui_label = "Signal Adjust Y [VHSPro]";
	ui_tooltip = "Adjusts the Luma part of the signal.";
> = 1.0;

uniform float signalShiftI <
	ui_type = "slider";
	ui_min = -2.0;
	ui_max = 2.0;
	ui_label = "Signal Adjust I [VHSPro]";
	ui_tooltip = "Adjusts the Chroma part of the signal.";
> = 1.0;

uniform float signalShiftQ <
	ui_type = "slider";
	ui_min = -2.0;
	ui_max = 2.0;
	ui_label = "Signal Adjust Q [VHSPro]";
	ui_tooltip = "Adjusts the Chroma part of the signal.";
> = 1.0;

uniform float gammaCorection <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 2.0;
	ui_label = "Signal Gamma Correction [VHSPro]";
	ui_tooltip = "Gamma corrects the image.";
> = 1.0;

uniform bool VHS_Feedback <
	ui_label = "Phosphor Trails [VHSPro]";
	ui_tooltip = "Enables phosphor-trails from old CRT monitors.";
> = false;

uniform float feedbackAmount <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 3.0;
	ui_label = "Input Amount [VHSPro]";
	ui_tooltip = "Amount of Phosphor Trails.";
> = 2.0;

uniform float feedbackFade <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.0;
	ui_label = "Phosphor Fade [VHSPro]";
	ui_tooltip = "Fade-time of the phosphor-trails.";
> = 0.82;

uniform float feedbackThresh <
	ui_type = "slider";
	ui_min = 0.0;
	ui_max = 1.0;
	ui_label = "Input Cutoff [VHSPro]";
	ui_tooltip = "Cutoff of the trail.";
> = 0.1;

uniform float3 feedbackColor <
	ui_type = "color";
	ui_label = "Phosphor Trail Color [VHSPro]";
	ui_tooltip = "Color of the trail.";
> = float3(1.0,0.5,0.0);

uniform bool feedbackDebug <
	ui_type = "bool";
	ui_label = "Debug Trail [VHS Pro]";
	ui_tooltip = "Enables the visualization of the phosphor-trails only.";
> = false;

uniform bool VHS_Filter <
	ui_type = "bool";
	ui_label = "Linear Filtering [VHSPro]";
	ui_tooltip = "Filters the image linearly, increasing quality.\n\nIn order to use this setting as shown, please install the \"UIBind\" addon created by cot6.\n\nAlternatively, you may enable linear filtering in the Preprocessor Definitions below by adjusting VHSLINEARFILTER to 1";
	ui_bind = "VHSLINEARFILTER";
> = false;

#ifndef VHSLINEARFILTER
	#define VHSLINEARFILTER 0
#endif

//textures and samplers
#if VHSLINEARFILTER
	#define VHSFILTERMODE LINEAR
#else
	#define VHSFILTERMODE POINT
#endif

static const float fisheyeSize = 1.2; 					//Size (DO NOT CHANGE)
static const float filmGrainPower = 1.0;				//Film Grain Power (DO NOT CHANGE)
static const float feedbackAmp = 1.0; 					//Feedback Amp (DO NOT CHANGE)

#define _ScreenParams float2(BUFFER_WIDTH, BUFFER_HEIGHT)
#define vhsPixelSize  	float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT)
uniform float  Timer < source = "timer"; >;

static const float Pi2 = 6.283185307;

#define TEXHEIGHT   noiseLinesRes
#define TEXWIDTH 	int(((float)TEXHEIGHT*(float)BUFFER_WIDTH/(float)BUFFER_HEIGHT))

texture2D VHS_InputTexA    { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA8; };
texture2D VHS_InputTexB    { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA8; };
texture2D VHS_FeedbackTexA { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA8; };
texture2D VHS_FeedbackTexB { Width = BUFFER_WIDTH; Height = BUFFER_HEIGHT; Format = RGBA8; };
texture2D _TapeTex { Width = TEXWIDTH; Height = TEXHEIGHT; Format = RGBA8; };

sampler2D SamplerColorVHS
{
	Texture = ReShade::BackBufferTex;
	MinFilter = VHSFILTERMODE;
	MagFilter = VHSFILTERMODE;
	MipFilter = VHSFILTERMODE;
	AddressU = Clamp;
	AddressV = Clamp;
};

sampler2D SamplerTape
{
	Texture = _TapeTex;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = POINT;
	AddressU = Clamp;
	AddressV = Clamp;
};

sampler2D VHS_InputA    { Texture = VHS_InputTexA; MinFilter = VHSFILTERMODE;  MagFilter = VHSFILTERMODE;  MipFilter = VHSFILTERMODE; };
sampler2D VHS_InputB    { Texture = VHS_InputTexB; MinFilter = VHSFILTERMODE;  MagFilter = VHSFILTERMODE;  MipFilter = VHSFILTERMODE; };
sampler2D VHS_FeedbackA { Texture = VHS_FeedbackTexA; MinFilter = VHSFILTERMODE;  MagFilter = VHSFILTERMODE;  MipFilter = VHSFILTERMODE; };
sampler2D _FeedbackTex { Texture = VHS_FeedbackTexB; MinFilter = VHSFILTERMODE;  MagFilter = VHSFILTERMODE;  MipFilter = VHSFILTERMODE; };

//functions

#define PI 3.1415926535897932
static const float3 MOD3 = float3(443.8975, 397.2973, 491.1871);

float fmod(float a, float b) {
	const float c = frac(abs(a / b)) * abs(b);
	if (a < 0)
		return -c;
	else
		return c;
}

float3 bms(float3 c1, float3 c2){ return 1.0- (1.0-c1)*(1.0-c2); }

//turns sth on and off //a - how often 
float onOff(float a, float b, float c, float t)
{
	return step(c, sin(t + a*cos(t*b)));
}

float hash( float n ){ return frac(sin(n)*43758.5453123); }

float hash12(float2 p){
	float3 p3  = frac(float3(p.xyx) * MOD3);
	p3 += dot(p3, p3.yzx + 19.19);
	return frac(p3.x * p3.z * p3.y);
}

float2 hash22(float2 p) {
	float3 p3 = frac(float3(p.xyx) * MOD3);
	p3 += dot(p3.zxy, p3.yzx+19.19);
	return frac(float2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));
}

//random hash
float4 hash42(float2 p)
{
	float4 p4 = frac(float4(p.xyxy) * float4(443.8975,397.2973, 491.1871, 470.7827));
	p4 += dot(p4.wzxy, p4 + 19.19);
	return frac(float4(p4.x * p4.y, p4.x*p4.z, p4.y*p4.w, p4.x*p4.w));
}

float niq( in float3 x ){
	const float3 p = floor(x);
	float3 f = frac(x);
	f = f*f*(3.0-2.0*f);
	const float n = p.x + p.y*57.0 + 113.0*p.z;
	return lerp(lerp(		lerp( hash(n+  0.0), hash(n+  1.0),f.x),
				            lerp( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
				            lerp( lerp( hash(n+113.0), hash(n+114.0),f.x),
				            lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}

float filmGrain(float2 uv, float t, float c )
{
//cheap noise - is ok atm
	return pow(hash12( uv + 0.07*frac( t ) ), 3);
}

float2 n4rand_bw( float2 p, float t, float c )
{
	t = frac( t );//that's why its sort of twitching 
	float2 nrnd0 = hash22( p + 0.07*t );
	c = 1.0 / (10.0*c); //iMouse.y  / iResolution.y
	return pow(nrnd0, c); //TODO try to invert 1-...
}

float scanLines(float2 p, float t)
{

	//cheap (maybe make an option later)
	// float scanLineWidth = 0.26;
	// float scans = 0.5*(cos((p.y*float(screenLinesNum)+t+.5)*2.0*PI) + 1.0);
	// if(scans>scanLineWidth) scans = 1.; else scans = 0.;

		float t_sl = 0.0;
		//if lines aren't floating -> scanlines also shudn't 
		if (VHS_LinesFloat) {
			t_sl = t*linesFloatSpeed;
		}

		//expensive but better
		float scans = 0.5*(cos( (p.y*float(screenLinesNum)+0.5+t_sl)*2.0*PI) + 1.0);
		scans = pow(scans, scanLineWidth); 
		return 1.0 - scans; 
}

float gcos(float2 uv, float s, float p)
{
	return (cos( uv.y * PI * 2.0 * s + p)+1.0)*0.5;
}

//mw - maximum width
//wcs = widthChangeSpeed
//lfs = line float speed = .5
//lf phase = line float phase = .0
float2 stretch(float2 uv, float t, float mw, float wcs, float lfs, float lfp){
   
	const float SLN = float(screenLinesNum) + 0.5; //TODO use only SLN
	//width change
	const float tt = t*wcs; //widthChangeSpeed
	const float t2 = tt-fmod(tt, 0.5);

	//float dw  = hash42( vec2(0.01, t2) ).x ; //on t and not on y
	float w = gcos(uv, 2.0*(1.0-frac(t2)), PI-t2) * clamp( gcos(uv, frac(t2), t2) , 0.5, 1.0);
	//w = clamp(w,0.,1.);
	w = floor(w*mw)/mw;
	w *= mw;
	//get descreete line number
	float ln = (1.0-frac(t*lfs + lfp)) *(float(screenLinesNum) + 0.5); 
	ln = ln - frac(ln); 
	// float ln = (1.-fmod(t*lfs + lfp, 1.))*SLN; 
	// ln = ln - fmod(ln, 1.); //descreete line number

	//ln = 10.;
	//w = 4.;

	//////stretching part///////

	const float oy = 1.0/SLN; //TODO global
	const float sh2 =  1.0 - fmod(ln, w)/w; // shift 0..1

	// #if VHS_LINESFLOAT_ON
	// 	float sh = fmod(t, 1.);
	//  	uv.y = floor( uv.y *SLN  +sh )/SLN - sh/SLN;
	// #else 
	//  	uv.y = floor( uv.y *SLN  )/SLN;
	//  #endif

	// uv.y = floor( uv.y  *SLN  )/SLN ;

	const float slb = SLN / w; //screen lines big        

	//TODO finish
	// #if VHS_LINESFLOAT_ON

		//  if(uv.y<oy*ln && uv.y>oy*(ln-w)) ////if(uv.y>oy*ln && uv.y<oy*(ln+w)) 
		//     uv.y = floor( uv.y*slb +sh2 +sh )/slb - (sh2-1.)/slb - sh/slb;

	//   #else

		if(uv.y<oy*ln && uv.y>oy*(ln-w)) ////if(uv.y>oy*ln && uv.y<oy*(ln+w)) 
			uv.y = floor( uv.y*slb +sh2 )/slb - (sh2-1.0)/slb ;

	// #endif

	return uv;
}

float rnd_rd(float2 co)
{
	return frac(sin(fmod(dot(co.xy ,float2(12.9898,78.233)),3.14)) * 43758.5453);
}

//DANG WINDOWS
float3 rgb2yiq(float3 c)
{   
	return float3(
	0.2989*c.x + 0.5959*c.y + 0.2115*c.z,
	0.5870*c.x - 0.2744*c.y - 0.5229*c.z,
	0.1140*c.x - 0.3216*c.y + 0.3114*c.z);
};

float3 yiq2rgb(float3 c)
{				
	return float3(
	1.0*c.x +1.0*c.y +1.0*c.z,
	0.956*c.x - 0.2720*c.y - 1.1060*c.z,
	0.6210*c.x - 0.6474*c.y + 1.7046*c.z);
};

//rgb distortion
float3 rgbDistortion(float2 uv,  float magnitude, float t)
{
	magnitude *= 0.0001; // float magnitude = 0.0009;
	float3 offsetX = float3( uv.x, uv.x, uv.x );
	offsetX.r += rnd_rd(float2(t*0.03,uv.y*0.42)) * 0.001 + sin(rnd_rd(float2(t*0.2, uv.y)))*magnitude;
	offsetX.g += rnd_rd(float2(t*0.004,uv.y*0.002)) * 0.004 + sin(t*9.0)*magnitude;
	// offsetX.b = uv.y + rnd_rd(float2(cos(t*0.01),sin(uv.y)))*magnitude;
	// offsetX.b = uv.y + rand_rd(float2(cos(t*0.01),sin(uv.y)))*magnitude;

	float3 col = float3(0.0, 0.0, 0.0);
	//it cud be optimized / but hm
	col.x = rgb2yiq( tex2D( SamplerColorVHS, float2(offsetX.r, uv.y) ).rgb ).x;
	col.y = rgb2yiq( tex2D( SamplerColorVHS, float2(offsetX.g, uv.y) ).rgb ).y;
	col.z = rgb2yiq( tex2D( SamplerColorVHS, float2(offsetX.b, uv.y) ).rgb ).z;

	return yiq2rgb(col);
}

float rndln(float2 p, float t)
{
	float sample_ln = rnd_rd(float2(1.0,2.0*cos(t))*t*8.0 + p*1.0).x;
	sample_ln *= sample_ln;//*sample;
	return sample_ln;
}

float lineNoise(float2 p, float t)
{
	float n = rndln(p* float2(0.5,1.0) + float2(1.0,3.0), t)*20.0;

	float freq = abs(sin(t));  //1.
	float c = n*smoothstep(fmod(p.y*4.0 + t/2.0+sin(t + sin(t*0.63)),freq), 0.0,0.95);

	return c;
}


// 3d noise function (iq's)
float n( in float3 x )
{
	const float3 p = floor(x);
	float3 f = frac(x);
	f = f*f*(3.0-2.0*f);
	const float n = p.x + p.y*57.0 + 113.0*p.z;
	return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),f.x),
	lerp( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
	lerp( lerp( hash(n+113.0), hash(n+114.0),f.x),
	lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}

float tapeNoiseLines(float2 p, float t){

//so atm line noise is depending on hash for int values
//i gotta rewrite to for hash for 0..1 values 
//then i can use normilized p for generating lines

	const float y = p.y*_ScreenParams.y;
	const float s = t*2.0;
	return  	(niq( float3(y*0.01 +s, 			1.0, 1.0) ) + 0.0)
				*(niq( float3(y*0.011+1000.0+s,	1.0, 1.0) ) + 0.0) 
				*(niq( float3(y*0.51+421.0+s, 	1.0, 1.0) ) + 0.0)   
				;
}


float tapeNoise(float nl, float2 p, float t){

	//TODO custom adjustable density (Probability distribution)
	// but will be expensive (atm its ok)

	//atm its just contrast noise 
				   
	//this generates noise mask
	float nm = 	hash12( frac(p+t*float2(0.234,0.637)) ) 
				// *hash12( frac(p+t*float2(0.123,0.867)) ) 
				// *hash12( frac(p+t*float2(0.441,0.23)) );
									;
		nm = pow(nm, 4) +0.3; //cheap and ok
		//nm += 0.3 ; //just bit brighter or just more to threshold?

		nl*= nm; // put mask
		// nl += 0.3; //Value add .3//

	if(nl<tapeNoiseTH) nl = 0.0; else nl =1.0;  //threshold
	return nl;
}

float2 twitchVertical(float freq, float2 uv, float t){

	float vShift = 0.4*onOff(freq,3.0,0.9, t);
	vShift*=(sin(t)*sin(t*20.0) + (0.5 + 0.1*sin(t*200.0)*cos(t)));
	uv.y = fmod(uv.y + vShift, 1.0); 
	return uv;
}

float2 twitchHorizonal(float freq, float2 uv, float t){

	uv.x += sin(uv.y*10.0 + t)/50.0*onOff(freq,4.0,0.3, t)*(1.0+cos(t*80.0))*(1.0/(1.0+20.0*(uv.y-fmod(t/4.0,1.0))*(uv.y-fmod(t/4.0, 1.0))));
	return uv;
}


//all that shit is for postVHS"Pro"_First - end

//all that shit is for postVHS"Pro"_Second

//size 1.2, bend 2.
float2 fishEye(float2 uv, float size, float bend)
{
	if (!VHS_FishEye_Hyperspace){
		uv -= float2(0.5,0.5);
		uv *= size*(1.0/size+bend*uv.x*uv.x*uv.y*uv.y);
		uv += float2(0.5,0.5);
	}

			if (VHS_FishEye_Hyperspace){

				//http://paulbourke.net/miscellaneous/lenscorrection/

				const float prop = _ScreenParams.x / _ScreenParams.y;
				const float2 m = float2(0.5, 0.5 / prop);
				const float2 d = (uv*_ScreenParams.xy) /_ScreenParams.x - m;
				const float r = sqrt(dot(d, d));
				float bind;

				float power = ( 2.0 * 3.141592 / (2.0 * sqrt(dot(m, m))) ) *
				(bend/50.0 - 0.5); //amount of effect

				if (power > 0.0) bind = sqrt(dot(m, m));//stick to corners
				else {if (prop < 1.0) bind = m.x; else bind = m.x;}//stick to borders

				if (power > 0.0) //fisheye
					uv = m + normalize(d) * tan(r * power) * bind / tan( bind * power);
				else if (power < 0.0) //antifisheye
					uv = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
				else uv = (uv*_ScreenParams.xy) /_ScreenParams.x; 

			uv.y *=  prop;
		}

	//adjust size
	// uv -= float2(0.5,0.5);
	// uv *= size;
	// uv += float2(0.5,0.5);

	return uv;
}

//pulse vignette
float vignette(float2 uv, float t)
{
	const float vigAmt = 2.5+0.1*sin(t + 5.0*cos(t*5.0));
	float c = (1.0-vigAmt*(uv.y-0.5)*(uv.y-0.5))*(1.0-vigAmt*(uv.x-0.5)*(uv.x-0.5));
	c = pow(abs(c), vignetteAmount); //expensive!
	return c;
}

float3 t2d(float2 p)
{
	return rgb2yiq( tex2Dlod (SamplerColorVHS, float4( p, 0.0, 0.0 ) ).rgb );
}

float3 yiqDist(float2 uv, float m, float t)
{	
						m *= 0.0001; // float m = 0.0009;
						float3 offsetX = float3( uv.x, uv.x, uv.x );

						offsetX.r += rnd_rd(float2(t*0.03, uv.y*0.42)) * 0.001 + sin(rnd_rd(float2(t*0.2, uv.y)))*m;
						offsetX.g += rnd_rd(float2(t*0.004,uv.y*0.002)) * 0.004 + sin(t*9.0)*m;
						// offsetX.b = uv.y + rnd_rd(float2(cos(t*0.01),sin(uv.y)))*m;
						// offsetX.b = uv.y + rand_rd(float2(cos(t*0.01),sin(uv.y)))*m;
					    
					   float3 signal = float3(0.0, 0.0, 0.0);
					   //it cud be optimized / but hm
					   signal.x = rgb2yiq( tex2D( SamplerColorVHS, float2(offsetX.r, uv.y) ).rgb ).x;
					   signal.y = rgb2yiq( tex2D( SamplerColorVHS, float2(offsetX.g, uv.y) ).rgb ).y;
					   signal.z = rgb2yiq( tex2D( SamplerColorVHS, float2(offsetX.b, uv.y) ).rgb ).z;

					   // signal = yiq2rgb(col);
						return signal;    
}

#define fixCoord (p - float2( 0.5 * vhsPixelSize.x, .0)) 
#define fetch_offset(offset, one_x) t2d(fixCoord + float2( (offset) * (ONE_X), 0.0));

/////////////////////////PIXEL SHADERS//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////PIXEL SHADERS//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

float4 PS_VHS1(float4 pos : SV_Position, float2 txcoord : TEXCOORD) : SV_Target
{
	const float t = Timer.x * 0.001;//_Time.y;
	float2 p = txcoord.xy;
	
	float SLN = float(screenLinesNum) + 0.5; //TODO use only SLN
	float SLN_Noise = float(noiseLinesNum) + 0.5; //TODO only SLN_Noise
	float ONE_X = 0.0;
	float ONE_Y = 0.0;

	//basically if its 0 -> set it to fullscreen
	//TODO calc it before shader / already float done
	SLN = float(screenLinesNum) + 0.5; //TODO use only SLN
	SLN_Noise = float(noiseLinesNum) + 0.5; //TODO only SLN_Noise
	if(SLN==0.0) SLN = _ScreenParams.y;

	if(SLN_Noise==0 || SLN_Noise>SLN) SLN_Noise = SLN;

	ONE_X = 1.0/_ScreenParams.x; //assigning works only here 
	ONE_Y = 1.0/_ScreenParams.y; 


	if (VHS_Twitch_V){
		p = twitchVertical(0.5*twitchVFreq, p, t); 
	}

	if (VHS_Twitch_H){
		p = twitchHorizonal(0.1*twitchHFreq, p, t);
	}	

	//make discrete lines /w or wo float 
	if(VHS_LinesFloat){
		float sh = frac(-t*linesFloatSpeed); //shift  // float sh = fmod(t, 1.); //shift
		// if(p.x>0.5)
		p.y = -floor( -p.y * SLN + sh )/SLN + sh/SLN;  //v1.3
		// p.y = floor( p.y * SLN + sh )/SLN - sh/SLN; //v1.2
	} else {
		// if(p.x>0.5)
		p.y = -floor( -p.y * SLN )/SLN;  //v1.3
		// p.y = floor( p.y * SLN )/SLN; //v1.2
	}

	if (VHS_Stretch){
		p = stretch(p, t, 15.0, 1.0, 0.5, 0.0);
		p = stretch(p, t, 8.0, 1.2, 0.45, 0.5);
		p = stretch(p, t, 11.0, 0.5, -0.35, 0.25); //up   
	}

	if (VHS_Jitter_H){
		if( fmod( p.y * SLN, 2.0)<1.0) 
		p.x += ONE_X*sin(t*13000.0)*jitterHAmount;
	}
	
	//just init
	float3 col = float3(0.0,0.0,0.0);
	float3 signal = float3(0.0,0.0,0.0);// rgb2yiq(col);

	//gotta initiate all these things here coz of tape noise distortion

	//[NOISE uv init]
	//if SLN_Noise different from SLN->recalc linefloat
	float2 pn = p;
	if(SLN!=SLN_Noise){
		if(VHS_LineNoise){
			const float sh = frac(t); //shift  // float sh = fmod(t, 1.); //shift
			pn.y = floor( pn.y * SLN_Noise + sh )/SLN_Noise - sh/SLN_Noise;
		} else  {
			pn.y = floor( pn.y * SLN_Noise )/SLN_Noise;
		}
	}

	//SLN_X is quantization of X. goest from _ScreenParams.x to SLN_X
	const float ScreenLinesNumX = SLN_Noise * _ScreenParams.x / _ScreenParams.y;
	const float SLN_X = noiseQuantizeX*(_ScreenParams.x - ScreenLinesNumX) + ScreenLinesNumX;
	pn.x = floor( pn.x * SLN_X )/SLN_X;

	const float2 pn_ = pn*_ScreenParams.xy;

	//TODO probably it shud be 1.0/SLN_Noise
	const float ONEXN = 1.0/SLN_X;
	//[//noise uv init]

	float distShift = 0; // for 2nd part of tape noise

	if (VHS_TapeNoise) {

		//uv distortion part of tapenoise
		distShift = 0; // for 2nd part of tape noise 
		for (int ii = 0; ii < 20 % 1023; ii++){

			//this is t.n. line value at pn.y and down each pixel
			//TODO i guess ONEXN shud be 1.0/sln noise
			const float tnl = tex2Dlod(SamplerTape, float4(0.0,pn.y-ONEXN*ii, 0.0, 0.0)).y;
			// float tnl = tex2D(SamplerTape, float2(0.0,pn.y-ONEXN*ii)).y;
			// float tnl = tapeNoiseLines(float2(0.0,pn.y-ONEXN*i), t*tapeNoiseSpeed)*tapeNoiseAmount;

			// float fadediff = hash12(float2(pn.x-ONEXN*i,pn.y)); 
			if(tnl>0.55) {
					//TODO get integer part other way
					const float sh = sin( 1.0*PI*(float(ii)/float(20))) ; //0..1								
					p.x -= float(int(sh)*4.0*ONEXN); //displacement
					distShift += sh ; //for 2nd part
					// p.x +=  ONEXN * float(int(((tnl-thth)/thth)*4.0));
					// col.x = sh;
				}
			}
		}

	//uv transforms over

	//picture proccess start
	if (VHS_Jitter_V){
		signal = yiqDist(p, jitterVAmount, t*jitterVSpeed);
	} else {
		col = tex2D(SamplerColorVHS, p).rgb;
		// col = float3(p.xy, 0.0);//debug
		signal = rgb2yiq(col);
	}


	if (VHS_LineNoise || VHS_FilmGrain){
		signal.x += tex2D(SamplerTape, pn).z;
	}

	//iq noise from yiq
	if (VHS_YIQNoise){
		if (signalNoiseType == 0) {
						//TODO make cheaper noise 
						//type 1 (best) w Y mask
						const float2 noise = n4rand_bw( pn_,t,1.0-signalNoisePower ) ; 
						signal.y += (noise.x*2.0-1.0)*signalNoiseAmount*signal.x;
						signal.z += (noise.y*2.0-1.0)*signalNoiseAmount*signal.x;
				} else if (signalNoiseType == 1){
						//type 2
						const float2 noise = n4rand_bw( pn_,t, 1.0-signalNoisePower ) ; 
						signal.y += (noise.x*2.0-1.0)*signalNoiseAmount;
						signal.z += (noise.y*2.0-1.0)*signalNoiseAmount;
				} else {
						//type 3
						const float2 noise = n4rand_bw( pn_,t, 1.0-signalNoisePower )*signalNoiseAmount ; 
						signal.y *= noise.x;
						signal.z *= noise.y;
						signal.x += (noise.x*2.0-1.0)*0.05;
				}
			}

	//2nd part with noise, tail and yiq col shift
	if (VHS_TapeNoise){

	//here is normilized p (0..1)
	float tn = tex2D(SamplerTape, pn).x;
	signal.x = bms(signal.x, tn*tapeNoiseAmount ).x;  
	// float tn = tapeNoise(pn, t*tapeNoiseSpeed)*tapeNoiseAmount;

	//tape noise tail
	const int tailLength=10; //TODO adjustable

	for(int j = 0; j < tailLength % 1023; j++){

		const float jj = float(j);
		const float2 d = float2(pn.x-ONEXN*jj,pn.y);
		tn = tex2Dlod(SamplerTape, float4(d,0.0,0.0) ).x;
		// tn = tex2D(SamplerTape, d).x;
		// tn = tapeNoise(float2(pn.x-ONEXN*i,pn.y), t*tapeNoiseSpeed)*tapeNoiseAmount;

		float fadediff = 0.0;

		//for tails length difference
		if(__RENDERER__ == 0x0A100 || __RENDERER__ == 0x0B000) {
			fadediff = tex2Dlod(SamplerTape, float4(d,0.0,0.0)).a; //hash12(d);
		}

		if(__RENDERER__ == 0x09300 || __RENDERER__ >= 0x10000) {
			fadediff = tex2D(SamplerTape, d).a; //hash12(d);
		}

		if( tn > 0.8 ){
			float nsx =  0.0; //new signal x
			const float newlength = float(tailLength)*(1.0-fadediff); //tail lenght diff
			if( jj <= newlength ) nsx = 1.0-( jj/ newlength ); //tail
			signal.x = bms(signal.x, nsx*tapeNoiseAmount).x;
		}
	}

	//tape noise color shift
	if(distShift>0.4){
		// float tnl = tapeNoiseLines(float2(0.0,pn.y), t*tapeNoiseSpeed)*tapeNoiseAmount;
		const float tnl = tex2D(SamplerTape, pn).y;//tapeNoiseLines(float2(0.0,pn.y), t*tapeNoiseSpeed)*tapeNoiseAmount;
		signal.y *= 1.0/distShift;//tnl*0.1;//*distShift;//*signal.x;
		signal.z *= 1.0/distShift;//*distShift;//*signal.x;

		}
}

	//back to rgb color space
	//signal has negative values
	col = yiq2rgb(signal);

	//TODO put it into 2nd pass
	if (VHS_ScanLines){
		col *= scanLines(txcoord.xy, t);
	}

	//fisheye cutoff / outside fisheye
	//helps to remove noise outside the fisheye
	if (VHS_FishEye){

		p = txcoord.xy;

		float far;
		const float2 hco = float2(ONE_X*cutoffX, ONE_Y*cutoffY); //hard cuttoff x
		const float2 sco = float2(ONE_X*cutoffFadeX, ONE_Y*cutoffFadeY); //soft cuttoff x

		//hard cutoff
		if( p.x<=(0.0+hco.x) || p.x>=(1.0-hco.x) || p.y<=(0.0+hco.y) || p.y>=(1.0-hco.y) ){
			col = float3(0.0,0.0,0.0);
		} else { 
			//fades

			if( //X
				(p.x>(0.0+hco.x) && p.x<(0.0+(sco.x+hco.x) )) || (p.x>(1.0-(sco.x+hco.x)) && p.x<(1.0-hco.x)) 
			){	
				if(p.x<0.5)	far = (0.0-hco.x+p.x)/(sco.x);
				else
				far = (1.0-hco.x-p.x)/(sco.x);

				col *= float(far).xxx;
			}; 

				if( //Y
					(p.y>(0.0+hco.y) 			 && p.y<(0.0+(sco.y+hco.y) )) || (p.y>(1.0-(sco.y+hco.y)) && p.y<(1.0-hco.y)) 
				){
					if(p.y<0.5)	far = (0.0-hco.y+p.y)/(sco.y);
					else
					far = (1.0-hco.y-p.y)/(sco.y);

					col *= float(far).xxx;
				}
			}
		}
		
	// col = tex2D(SamplerTape, txcoord.xy).x;
	return float4(col, 1.0);
}

float4 PS_VHS2(float4 pos : SV_Position, float2 txcoord : TEXCOORD) : SV_Target
{
	const float t = Timer.x * 0.001;//_Time.y;
	float2 p = txcoord.xy;
	float SLN = float(screenLinesNum) + 0.5; //TODO use only SLN

	//basically if its 0 -> set it to fullscreen
	if(SLN==0.0) SLN = _ScreenParams.y;

	 //TODO maybe make it based on num of lines? and height ? 
	//TODO make switch between real pixels and pixelated pixels!
    // ONE_X = 1.0 / float(screenLinesNum) + 0.5;

	float ONE_X = 1.0 / _ScreenParams.x;  // 1px
    ONE_X *= bleedAmount; // longer tails, more bleeding, default 1.

	//distortions
	if (VHS_FishEye){
		p = fishEye(p, fisheyeSize, fisheyeBend); // p = fishEye(p, 1.2, 2.0);
		// return float4( frac(p*5.0)/2.5, 1.0, 1.0); //debug
	}

	int bleedLength = 21;

	if((VHS_BleedMode == 0 || VHS_BleedMode == 1 || VHS_BleedMode == 3))
	{
		bleedLength = 25;
	}

	if((VHS_BleedMode == 2 || VHS_BleedMode == 4))
	{
		bleedLength = 32;
	}
	 
	float luma_filter[33] = {
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000
	};
	
	float chroma_filter[33] = {
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000,
	0.000000000
	};
	
	if (VHS_BleedMode == 0 || VHS_BleedMode == 3){ //Three Phase and Three Phase (RetroArch)
		luma_filter[0] =	-0.000012020;
		luma_filter[1] =	-0.000022146;
		luma_filter[2] =	-0.000013155;
		luma_filter[3] =	-0.000012020;
		luma_filter[4] =	-0.000049979;
		luma_filter[5] =	-0.000113940;
		luma_filter[6] =	-0.000122150;
		luma_filter[7] =	-0.000005612;
		luma_filter[8] =	0.000170516;
		luma_filter[9] =	0.000237199;
		luma_filter[10] =	0.000169640;
		luma_filter[11] =	0.000285688;
		luma_filter[12] =	0.000984574;
		luma_filter[13] =	0.002018683;
		luma_filter[14] =	0.002002275;
		luma_filter[15] =	-0.000909882;
		luma_filter[16] =	-0.007049081;
		luma_filter[17] =	-0.013222860;
		luma_filter[18] =	-0.012606931;
		luma_filter[19] =	0.002460860;
		luma_filter[20] =	0.035868225;
		luma_filter[21] =	0.084016453;
		luma_filter[22] =	0.135563500;
		luma_filter[23] =	0.175261268;
		luma_filter[24] =	0.190176552;

		chroma_filter[0] =	-0.000118847;
		chroma_filter[1] =	-0.000271306;
		chroma_filter[2] =	-0.000502642;
		chroma_filter[3] =	-0.000930833;
		chroma_filter[4] =	-0.001451013;
		chroma_filter[5] =	-0.002064744;
		chroma_filter[6] =	-0.002700432;
		chroma_filter[7] =	-0.003241276;
		chroma_filter[8] =	-0.003524948;
		chroma_filter[9] =	-0.003350284;
		chroma_filter[10] =	-0.002491729;
		chroma_filter[11] =	-0.000721149;
		chroma_filter[12] =	0.002164659;
		chroma_filter[13] =	0.006313635;
		chroma_filter[14] =	0.011789103;
		chroma_filter[15] =	0.018545660;
		chroma_filter[16] =	0.026414396;
		chroma_filter[17] =	0.035100710;
		chroma_filter[18] =	0.044196567;
		chroma_filter[19] =	0.053207202;
		chroma_filter[20] =	0.061590275;
		chroma_filter[21] =	0.068803602;
		chroma_filter[22] =	0.074356193;
		chroma_filter[23] =	0.077856564;
		chroma_filter[24] =	0.079052396;
	}
	
	else if (VHS_BleedMode == 1) { //Old Three-Phase
		luma_filter[0] = -0.000071070;
		luma_filter[1] = -0.000032816;
		luma_filter[2] = 0.000128784;
		luma_filter[3] = 0.000134711;
		luma_filter[4] = -0.000226705;
		luma_filter[5] = -0.000777988;
		luma_filter[6] = -0.000997809;
		luma_filter[7] = -0.000522802;
		luma_filter[8] = 0.000344691;
		luma_filter[9] = 0.000768930;
		luma_filter[10] = 0.000275591;
		luma_filter[11] = -0.000373434;
		luma_filter[12] = 0.000522796;
		luma_filter[13] = 0.003813817;
		luma_filter[14] = 0.007502825;
		luma_filter[15] = 0.006786001;
		luma_filter[16] = -0.002636726;
		luma_filter[17] = -0.019461182;
		luma_filter[18] = -0.033792479;
		luma_filter[19] = -0.029921972;
		luma_filter[20] = 0.005032552;
		luma_filter[21] = 0.071226466;
		luma_filter[22] = 0.151755921;
		luma_filter[23] = 0.218166470;
		luma_filter[24] = 0.243902439;
			
		chroma_filter[0] = 0.001845562;
		chroma_filter[1] = 0.002381606;
		chroma_filter[2] = 0.003040177;
		chroma_filter[3] = 0.003838976;
		chroma_filter[4] = 0.004795341;
		chroma_filter[5] = 0.005925312;
		chroma_filter[6] = 0.007242534;
		chroma_filter[7] = 0.008757043;
		chroma_filter[8] = 0.010473987;
		chroma_filter[9] = 0.012392365;
		chroma_filter[10] = 0.014503872;
		chroma_filter[11] = 0.016791957;
		chroma_filter[12] = 0.019231195;
		chroma_filter[13] = 0.021787070;
		chroma_filter[14] = 0.024416251;
		chroma_filter[15] = 0.027067414;
		chroma_filter[16] = 0.029682613;
		chroma_filter[17] = 0.032199202;
		chroma_filter[18] = 0.034552198;
		chroma_filter[19] = 0.036677005;
		chroma_filter[20] = 0.038512317;
		chroma_filter[21] = 0.040003044;
		chroma_filter[22] = 0.041103048;
		chroma_filter[23] = 0.041777517;
		chroma_filter[24] = 0.042004791;
	}
	
	else if (VHS_BleedMode == 2) { //Two-Phase
		luma_filter[0] =	-0.000205844;
		luma_filter[1] =	-0.000149453;
		luma_filter[2] =	-0.000051693;
		luma_filter[3] =	0.000000000;
		luma_filter[4] =	-0.000066171;
		luma_filter[5] =	-0.000245058;
		luma_filter[6] =	-0.000432928;
		luma_filter[7] =	-0.000472644;
		luma_filter[8] =	-0.000252236;
		luma_filter[9] =	0.000198929;
		luma_filter[10] =	0.000687058;
		luma_filter[11] =	0.000944112;
		luma_filter[12] =	0.000803467;
		luma_filter[13] =	0.000363199;
		luma_filter[14] =	0.000013422;
		luma_filter[15] =	0.000253402;
		luma_filter[16] =	0.001339461;
		luma_filter[17] =	0.002932972;
		luma_filter[18] =	0.003983485;
		luma_filter[19] =	0.003026683;
		luma_filter[20] =	-0.001102056;
		luma_filter[21] =	-0.008373026;
		luma_filter[22] =	-0.016897700;
		luma_filter[23] =	-0.022914480;
		luma_filter[24] =	-0.021642347;
		luma_filter[25] =	-0.008863273;
		luma_filter[26] =	0.017271957;
		luma_filter[27] =	0.054921920;
		luma_filter[28] =	0.098342579;
		luma_filter[29] =	0.139044281;
		luma_filter[30] =	0.168055832;
		luma_filter[31] =	0.178571429;
		
		chroma_filter[0] =	0.001384762;
		chroma_filter[1] =	0.001678312;
		chroma_filter[2] =	0.002021715;
		chroma_filter[3] =	0.002420562;
		chroma_filter[4] =	0.002880460;
		chroma_filter[5] =	0.003406879;
		chroma_filter[6] =	0.004004985;
		chroma_filter[7] =	0.004679445; 
		chroma_filter[8] =	0.005434218;
		chroma_filter[9] =	0.006272332; 
		chroma_filter[10] =	0.007195654; 
		chroma_filter[11] =	0.008204665; 
		chroma_filter[12] =	0.009298238; 
		chroma_filter[13] =	0.010473450;
		chroma_filter[14] =	0.011725413; 
		chroma_filter[15] =	0.013047155;
		chroma_filter[16] =	0.014429548;
		chroma_filter[17] =	0.015861306; 
		chroma_filter[18] =	0.017329037; 
		chroma_filter[19] =	0.018817382;
		chroma_filter[20] =	0.020309220; 
		chroma_filter[21] =	0.021785952;
		chroma_filter[22] =	0.023227857;
		chroma_filter[23] =	0.024614500; 
		chroma_filter[24] =	0.025925203;
		chroma_filter[25] =	0.027139546; 
		chroma_filter[26] =	0.028237893;
		chroma_filter[27] =	0.029201910; 
		chroma_filter[28] =	0.030015081; 
		chroma_filter[29] =	0.030663170;
		chroma_filter[30] =	0.031134640;
		chroma_filter[31] =	0.031420995; 
		chroma_filter[32] =	0.031517031;
	}
	
	else if (VHS_BleedMode == 4) { //Two-Phase (RetroArch)
		luma_filter[0] =  -0.000174844;
		luma_filter[1] =  -0.000205844;
		luma_filter[2] =  -0.000149453;
		luma_filter[3] =  -0.000051693;
		luma_filter[4] = 0.000000000;
		luma_filter[5] =  -0.000066171;
		luma_filter[6] =  -0.000245058;
		luma_filter[7] =  -0.000432928;
		luma_filter[8] =  -0.000472644;
		luma_filter[9] =  -0.000252236;
		luma_filter[10] =  0.000198929;
		luma_filter[11] =  0.000687058;
		luma_filter[12] =  0.000944112;
		luma_filter[13] =  0.000803467;
		luma_filter[14] =  0.000363199;
		luma_filter[15] =  0.000013422;
		luma_filter[16] =  0.000253402;
		luma_filter[17] =  0.001339461;
		luma_filter[18] =  0.002932972;
		luma_filter[19] =  0.003983485;
		luma_filter[20] =  0.003026683;
		luma_filter[21] =  -0.001102056;
		luma_filter[22] =  -0.008373026;
		luma_filter[23] =  -0.016897700;
		luma_filter[24] =  -0.022914480;
		luma_filter[25] =  -0.021642347;
		luma_filter[26] =  -0.008863273;
		luma_filter[27] =  0.017271957;
		luma_filter[28] =  0.054921920;
		luma_filter[29] =  0.098342579;
		luma_filter[30] =  0.139044281;
		luma_filter[31] =  0.168055832;
		luma_filter[32] =  0.178571429;

		chroma_filter[0] =   0.001384762;
		chroma_filter[1] =   0.001678312;
		chroma_filter[2] =   0.002021715;
		chroma_filter[3] =   0.002420562;
		chroma_filter[4] =   0.002880460;
		chroma_filter[5] =   0.003406879;
		chroma_filter[6] =   0.004004985;
		chroma_filter[7] =   0.004679445;
		chroma_filter[8] =   0.005434218;
		chroma_filter[9] =   0.006272332;
		chroma_filter[10] =   0.007195654;
		chroma_filter[11] =   0.008204665;
		chroma_filter[12] =   0.009298238;
		chroma_filter[13] =   0.010473450;
		chroma_filter[14] =   0.011725413;
		chroma_filter[15] =   0.013047155;
		chroma_filter[16] =   0.014429548;
		chroma_filter[17] =   0.015861306;
		chroma_filter[18] =   0.017329037;
		chroma_filter[19] =   0.018817382;
		chroma_filter[20] =   0.020309220;
		chroma_filter[21] =   0.021785952;
		chroma_filter[22] =   0.023227857;
		chroma_filter[23] =   0.024614500;
		chroma_filter[24] =   0.025925203;
		chroma_filter[25] =   0.027139546;
		chroma_filter[26] =   0.028237893;
		chroma_filter[27] =   0.029201910;
		chroma_filter[28] =   0.030015081;
		chroma_filter[29] =   0.030663170;
		chroma_filter[30] =   0.031134640;
		chroma_filter[31] =   0.031420995;
		chroma_filter[32] =   0.031517031;
	}


	//Bleeding
	float3 signal = float3(0.0,0.0,0.0);

	if (VHS_Bleed){

		float3 norm = 	float3(0.0,0.0,0.0);
		float3 adj = 	float3(0.0,0.0,0.0);
		
		const int taps = bleedLength-4;
		//int taps = bleedLength; //RetroArch

		for (int ii = 0; float(ii) < float(taps) % 1023; ii++){

			const float offset = float(ii);
			const float3 sums = 	fetch_offset(offset - float(taps), ONE_X) +
							fetch_offset(float(taps) - offset, ONE_X) ;

			adj = float3(luma_filter[ii+3], chroma_filter[ii], chroma_filter[ii]);
			//adj = float3(luma_filter[ii], chroma_filter[ii], chroma_filter[ii]); //RetroArch

			signal += sums * adj;
			norm += adj;

		}

		adj = float3(luma_filter[taps], chroma_filter[taps], chroma_filter[taps]);

		signal += t2d(fixCoord) * adj;
		norm += adj;
		signal = signal / norm;
	} else {
	    //no bleeding
	    signal = t2d(p);
	}

	//[Signal Tweak]
	if (VHS_SignalTweak){

		//adjust
		signal.x += signalAdjustY; 
		signal.y += signalAdjustI; 
		signal.z += signalAdjustQ; 

		//shift
		signal.x *= signalShiftY; 
		signal.y *= signalShiftI; 
		signal.z *= signalShiftQ; 

		// //test YIQ
		// signal.x = 0.5;
		// signal.y = p.x*2.0-1.0;
		// signal.z = p.y*2.0-1.0;

		//signal noise
		// float2 noise = n4rand_bw( p,t, 1.0-signalNoisePower )*signalNoiseAmount ; 
		// signal.y *= noise.x;
		// signal.z *= noise.y;


		//phase - it cant be dont coz of intervals
		// signal.x = frac( (signal.x +1.0)*0.5f )*2.0f - 1.0f ;
		//signal.y = frac( (signal.y +1.0)*0.5f )*2.0f - 1.0f ;
		//signal.z = frac( (signal.z +1.0)*0.5f )*2.0f - 1.0f ;
	}
				    
	float3 rgb = yiq2rgb(signal);
				   
	if (VHS_SignalTweak){
		if(gammaCorection!=1.0) rgb = pow(abs(rgb), gammaCorection); //float3(gammaCorection).rgb
	}

	//cut trash after fish eye
	// if( p.x<0. || p.x>1. || p.y<0. || p.y>1.){
	// 	rgb *= 0.0;
	// }

	//TODO maybe on yiq channel
	if (VHS_Vignette){
		rgb *= vignette(p, t*vignetteSpeed); //TODO params //txcoord.xy
	}

	return float4(rgb, 1.0);
}

//third pass

float3 bm_screen(float3 a, float3 b){ 	return 1.0- (1.0-a)*(1.0-b); }

void PS_VHS3(float4 vpos     : SV_Position, out float4 feedbackoutput   : SV_Target0,     
            float2 texcoord : TEXCOORD,    out float4 feedback : SV_Target1 )
{
	float2 p = texcoord.xy;
	const float one_x = 1.0/_ScreenParams.x;
    float3 fc = tex2D( SamplerColorVHS, texcoord).rgb;    // curent frame

    //new feedback
    const float3 fl = tex2D( VHS_InputB, texcoord).rgb;    //last frame without feedback
    float  diff = abs(fl.x-fc.x + fl.y-fc.y + fl.z-fc.z)/3.0; //dfference between frames
    if(diff<feedbackThresh) diff = 0.0;
    float3 fbn = fc*diff*feedbackAmount; //feedback new
    // fbn = float3(0.0, 0.0, 0.0);

    //feedback buffer
	float3 fbb = ( //blur old bufffer a bit
			tex2D( _FeedbackTex, texcoord).rgb + 
			tex2D( _FeedbackTex, texcoord + float2(one_x, 0.0)).rgb + 
			tex2D( _FeedbackTex, texcoord - float2(one_x, 0.0)).rgb
		) / 3.0;
	fbb *= feedbackFade;

    //blend
	fc = bm_screen(fc, fbb);
	fbn = bm_screen(fbn, fbb); //feedback
    feedbackoutput = float4(fc, 1.0); 
    feedback = float4(fbn * feedbackColor, 1.0);
}

void PS_VHS_Lastframe(float4 vpos     : SV_Position, out float4 feedbackoutput   : SV_Target0, 
                      float2 texcoord : TEXCOORD,    out float4 feedback : SV_Target1)
{
    feedbackoutput   = tex2D( VHS_InputA,    texcoord);
    feedback = tex2D( VHS_FeedbackA, texcoord);
}

float4 PS_VHS4(float4 vpos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{

	float3 col = tex2D( SamplerColorVHS, texcoord).rgb; //curent frame
	const float3 fbb = tex2D( _FeedbackTex, texcoord).rgb; //feedback buffer
	
	if (VHS_Feedback){
		col = bm_screen(col, fbb*feedbackAmp);
		if (feedbackDebug){
			col = fbb;
		}
	}

	return float4(col, 1.0);
}

float4 PS_VHSTape(float4 pos : SV_Position, float2 txcoord : TEXCOORD) : SV_Target
{
	const float t = Timer.x * 0.001;
	float2 p = txcoord.xy;
	
	p.y=1-p.y;
	
	const float2 p_ = p*_ScreenParams;
	
	float ns = 0.0; //signal noise //tex2D(_TapeTex, p);
	float nt = 0.0; //tape noise
	float nl = 0.0; //lines for tape noise
	float ntail = 0.0; //tails values for tape noise 
	
	if (VHS_TapeNoise) {

		//here is normilized p (0..1)
			nl = tapeNoiseLines(p, t*tapeNoiseSpeed)*1.0;//tapeNoiseAmount;
			nt = tapeNoise(nl, p, t*tapeNoiseSpeed)*1.0;//tapeNoiseAmount;
			ntail = hash12(p+ float2(0.01,0.02) );
	}
	
	if (VHS_LineNoise) {
			ns += lineNoise(p_, t*lineNoiseSpeed)*lineNoiseAmount;
			// col += lineNoise(pn_, t*lineNoiseSpeed)*lineNoiseAmount;//i.uvn
	}
			
	//y noise from yiq
	if (VHS_FilmGrain) {
		//cheap n ok atm
		ns += filmGrain((p_-0.5*_ScreenParams.xy)*0.5, t, filmGrainPower ) * filmGrainAmount;
	}
		
	return float4(nt,nl,ns,ntail);
}

float4 PS_VHSClear(float4 pos : SV_Position, float2 txcoord : TEXCOORD) : SV_Target
{
	return float4(tex2D(SamplerColorVHS, txcoord).rgb, 1.0);  //black
}

/////////////////////////TECHNIQUES/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////TECHNIQUES/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

technique VHS_Pro
{
	pass VHSPro_First
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_VHS1;
	}
	pass VHSPro_Second
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_VHS2;
	}
	pass VHSPro_Third
	{
		VertexShader  = PostProcessVS;
		PixelShader   = PS_VHS3;
		RenderTarget0 = VHS_InputTexA;
		RenderTarget1 = VHS_FeedbackTexA;
	}
	pass VHSPro_LastFrame
	{
		VertexShader  = PostProcessVS;
		PixelShader   = PS_VHS_Lastframe;   
		RenderTarget0 = VHS_InputTexB;
		RenderTarget1 = VHS_FeedbackTexB;
	}
	pass VHSPro_Forth
	{
		VertexShader  = PostProcessVS;
		PixelShader   = PS_VHS4;
	}
	pass VHSPro_Tape
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_VHSTape;
		RenderTarget = _TapeTex;
	}
	pass VHSPro_Clear
	{
		VertexShader  = PostProcessVS;
		PixelShader   = PS_VHSClear;
	}
}
