mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-14 02:17:36 +08:00
293 lines
6.8 KiB
GLSL
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
|