From 73db8b3fd27cc8ae8b475c6dfafeb543b8783935 Mon Sep 17 00:00:00 2001 From: hunterk Date: Tue, 12 Jul 2016 15:20:15 -0500 Subject: [PATCH] add crt-lottes --- crt/crt-lottes.slang | 287 ++++++++++++++++++++++++++++++++++++++++++ crt/crt-lottes.slangp | 5 + 2 files changed, 292 insertions(+) create mode 100644 crt/crt-lottes.slang create mode 100644 crt/crt-lottes.slangp diff --git a/crt/crt-lottes.slang b/crt/crt-lottes.slang new file mode 100644 index 0000000..aa82dd6 --- /dev/null +++ b/crt/crt-lottes.slang @@ -0,0 +1,287 @@ +#version 450 + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; +} global; + +#pragma stage vertex +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; + +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +// 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. + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec2 FragCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +// -- config -- // + +#define hardScan -8.0 +#define hardPix -3.0 +#define warpX 0.031 +#define warpY 0.041 +#define maskDark 0.5 +#define maskLight 1.5 +#define scaleInLinearGamma 1 +#define shadowMask 1 +#define brightboost 1 +#define hardBloomScan -2.0 +#define hardBloomPix -1.5 +#define bloomAmount 1.0/16.0 +#define shape 2.0 + +//Uncomment to reduce instructions with simpler linearization +//(fixes HD3000 Sandy Bridge IGP) +//#define SIMPLE_LINEAR_GAMMA +#define DO_BLOOM 1 + +// ------------- // + +// sRGB to Linear. +// Assuming using sRGB typed textures this should not be needed. +#ifdef SIMPLE_LINEAR_GAMMA +float ToLinear1(float c) +{ + return c; +} +float3 ToLinear(vec3 c) +{ + return c; +} +vec3 ToSrgb(vec3 c) +{ + return pow(c, 1.0 / 2.2); +} +#else +float ToLinear1(float c) +{ + if (scaleInLinearGamma==0) return c; + return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4); +} + +vec3 ToLinear(vec3 c) +{ + if (scaleInLinearGamma==0) return c; + return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b)); +} + +// Linear to sRGB. +// Assuming using sRGB typed textures this should not be needed. +float ToSrgb1(float c) +{ + if (scaleInLinearGamma==0) return c; + return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055); +} + +vec3 ToSrgb(vec3 c) +{ + if (scaleInLinearGamma==0) return c; + return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b)); +} +#endif + + + +// Nearest emulated sample given floating point position and texel offset. +// Also zero's off screen. +vec3 Fetch(vec2 pos,vec2 off){ + pos=(floor(pos*global.SourceSize.xy+off)+vec2(0.5,0.5))/global.SourceSize.xy; +#ifdef SIMPLE_LINEAR_GAMMA + return ToLinear(brightboost * pow(texture(Source,pos.xy).rgb, 2.2)); +#else + return ToLinear(brightboost * texture(Source,pos.xy).rgb);} +#endif + +// Distance in emulated pixels to nearest texel. +vec2 Dist(vec2 pos){pos=pos*global.SourceSize.xy;return -((pos-floor(pos))-vec2(0.5));} + +// 1D Gaussian. +float Gaus(float pos,float scale){return exp2(scale*pow(abs(pos),shape));} + +// 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);} + +// 7-tap Gaussian filter along horz line. +vec3 Horz7(vec2 pos,float off){ + vec3 a=Fetch(pos,vec2(-3.0,off)); + vec3 b=Fetch(pos,vec2(-2.0,off)); + vec3 c=Fetch(pos,vec2(-1.0,off)); + vec3 d=Fetch(pos,vec2( 0.0,off)); + vec3 e=Fetch(pos,vec2( 1.0,off)); + vec3 f=Fetch(pos,vec2( 2.0,off)); + vec3 g=Fetch(pos,vec2( 3.0,off)); + float dst=Dist(pos).x; + // Convert distance to weight. + float scale=hardBloomPix; + float wa=Gaus(dst-3.0,scale); + float wb=Gaus(dst-2.0,scale); + float wc=Gaus(dst-1.0,scale); + float wd=Gaus(dst+0.0,scale); + float we=Gaus(dst+1.0,scale); + float wf=Gaus(dst+2.0,scale); + float wg=Gaus(dst+3.0,scale); + // Return filtered sample. + return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);} + +// Return scanline weight. +float Scan(vec2 pos,float off){ + float dst=Dist(pos).y; + return Gaus(dst+off,hardScan);} + +// Return scanline weight for bloom. +float BloomScan(vec2 pos,float off){ + float dst=Dist(pos).y; + return Gaus(dst+off,hardBloomScan);} + +// 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;} + +// Small bloom. +vec3 Bloom(vec2 pos){ + vec3 a=Horz5(pos,-2.0); + vec3 b=Horz7(pos,-1.0); + vec3 c=Horz7(pos, 0.0); + vec3 d=Horz7(pos, 1.0); + vec3 e=Horz5(pos, 2.0); + float wa=BloomScan(pos,-2.0); + float wb=BloomScan(pos,-1.0); + float wc=BloomScan(pos, 0.0); + float wd=BloomScan(pos, 1.0); + float we=BloomScan(pos, 2.0); + return a*wa+b*wb+c*wc+d*wd+e*we;} + +// Distortion of scanlines, and end of screen alpha. +vec2 Warp(vec2 pos){ + pos=pos*2.0-1.0; + pos*=vec2(1.0+(pos.y*pos.y)*warpX,1.0+(pos.x*pos.x)*warpY); + return pos*0.5+0.5;} + +// Shadow mask. +vec3 Mask(vec2 pos){ + vec3 mask=vec3(maskDark,maskDark,maskDark); + +// Very compressed TV style shadow mask. + if (shadowMask == 1.0) { + float line=maskLight; + float odd=0.0; + if(fract(pos.x/6.0)<0.5)odd=1.0; + if(fract((pos.y+odd)/2.0)<0.5)line=maskDark; + pos.x=fract(pos.x/3.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + mask*=line; + } + +// Aperture-grille. + else if (shadowMask == 2.0) { + pos.x=fract(pos.x/3.0); + + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + } + +// Stretched VGA style shadow mask (same as prior shaders). + else if (shadowMask == 3.0) { + pos.x+=pos.y*3.0; + 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; + } + +// VGA style shadow mask. + else if (shadowMask == 4.0) { + pos.xy=floor(pos.xy*vec2(1.0,0.5)); + pos.x+=pos.y*3.0; + 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;} + +void main() +{ +vec2 pos=Warp(vTexCoord); +vec3 outColor = Tri(pos); + #ifdef DO_BLOOM + //Add Bloom + outColor.rgb+=Bloom(pos)*bloomAmount; + #endif +if(shadowMask > 0.0) + outColor.rgb*=Mask(vTexCoord.xy / global.OutputSize.zw * 1.000001); +//hacky clamp fix +vec2 bordertest = pos; +if ( bordertest.x > 1.0 && bordertest.x < 0.0 && bordertest.y > 1.0 && bordertest.y < 0.0) +outColor.rgb = vec3(0.0); +else +outColor.rgb = outColor.rgb; + + FragColor = vec4(ToSrgb(outColor.rgb), 1.0); +} \ No newline at end of file diff --git a/crt/crt-lottes.slangp b/crt/crt-lottes.slangp new file mode 100644 index 0000000..d3554bd --- /dev/null +++ b/crt/crt-lottes.slangp @@ -0,0 +1,5 @@ +shaders = 1 + +shader0 = crt-lottes.slang +filter_linear0 = false +scale_type_0 = source