This thread is about two shaders, namely crt-guest-sm and crt-guest-advanced. The first part of the thread is about crt-guest-sm, it’s latest versions are available in the official libretro repositories.
The end of the thread contains updates and feedback on my continued development of the later crt-guest-advanced (former crt-guest-dr-venom(2)) - slang shaders.
The latest crt-guest-advanced versions and presets are now in the official libretro shaders/shaders_slang/crt/ folder.
Double internal resolution support added (i.e. 224.0 -> 448.0), replaces 240p, which can be respectively achieved with further internal resolution dividers.
Performance optimizations, up to 10% speed boost
Compatibility mode added to fastest version, should now work with D3D drivers
Substractive options added to bloom passes for better shaping of bloom and halation effect
Stronger filtering mode with interlaced mode implemented (standard, fast versions)
“mclip” parameter now defaults 0.0
added a new option to clean up / adjust mask-scanline interaction in brighter tones when mask effects is too dominant between two of them
mask boost feature is now ‘edge’ aware
some scanline parameter range adjustments
mask bloom overhaul, some minor changes
scanline saturation has now 2 operating modes (one for positive, other for negative values), current presets aren’t affected by changes.
substractive sharpness parameter value increased to 2.0 (standard, fast, fastest versions), the filtering with this mode is improved.
Mask gamma now affects ordinary bloom, contrast improvements.
Magic Glow added, mask 2&6 fixes for slotmask alignment.
Magic Glow improvements, mask control.
Slotmask interaction mitigation implemented.
Brightness adjustments for interlaced modes.
Chroma scaling options added (ntsc), maximum sharpness parameters added to HD and ntsc versions.
slotmask automatic width implemented, support for 16-size luts added
mask zoom feature added
mask zoom overhaul
much better no-scanline mode
mask “shrink” implementation, more fixes
Base Mask strength feature added
ShadowMask options changed, new NTSC phase mode added
New functionality under ‘negative’ mask bloom parameter. Fine bloom sampling option added.
Mask strength controls for bloom and halation added.
Options for thin dark scanlines, per-channel gamma correction and negative bloom distribution added.
New functionality “Clip Saturated Color Beams” added.
Smart Sharpen Scanlines feature added. New ‘negative’ mask bloom.
Scanline saturation functionality overhaul.
Lite version added, grade shader update.
Mask bloom added to the fastest version.
Mask Zoom sharpen option added.
VGA Single/Double scan mode added.
Mask Zoom and Clip Beams added to the fastest version.
Fastest version FF mode fix.
Fastest version overhaul, NTSC version improvements.
Deblur added to NTSC version.
NTSC version scaler support added, various improvements.
High Resolution Scanlines option added to HD and NTSC versions.
High Resolution Scanlines added to standard and fast versions. Fine Glow Sampling added.
The idea is to calculate “scanlines” for individual phosphors and then gradually merge them with regular scanlines. No masks are needed and i think the real CRT behaviour is emulated even better.
Very excited to try this, I think it looks fantastic! Love the concept, too! Above shot looks great on my 6 year old TN panel with my backlight maxed out.
A few things to make it complete:
Can you adjust the phosphors so that pure white appears as 100% magenta/green and the dark lines (“scanlines”) have a certain amount of black added to them? That would get us even closer to how phosphors worked on a CRT (pure white is 100% R, G and B). Normally, this results in an absurdly dark image, but CRT-Aperture does some kind of magic that preserves the brightness and color when increasing the mask strength this way.
I’d also like to see the different phosphor arrangements described in this thread.
a “switch phosphor colors” option that replaces magenta/green with yellow/blue for displays that have RBG subpixels (instead of the usual RGB arrangement)
Options to adjust the scanlines by adjusting phosphor bloom (like on a CRT when you adjust contrast, but without actually altering the phosphor intensities)
A separate option to adjust phosphor intensity independent of phosphor bloom
sharpness adjustment (ideally, you would have a range here with 0 being not quite as blurry as bilinear filter and max being nearest neighbor)
Now i was quite motivated to create a proper shader for this thread.
I used a different template, much faster and simpler, GLSL only, but it should be enough for couriosity sake.
crt-guest-test.glsl (goes in crt/shaders)
/*
CRT - Guest - Test
Copyright (C) 2019 guest(r) - [email protected]
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// Parameter lines go here:
#pragma parameter brightboost "Bright boost" 1.20 0.5 2.0 0.05
#pragma parameter scanline "Scanline adjust" 8.0 1.0 12.0 1.0
#pragma parameter beam_min "Scanline dark" 1.35 0.5 2.0 0.05
#pragma parameter beam_max "Scanline bright" 1.05 0.5 2.0 0.05
#pragma parameter h_sharp "Horizontal sharpness" 2.0 1.0 5.0 0.05
#pragma parameter mask "CRT Mask" 0.0 0.0 1.0 1.0
#pragma parameter maskstr "Raw CRT Mask Strength" 0.15 0.0 1.0 0.05
#pragma parameter gamma_out "Gamma Out" 2.20 1.0 3.0 0.05
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
vec4 _oPosition1;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
COL0 = COLOR;
TEX0.xy = TexCoord.xy * 1.00001;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutputSize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float brightboost;
uniform COMPAT_PRECISION float scanline;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float h_sharp;
uniform COMPAT_PRECISION float mask;
uniform COMPAT_PRECISION float maskstr;
uniform COMPAT_PRECISION float gamma_out;
#else
#define brightboost 1.20 // adjust brightness
#define scanline 8.00 // scanline param, vertical sharpness
#define beam_min 1.35 // dark area beam min - narrow
#define beam_max 1.05 // bright area beam max - wide
#define h_sharp 1.25 // pixel sharpness
#define mask 0.00 // crt mask
#define maskstr 0.15 // raw crt mask strength
#define gamma_out 2.20 // gamma out
#endif
float st(float x)
{
return exp2(-scanline*x*x);
}
vec3 sw(float x, vec3 color)
{
vec3 tmp = mix(vec3(2.75*beam_min),vec3(beam_max), color);
tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color+0.3));
vec3 ex = vec3(x)*tmp;
return exp2(-scanline*ex*ex)/(0.65 + 0.35*color);
}
void main()
{
vec2 OGL2Pos = TEX0.xy * SourceSize.xy - vec2(0.5);
vec2 fp = fract(OGL2Pos);
vec2 pC4 = (floor(OGL2Pos) + vec2(0.5)) * SourceSize.zw;
// Reading the texels
vec3 ul = COMPAT_TEXTURE(Texture, pC4 ).xyz; ul*=ul;
vec3 ur = COMPAT_TEXTURE(Texture, pC4 + vec2(SourceSize.z,0.0)).xyz; ur*=ur;
vec3 dl = COMPAT_TEXTURE(Texture, pC4 + vec2(0.0,SourceSize.w)).xyz; dl*=dl;
vec3 dr = COMPAT_TEXTURE(Texture, pC4 + SourceSize.zw ).xyz; dr*=dr;
float lx = fp.x; lx = pow(lx, h_sharp);
float rx = 1.0 - fp.x; rx = pow(rx, h_sharp);
float w = 1.0/(lx+rx);
vec3 color1 = w*(ur*lx + ul*rx);
vec3 color2 = w*(dr*lx + dl*rx);
ul*=ul*ul; ul*=ul;
ur*=ur*ur; ur*=ur;
dl*=dl*dl; dl*=dl;
dr*=dr*dr; dr*=dr;
vec3 scolor1 = w*(ur*lx + ul*rx); scolor1 = pow(scolor1, vec3(0.166666666666667));
vec3 scolor2 = w*(dr*lx + dl*rx); scolor2 = pow(scolor2, vec3(0.166666666666667));
// calculating scanlines
float f = fp.y;
float t1 = st(f);
float t2 = st(1.0-f);
vec3 color = color1*t1 + color2*t2;
vec3 scolor = scolor1*t1 + scolor2*t2;
vec3 ctemp = color / (t1 + t2);
vec3 sctemp = scolor / (t1 + t2);
vec3 cref1 = mix(scolor1, sctemp, 0.4);
vec3 cref2 = mix(scolor2, sctemp, 0.4);
vec3 w1 = sw(f,cref1);
vec3 w2 = sw(1.0-f,cref2);
color = color1*w1 + color2*w2;
color = min(color, 1.0);
vec3 scan3 = vec3(0.0);
float spos = floor(gl_FragCoord.x * 1.000001); float spos1 = 0.0;
vec3 tmp1 = 0.5*(ctemp+sqrt(ctemp));
if (mask == 1.0)
{
spos1 = fract(spos/3.0);
if (spos1 < 0.333) scan3.x = w1.x*color1.r + w2.x*color2.r;
else if (spos1 < 0.666) scan3.y = w1.y*color1.g + w2.y*color2.g;
else scan3.z = w1.z*color1.b + w2.z*color2.b;
}
else
{
spos1 = fract(spos/2.0);
if (spos1 < 0.5) scan3.xz = w1.xz*color1.rb + w2.xz*color2.rb;
else scan3.y = w1.y* color1.g + w2.y* color2.g;
}
color = mix(1.2*scan3, color, (1.0-maskstr)*tmp1)*(1.0 + 0.2*maskstr);
color*=brightboost;
float corr = (max(max(color.r,color.g),color.b) + 0.0001);
if (corr < 1.0) corr = 1.0;
color = color/corr;
color = pow(color, vec3(1.0/(gamma_out + 0.3*mask)));
FragColor = vec4(color, 1.0);
}
#endif
And the preset (goes to crt…):
shaders = 1
shader0 = shaders/crt-guest-test.glsl
I also thought about Nesguy’s recommendations and he’s definetely very experienced not only with shaders, but with real crt technology also. I’m somewhat limited with 1080p and 60Hz / SDR atm., my HW changes are incomming, but haven’t decided yet what to buy. 4k would make things very interesting, but i’m leaning into an 1440p fast refresh rate purchase. Thankfully there are some geared and talented shader coders around also…
This looks great so far! Just needs the other options I mentioned earlier and it’ll be complete
Going back to my first recommendation, is it possible to get white to appear as 100% magenta/green? This would more closely match what a CRT does (where white is actually created with 100% red, blue and green phosphors). CRT-Aperture with mask strength at 100% does this, but it looks like it also does something to maintain color/brightness/contrast when you do this; otherwise, you wind up wrecking the image quality. It’s recommended that you max out the backlight when doing this; a bright backlight and full array local dimming are also recommended.
I don’t know how essential that particular phosphor arrangement is to the shader, but the ability to switch between different phosphor patterns is also a must-have, IMO.
Not half as experienced as some of the people around here, but thanks! I’ve spent way too much time looking at CRTs and shaders up close.
If you want to do backlight/mask tricks, I’d check out the Samsung QLEDs. They’re the brightest displays you can get aside from models made for outdoor use; I actually looked into those and none of them are great as gaming displays due to input latency. The Samsung QLEDs all support 120 Hz modes so you can do black frame insertion to eliminate motion blur, and they also have less than 1 frame of input lag, although not all models support 4K @ 120 Hz, so that’s something to be aware of.
This is probably the best one. It’s definitely on the higher-end, price wise. Some of the other options are around $1,000 or less, though.
Just tried the updated shader, looking very nice so far! Thanks for your efforts thus far. The magenta/green mask is a big improvement from the standpoint of accuracy since phosphors on a CRT were evenly spaced (and the magenta/green mask gives you even spacing of the LCD subpixels). The second mask option gives you uneven subpixel spacing which makes the blue “phosphors” less visible. Some may prefer it though because it results in a lower TVL (360 as opposed to 540 for the magenta/green mask, on a 1080p display).
I’m still not able to get the correct/desired behavior for very bright phosphors though. It seems like the phosphors disappear altogether over bright colors.
Here’s a closeup showing the correct behavior (correct being what a CRT actually does). This is at 5x vertical integer scale (5 pixels per scanline).
For this to look right you need to max out the LCD backlight and you also need some kind of correction for brightness/contrast/color/etc. CRT-Aperture handles this nicely but I don’t know how it works because I don’t know code.
Also, is there a way to get this concept to work with any phosphor arrangement, or is it limited to aperture grille? The magenta-green slotmask pattern and magenta-green dotmask pattern should be options, if it’s something that can be easily added.
Edit: the sharpness adjustment is excellent! Very CRT-like.
Sounds like a good suggestion. I’ve added a parameter “Raw CRT mask strength” which, at full capacity, produces pure RGB/magenta-green mask pictures (updated version is posted above).
About other dotmasks, pure horizontal ones are the prefered ones with this shader, as it emulates specific phosphor dot sizes per scanline (more or less…).
Slotmask can be added as a seperate function, but i try to keep the code more or less lightweight.
It can be tried with crt-guest-dr-venom, but it likes high vertical display resolutions.
In terms of looks, ease of use and realism, this now rivals CRT-Aperture, IMO. I really like the concept behind this one, though. This one might actually be slightly easier to understand/use, as well. This is now my new favorite shader!
Can you also throw in a parameter for white balance adjustment? That’s about the only other thing I can think of.
I think I can sort of almost understand the reason why it won’t work with dotmask; but it depends on how a “scanline” is defined and I don’t know code so I don’t really know how you’ve done it.
If a “scanline” is one row of pixels being output by the emulator, you could take the average intensity of all the pixels in that row, do some kind of calculation to determine a phosphor size adjustment based on that, and then apply that adjustment to all the pixels in that row when you scale the image.
Am I close at all?
That would explain why it wouldn’t work with dotmask… but then you say it would work with slotmask, so I’m confused There’s a good chance I wouldn’t understand even if you tried explaining it, so no worries!
Very high vertical resolutions . You actually need 8K for the slotmask to produce something close to a realistic result, so slotmask emulation isn’t really an immediate concern but something to keep in mind for the future as 8K and higher displays become more common. When we’re old men we should be able to do a fairly convincing slotmask emulation, lol.
edit: I’ve been playing around with the shader some more and I’ve noticed that it’s possible to fine-tune this even more than CRT-Aperture; looks like the granularity of the adjustments is greater. Compared to CRT-Aperture, I also think I slightly prefer the way your new shader looks after playing around with the parameters a bit; looks like the scanlines taper in a way that is more natural. Threw a bunch of different games at it and everything looked fantastic. Very nice work!
Raw CRT mask strength @ 50%, and a few other tweaks. Must be viewed @ 1080p and at full size or the mask won’t scale correctly. Also helps to crank up the backlight.
@guest.r
I was wondering if if it was maybe possible to get other scanline (modes) like in crt-guest-dr-venom? The only reason I’m bringing it up is scanline code seems similar-ish in places so I was thinking it may not be too hard to do it.
I can do that, sure. I included my standard shader for this, you can get a warm or a cool touch with the colors, plus saturation is included.
Pretty close. It’s based on a fact, that CRT phosphors don’t share the same coordinates on the display plane. So each “emulated” phosphor has a size depending on the color component strength and the mask effect is the result. But we got to mix it with regular “LCD” beams, because of the tricks optics play on us when we look at CRT’s from a greater distance.
Slotmask can be added seperately, it doesn’t have to be a part of the regular composition. But, as you mentioned, it gets better with high resolution displays with HDR.
Thanks indeed. I think it’s even better than my older trinitron masks (5&6).
Scanline code is a bit heavy (so i’m hesitating to add more of it), but it should od well if you adjust the mask-dark value below 1.0 and adjust saturation, gamma and brightboost…
Nevertheless, i think the shader is complete now. There are minor changes in naming and in which folder it should go. Last but not least, i added smart Y integer scaling which works very nice for me and got used to it.
crt-guest-sm.glsl (goes into crt/shaders/guest )
/*
CRT - Guest - SM (Scanline Mask) Shader
Copyright (C) 2019 guest(r) - [email protected]
Big thanks to Nesguy from the Libretro forums for the masks and other ideas.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* README - MASKS GUIDE
To obtain the best results with masks 0, 1, 3, 4:
must leave “mask size” at 1 and the display must be set to its native resolution to result in evenly spaced “active” LCD subpixels.
Mask 0: Uses a magenta and green pattern for even spacing of the LCD subpixels.
Mask 1: Intended for displays that have RBG subpixels (as opposed to the more common RGB).
Uses a yellow/blue pattern for even spacing of the LCD subpixels.
Mask 2: Common red/green/blue pattern.
Mask 3: This is useful for 4K displays, where masks 0 and 1 can look too fine.
Uses a red/yellow/cyan/blue pattern to result in even spacing of the LCD subpixels.
Mask 4: Intended for displays that have the less common RBG subpixel pattern.
This is useful for 4K displays, where masks 0 and 1 can look too fine.
Uses a red/magenta/cyan/green pattern for even spacing of the LCD subpixels.
*/
// Parameter lines go here:
#pragma parameter smart "Smart Y Integer Scaling" 0.0 0.0 1.0 1.0
#pragma parameter brightboost "Bright boost" 1.20 0.5 2.0 0.05
#pragma parameter scanline "Scanline adjust" 8.0 1.0 12.0 1.0
#pragma parameter beam_min "Scanline dark" 1.35 0.5 2.0 0.05
#pragma parameter beam_max "Scanline bright" 1.05 0.5 2.0 0.05
#pragma parameter h_sharp "Horizontal sharpness" 2.0 1.0 5.0 0.05
#pragma parameter mask "CRT Mask (2,3,4 4k masks)" 0.0 0.0 4.0 1.0
#pragma parameter maskstr "Raw CRT Mask Strength" 0.15 0.0 1.0 0.05
#pragma parameter masksize "CRT Mask Size" 1.0 1.0 2.0 1.0
#pragma parameter gamma_out "Gamma Out" 2.20 1.0 3.0 0.05
#if defined(VERTEX)
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
vec4 _oPosition1;
uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
void main()
{
gl_Position = MVPMatrix * VertexCoord;
COL0 = COLOR;
TEX0.xy = TexCoord.xy * 1.00001;
}
#elif defined(FRAGMENT)
#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutputSize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float smart;
uniform COMPAT_PRECISION float brightboost;
uniform COMPAT_PRECISION float scanline;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float h_sharp;
uniform COMPAT_PRECISION float mask;
uniform COMPAT_PRECISION float maskstr;
uniform COMPAT_PRECISION float masksize;
uniform COMPAT_PRECISION float gamma_out;
#else
#define brightboost 0.00 // smart Y integer scaling
#define brightboost 1.20 // adjust brightness
#define scanline 8.00 // scanline param, vertical sharpness
#define beam_min 1.35 // dark area beam min - narrow
#define beam_max 1.05 // bright area beam max - wide
#define h_sharp 1.25 // pixel sharpness
#define mask 0.00 // crt mask
#define maskstr 0.15 // raw crt mask strength
#define masksize 1.00 // crt mask size
#define gamma_out 2.20 // gamma out
#endif
float st(float x)
{
return exp2(-scanline*x*x);
}
vec3 sw(float x, vec3 color)
{
vec3 tmp = mix(vec3(2.75*beam_min),vec3(beam_max), color);
tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color+0.3));
vec3 ex = vec3(x)*tmp;
return exp2(-scanline*ex*ex)/(0.65 + 0.35*color);
}
float Overscan(float pos, float dy){
pos=pos*2.0-1.0;
pos*=dy;
return pos*0.5+0.5;
}
void main()
{
vec2 tex = TEX0.xy * 1.000001;
if (smart == 1.0)
{
float factor = OutputSize.y/InputSize.y;
float intfactor = round(factor);
float diff = factor/intfactor;
tex.y = Overscan(tex.y*(SourceSize.y/InputSize.y), diff)*(InputSize.y/SourceSize.y);
}
vec2 OGL2Pos = tex * SourceSize.xy - vec2(0.5);
vec2 fp = fract(OGL2Pos);
vec2 pC4 = (floor(OGL2Pos) + vec2(0.5)) * SourceSize.zw;
// Reading the texels
vec3 ul = COMPAT_TEXTURE(Texture, pC4 ).xyz; ul*=ul;
vec3 ur = COMPAT_TEXTURE(Texture, pC4 + vec2(SourceSize.z,0.0)).xyz; ur*=ur;
vec3 dl = COMPAT_TEXTURE(Texture, pC4 + vec2(0.0,SourceSize.w)).xyz; dl*=dl;
vec3 dr = COMPAT_TEXTURE(Texture, pC4 + SourceSize.zw ).xyz; dr*=dr;
float lx = fp.x; lx = pow(lx, h_sharp);
float rx = 1.0 - fp.x; rx = pow(rx, h_sharp);
float w = 1.0/(lx+rx);
vec3 color1 = w*(ur*lx + ul*rx);
vec3 color2 = w*(dr*lx + dl*rx);
ul*=ul*ul; ul*=ul;
ur*=ur*ur; ur*=ur;
dl*=dl*dl; dl*=dl;
dr*=dr*dr; dr*=dr;
vec3 scolor1 = w*(ur*lx + ul*rx); scolor1 = pow(scolor1, vec3(0.166666666666667));
vec3 scolor2 = w*(dr*lx + dl*rx); scolor2 = pow(scolor2, vec3(0.166666666666667));
// calculating scanlines
float f = fp.y;
float t1 = st(f);
float t2 = st(1.0-f);
vec3 color = color1*t1 + color2*t2;
vec3 scolor = scolor1*t1 + scolor2*t2;
vec3 ctemp = color / (t1 + t2);
vec3 sctemp = scolor / (t1 + t2);
vec3 cref1 = mix(scolor1, sctemp, 0.35);
vec3 cref2 = mix(scolor2, sctemp, 0.35);
vec3 w1 = sw(f,cref1);
vec3 w2 = sw(1.0-f,cref2);
color = color1*w1 + color2*w2;
color = min(color, 1.0);
vec3 scan3 = vec3(0.0);
float spos = floor((gl_FragCoord.x * 1.000001)/masksize); float spos1 = 0.0;
vec3 tmp1 = 0.5*(ctemp+sqrt(ctemp));
if (mask == 0.0)
{
spos1 = fract(spos*0.5);
if (spos1 < 0.5) scan3.rb = color.rb;
else scan3.g = color.g;
}
else
if (mask == 1.0)
{
spos1 = fract(spos*0.5);
if (spos1 < 0.5) scan3.rg = color.rg;
else scan3.b = color.b;
}
else
if (mask == 2.0)
{
spos1 = fract(spos/3.0);
if (spos1 < 0.333) scan3.r = color.r;
else if (spos1 < 0.666) scan3.g = color.g;
else scan3.b = color.b;
}
else
if (mask == 3.0)
{
spos1 = fract(spos*0.25);
if (spos1 < 0.25) scan3.r = color.r;
else if (spos1 < 0.50) scan3.rg = color.rg;
else if (spos1 < 0.75) scan3.gb = color.gb;
else scan3.b = color.b;
}
else
{
spos1 = fract(spos*0.25);
if (spos1 < 0.25) scan3.r = color.r;
else if (spos1 < 0.50) scan3.rb = color.rb;
else if (spos1 < 0.75) scan3.gb = color.gb;
else scan3.g = color.g;
}
color = mix(1.2*scan3, color, (1.0-maskstr)*tmp1)*(1.0 + 0.2*maskstr);
color*=brightboost;
float corr = (max(max(color.r,color.g),color.b) + 0.0001);
if (corr < 1.0) corr = 1.0;
color = color/corr;
color = pow(color, vec3(1.0/(gamma_out + 0.2*mask)));
FragColor = vec4(color, 1.0);
}
#endif