Files
dosbox-x/contrib/glshaders/crt-lottes.tweaked.glsl
2020-09-22 14:02:29 -04:00

293 lines
6.8 KiB
GLSL

#version 120
/*
marty_CRT-Lottes_tweaked.glsl
marty's adaptation of dugan's CRT-Lottes port
adapted again by liPillON for usage with DosBox SVN r4319 and later
slightly tweaked the parameters in order to:
- reduce the blurring,
- bump up the brightness a bit,
- soften the crt mask,
- tone down the display curvature
source shader files:
https://github.com/duganchen/dosbox_shaders/blob/master/crt-lottes.frag
https://github.com/duganchen/dosbox_shaders/blob/master/crt-lottes.vert
marty's source is available only within the release package of its port:
https://github.com/MartyShepard/DOSBox-Optionals
first posted here:
https://www.vogons.org/viewtopic.php?f=32&t=72697
*/
uniform vec2 rubyTextureSize;
uniform vec2 rubyInputSize;
#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 a_position;
COMPAT_VARYING vec2 v_texCoord;
void main()
{
gl_Position = a_position;
v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize/rubyTextureSize;
}
#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 highp
#else
#define COMPAT_PRECISION
#endif
COMPAT_VARYING vec2 v_texCoord;
uniform vec2 rubyOutputSize;
uniform sampler2D rubyTexture;
const float hardScan = -7.0; //tweaked
const float hardPix = -3.5; //tweaked
const float warpX = 0.0075; //tweaked
const float warpY = 0.0075; //tweaked
const float maskDark = 0.5;
const float maskLight = 1.5;
const float scaleInLinearGamma = 1;
const float shadowMask = 1;
const float brightBoost = 1.4; //tweaked
#define Blackmask 1
//------------------------------------------------------------------------
// sRGB to Linear.
// Assuing using sRGB typed textures this should not be needed.
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.
// Assuing 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));
}
// Nearest emulated sample given floating point position and texel offset.
// Also zero's off screen.
vec3 Fetch(vec2 pos,vec2 off)
{
pos = (floor(pos * rubyTextureSize.xy + off) + vec2(0.5, 0.5)) / rubyTextureSize.xy;
return ToLinear(brightBoost * COMPAT_TEXTURE(rubyTexture, pos.xy).rgb);
}
// Distance in emulated pixels to nearest texel.
vec2 Dist(vec2 pos)
{
pos = pos * rubyTextureSize.xy;
return -((pos - floor(pos)) - vec2(0.5));
}
// 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;
}
// 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)
{
pos.x += pos.y * 3.0;
vec3 mask = vec3(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;
}
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
float box(vec2 _st, vec2 _size, float _smoothEdges) {
_size = vec2(.1) - _size*.2;
vec2 aa = vec2(_smoothEdges * 0.1);
vec2 uv = smoothstep(_size, _size+aa, _st);
uv *= smoothstep(_size, _size+aa, vec2(1.0)-_st);
return uv.x * uv.y;
}
vec3 drawRectangle(in vec2 st) {
// Each result will return 1.0 (white) or 0.0 (
vec3 color = vec3(0.0);
vec2 borders = step(vec2(0.0),st);
float pct = borders.x * borders.y;
// top-right
vec2 tr = step(vec2(0.1),1.0-st);
pct *= tr.x * tr.y;
// The multiplication of left*bottom will be similar to the logical AND.
color = vec3(pct);
return color;
}
void main()
{
vec2 pos = Warp(v_texCoord.xy * (rubyTextureSize.xy / rubyInputSize.xy)) * (rubyInputSize.xy / rubyTextureSize.xy);
#ifdef Blackmask
vec3 outColor = vec3(1.0);
outColor *= Tri(pos);
outColor *= drawRectangle(pos);
outColor *= vec3(box(pos, vec2(0.5), 0.01));
#else
vec3 outColor = Tri(pos);
#endif
if (shadowMask != 0)
{
outColor.rgb *= Mask(floor(v_texCoord.xy * (rubyTextureSize.xy / rubyInputSize.xy) * rubyOutputSize.xy) + vec2(0.5, 0.5));
}
FragColor = vec4(ToSrgb(outColor.rgb), 1.0);
}
#endif