/* 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.yoy*(ln-w)) ////if(uv.y>oy*ln && uv.yoy*(ln-w)) ////if(uv.y>oy*ln && uv.y 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