Created
August 31, 2021 07:33
-
-
Save blackle/8480a9c3f3d0c12fdad0dfb39f65325f to your computer and use it in GitHub Desktop.
This file contains hidden or 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. | |
// | |
uniform float total_scale = 1.0; | |
float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);} | |
float3 ToLinear(float3 c){return float3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));} | |
float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);} | |
float3 ToSrgb(float3 c){return float3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));} | |
float3 Fetch(float2 pos,float2 off){ | |
pos=floor(pos/uv_pixel_interval/6.0/total_scale+off)*uv_pixel_interval*6.0*total_scale; | |
if(max(abs(pos.x-0.5),abs(pos.y-0.5))>0.5)return float3(0.0,0.0,0.0); | |
return ToLinear(image.Sample(textureSampler,pos.xy).rgb);} | |
float2 Dist(float2 pos){pos=pos/uv_pixel_interval/6.0/total_scale;return -((pos-floor(pos))-float2(0.5));} | |
float Gaus(float pos,float scale){return exp2(scale*pos*pos);} | |
float3 Horz3(float2 pos,float off){ | |
float3 b=Fetch(pos,float2(-1.0,off)); | |
float3 c=Fetch(pos,float2( 0.0,off)); | |
float3 d=Fetch(pos,float2( 1.0,off)); | |
float dst=Dist(pos).x; | |
// Convert distance to weight. | |
float scale=-3.0; | |
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. | |
float3 Horz5(float2 pos,float off){ | |
float3 a=Fetch(pos,float2(-2.0,off)); | |
float3 b=Fetch(pos,float2(-1.0,off)); | |
float3 c=Fetch(pos,float2( 0.0,off)); | |
float3 d=Fetch(pos,float2( 1.0,off)); | |
float3 e=Fetch(pos,float2( 2.0,off)); | |
float dst=Dist(pos).x; | |
// Convert distance to weight. | |
float scale=-3.0; | |
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(float2 pos,float off){ | |
float dst=Dist(pos).y; | |
return Gaus(dst+off,-8.);} | |
// Allow nearest three lines to effect pixel. | |
float3 Tri(float2 pos){ | |
float3 a=Horz3(pos,-1.0); | |
float3 b=Horz5(pos, 0.0); | |
float3 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. | |
float3 Mask(float2 pos){ | |
pos /= total_scale; | |
pos.x+=pos.y*3.0; | |
float maskDark=0.5; | |
float maskLight=1.5; | |
float3 mask=float3(maskDark,maskDark,maskDark); | |
pos.x=fract(pos.x/6.0); | |
if(pos.x<0.333)mask.r=maskLight; | |
else if(pos.x<0.666)mask.g=maskLight; | |
else mask.b=maskLight; | |
return mask; | |
} | |
// Entry. | |
float4 mainImage(VertData v_in) : TARGET | |
{ | |
return float4(ToSrgb(Tri(v_in.uv)*Mask(v_in.uv/uv_pixel_interval)),1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment