Created
September 1, 2021 03:45
-
-
Save blackle/ab5bb98971fd6264269820c5f15330a9 to your computer and use it in GitHub Desktop.
Scanline Filter for ShaderFilter Plus
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER | |
// | |
// by Timothy Lottes | |
// | |
// This is more along the style of a really good CGA arcade monitor. | |
// With RGB inputs instead of NTSC. | |
// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration. | |
// | |
// Left it unoptimized to show the theory behind the algorithm. | |
// | |
// It is an example what I personally would want as a display option for pixel art games. | |
// Please take and use, change, or whatever. | |
// | |
//https://www.shadertoy.com/view/XsjSzR ported to ShaderFilter plus by Blackle Mori | |
#pragma shaderfilter set fade__description Fade | |
#pragma shaderfilter set fade__default 0.5 | |
#pragma shaderfilter set fade__min 0.0 | |
#pragma shaderfilter set fade__max 1.0 | |
#pragma shaderfilter set fade__step 0.01 | |
#pragma shaderfilter set fade__slider true | |
uniform float fade; | |
#pragma shaderfilter set scale__description Scale | |
#pragma shaderfilter set scale__default 1.0 | |
#pragma shaderfilter set scale__min 0.1 | |
#pragma shaderfilter set scale__max 2.0 | |
#pragma shaderfilter set scale__step 0.01 | |
#pragma shaderfilter set scale__slider true | |
uniform float scale; | |
#pragma shaderfilter set hardScan__description Intensity of Scanlines | |
#pragma shaderfilter set hardScan__default -10.0 | |
#pragma shaderfilter set hardScan__min -24.0 | |
#pragma shaderfilter set hardScan__max -4.0 | |
#pragma shaderfilter set hardScan__step 0.1 | |
#pragma shaderfilter set hardScan__slider true | |
uniform float hardScan; | |
#pragma shaderfilter set hardPix__description Hardness of pixels | |
#pragma shaderfilter set hardPix__default -3.0 | |
#pragma shaderfilter set hardPix__min -6.0 | |
#pragma shaderfilter set hardPix__max -1.0 | |
#pragma shaderfilter set hardPix__step 0.1 | |
#pragma shaderfilter set hardPix__slider true | |
uniform float hardPix; | |
#pragma shaderfilter set warp_x__description Warp X | |
#pragma shaderfilter set warp_x__default 0.03 | |
#pragma shaderfilter set warp_x__min 0.0 | |
#pragma shaderfilter set warp_x__max 0.1 | |
#pragma shaderfilter set warp_x__step 0.001 | |
#pragma shaderfilter set warp_x__slider true | |
uniform float warp_x; | |
#pragma shaderfilter set warp_y__description Warp Y | |
#pragma shaderfilter set warp_y__default 0.04 | |
#pragma shaderfilter set warp_y__min 0.0 | |
#pragma shaderfilter set warp_y__max 0.1 | |
#pragma shaderfilter set warp_y__step 0.001 | |
#pragma shaderfilter set warp_y__slider true | |
uniform float warp_y; | |
// Hardness of scanline. | |
// -8.0 = soft | |
// -16.0 = medium | |
// #define hardScan -16.0 | |
// Hardness of pixels in scanline. | |
// -2.0 = soft | |
// -4.0 = hard | |
// #define hardPix -4.0 | |
// Amount of shadow mask. | |
#define maskDark 0.5 | |
#define maskLight 1.5 | |
//------------------------------------------------------------------------ | |
// sRGB to Linear. | |
// Assuing using sRGB typed textures this should not be needed. | |
float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);} | |
vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));} | |
// Linear to sRGB. | |
// Assuing using sRGB typed textures this should not be needed. | |
float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);} | |
vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));} | |
// Nearest emulated sample given floating point position and texel offset. | |
// Also zero's off screen. | |
vec3 Fetch(vec2 pos,vec2 off){ | |
pos=round(pos/scale*builtin_uv_size/6.0+off)*scale/builtin_uv_size*6.0; | |
if(max(abs(pos.x-0.5),abs(pos.y-0.5))>0.5)return vec3(0.0,0.0,0.0); | |
return ToLinear(( | |
image.Sample(builtin_texture_sampler,pos).rgb | |
+image.Sample(builtin_texture_sampler,pos+vec2(0.0,0.5)/builtin_uv_size*scale*6.0).rgb | |
+image.Sample(builtin_texture_sampler,pos-vec2(0.0,0.5)/builtin_uv_size*scale*6.0).rgb)/3.0);} | |
// Distance in emulated pixels to nearest texel. | |
vec2 Dist(vec2 pos){pos=pos/scale*builtin_uv_size/6.0;return -((pos-round(pos)));} | |
// 1D Gaussian. | |
float Gaus(float pos,float scale){return exp2(scale*pos*pos);} | |
// 3-tap Gaussian filter along horz line. | |
vec3 Horz3(vec2 pos,float off){ | |
vec3 b=Fetch(pos,vec2(-1.0,off)); | |
vec3 c=Fetch(pos,vec2( 0.0,off)); | |
vec3 d=Fetch(pos,vec2( 1.0,off)); | |
float dst=Dist(pos).x; | |
// Convert distance to weight. | |
float scale=hardPix; | |
float wb=Gaus(dst-1.0,scale); | |
float wc=Gaus(dst+0.0,scale); | |
float wd=Gaus(dst+1.0,scale); | |
// Return filtered sample. | |
return (b*wb+c*wc+d*wd)/(wb+wc+wd);} | |
// 5-tap Gaussian filter along horz line. | |
vec3 Horz5(vec2 pos,float off){ | |
vec3 a=Fetch(pos,vec2(-2.0,off)); | |
vec3 b=Fetch(pos,vec2(-1.0,off)); | |
vec3 c=Fetch(pos,vec2( 0.0,off)); | |
vec3 d=Fetch(pos,vec2( 1.0,off)); | |
vec3 e=Fetch(pos,vec2( 2.0,off)); | |
float dst=Dist(pos).x; | |
// Convert distance to weight. | |
float scale=hardPix; | |
float wa=Gaus(dst-2.0,scale); | |
float wb=Gaus(dst-1.0,scale); | |
float wc=Gaus(dst+0.0,scale); | |
float wd=Gaus(dst+1.0,scale); | |
float we=Gaus(dst+2.0,scale); | |
// Return filtered sample. | |
return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we); | |
} | |
// Return scanline weight. | |
float Scan(vec2 pos,float off){ | |
float dst=Dist(pos).y; | |
return Gaus(dst+off,hardScan);} | |
// Allow nearest three lines to effect pixel. | |
vec3 Tri(vec2 pos){ | |
vec3 a=Horz3(pos,-1.0); | |
vec3 b=Horz5(pos, 0.0); | |
vec3 c=Horz3(pos, 1.0); | |
float wa=Scan(pos,-1.0); | |
float wb=Scan(pos, 0.0); | |
float wc=Scan(pos, 1.0); | |
return a*wa+b*wb+c*wc;} | |
// Shadow mask. | |
vec3 Mask(vec2 pos){ | |
pos.x+=pos.y*3.0; | |
vec3 mask=vec3(maskDark,maskDark,maskDark); | |
pos.x=fract(pos.x/6.0); | |
pos = round(pos*builtin_uv_size*scale)/builtin_uv_size/scale; | |
if(pos.x<0.333)mask.r=maskLight; | |
else if(pos.x<0.666)mask.g=maskLight; | |
else mask.b=maskLight; | |
return mask;} | |
vec2 Warp(vec2 pos){ | |
pos=pos*2.0-1.0; | |
pos*=vec2(1.0+(pos.y*pos.y)*warp_x,1.0+(pos.x*pos.x)*warp_y); | |
return pos*0.5+0.5;} | |
// Draw dividing bars. | |
float Bar(float pos,float bar){pos-=bar;return pos*pos<4.0?0.0:1.0;} | |
vec4 render(vec2 uv) { | |
uv = Warp(uv); | |
vec3 col = image.Sample(builtin_texture_sampler, uv).rgb; | |
return vec4(mix(col, ToSrgb(Tri(uv)*Mask(uv*builtin_uv_size)), fade), 1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment