mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-08 19:32:39 +08:00
263 lines
8.7 KiB
GLSL
263 lines
8.7 KiB
GLSL
#version 120
|
|
|
|
/*
|
|
zfast_crt_standard - A simple, fast CRT shader.
|
|
|
|
Copyright (C) 2017 Greg Hogan (SoltanGris42)
|
|
|
|
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.
|
|
|
|
|
|
Notes: This shader does scaling with a weighted linear filter for adjustable
|
|
sharpness on the x and y axes based on the algorithm by Inigo Quilez here:
|
|
http://http://www.iquilezles.org/www/articles/texture/texture.htm
|
|
but modified to be somewhat sharper. Then a scanline effect that varies
|
|
based on pixel brightness is applied along with a monochrome aperture mask.
|
|
This shader runs at 60fps on the Raspberry Pi 3 hardware at 2mpix/s
|
|
resolutions (1920x1080 or 1600x1200).
|
|
*/
|
|
|
|
//For testing compilation
|
|
//#define FRAGMENT
|
|
//#define VERTEX
|
|
|
|
//This can't be an option without slowing the shader down
|
|
//Comment this out for a coarser 3 pixel mask...which is currently broken
|
|
//on SNES Classic Edition due to Mali 400 gpu precision
|
|
#define FINEMASK
|
|
|
|
//Some drivers don't return black with texture coordinates out of bounds
|
|
//SNES Classic is too slow to black these areas out when using fullscreen
|
|
//overlays. But you can uncomment the below to black them out if necessary
|
|
//#define BLACK_OUT_BORDER
|
|
|
|
// Parameter lines go here:
|
|
#pragma parameter BLURSCALEX "Blur Amount X-Axis" 0.30 0.0 1.0 0.05
|
|
#pragma parameter LOWLUMSCAN "Scanline Darkness - Low" 6.0 0.0 10.0 0.5
|
|
#pragma parameter HILUMSCAN "Scanline Darkness - High" 8.0 0.0 50.0 1.0
|
|
#pragma parameter BRIGHTBOOST "Dark Pixel Brightness Boost" 1.25 0.5 1.5 0.05
|
|
#pragma parameter MASK_DARK "Mask Effect Amount" 0.25 0.0 1.0 0.05
|
|
#pragma parameter MASK_FADE "Mask/Scanline Fade" 0.8 0.0 1.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 a_position;
|
|
COMPAT_ATTRIBUTE vec4 COLOR;
|
|
COMPAT_ATTRIBUTE vec4 TexCoord;
|
|
COMPAT_VARYING vec4 COL0;
|
|
COMPAT_VARYING vec2 v_texCoord;
|
|
COMPAT_VARYING float maskFade;
|
|
COMPAT_VARYING vec2 invDims;
|
|
|
|
vec4 _oPosition1;
|
|
uniform mat4 MVPMatrix;
|
|
uniform COMPAT_PRECISION int FrameDirection;
|
|
uniform COMPAT_PRECISION int rubyFrameCount;
|
|
uniform COMPAT_PRECISION vec2 rubyOutputSize;
|
|
uniform COMPAT_PRECISION vec2 rubyTextureSize;
|
|
uniform COMPAT_PRECISION vec2 rubyInputSize;
|
|
|
|
// compatibility #defines
|
|
#define vTexCoord v_texCoord.xy
|
|
#define SourceSize vec4(rubyTextureSize, 1.0 / rubyTextureSize) //either rubyTextureSize or rubyInputSize
|
|
#define OutSize vec4(rubyOutputSize, 1.0 / rubyOutputSize)
|
|
|
|
#ifdef PARAMETER_UNIFORM
|
|
// All parameter floats need to have COMPAT_PRECISION in front of them
|
|
uniform COMPAT_PRECISION float BLURSCALEX;
|
|
//uniform COMPAT_PRECISION float BLURSCALEY;
|
|
uniform COMPAT_PRECISION float LOWLUMSCAN;
|
|
uniform COMPAT_PRECISION float HILUMSCAN;
|
|
uniform COMPAT_PRECISION float BRIGHTBOOST;
|
|
uniform COMPAT_PRECISION float MASK_DARK;
|
|
uniform COMPAT_PRECISION float MASK_FADE;
|
|
#else
|
|
#define BLURSCALEX 0.45
|
|
//#define BLURSCALEY 0.20
|
|
#define LOWLUMSCAN 5.0
|
|
#define HILUMSCAN 10.0
|
|
#define BRIGHTBOOST 1.25
|
|
#define MASK_DARK 0.25
|
|
#define MASK_FADE 0.8
|
|
#endif
|
|
|
|
void main()
|
|
{
|
|
gl_Position = a_position;
|
|
v_texCoord = vec2(a_position.x + 1.0, 1.0 - a_position.y) / 2.0 * rubyInputSize / rubyTextureSize;
|
|
|
|
maskFade = 0.3333*MASK_FADE;
|
|
invDims = 1.0/rubyTextureSize.xy;
|
|
}
|
|
|
|
#elif defined(FRAGMENT)
|
|
|
|
#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
|
|
|
|
#if __VERSION__ >= 130
|
|
#define COMPAT_VARYING in
|
|
#define COMPAT_TEXTURE texture
|
|
out COMPAT_PRECISION vec4 FragColor;
|
|
#else
|
|
#define COMPAT_VARYING varying
|
|
#define FragColor gl_FragColor
|
|
#define COMPAT_TEXTURE texture2D
|
|
#endif
|
|
|
|
uniform COMPAT_PRECISION int FrameDirection;
|
|
uniform COMPAT_PRECISION int rubyFrameCount;
|
|
uniform COMPAT_PRECISION vec2 rubyOutputSize;
|
|
uniform COMPAT_PRECISION vec2 rubyTextureSize;
|
|
uniform COMPAT_PRECISION vec2 rubyInputSize;
|
|
uniform sampler2D rubyTexture;
|
|
COMPAT_VARYING vec2 v_texCoord;
|
|
COMPAT_VARYING float maskFade;
|
|
COMPAT_VARYING vec2 invDims;
|
|
|
|
// compatibility #defines
|
|
#define Source rubyTexture
|
|
#define vTexCoord v_texCoord.xy
|
|
#define texture(c, d) COMPAT_TEXTURE(c, d)
|
|
#define SourceSize vec4(rubyTextureSize, 1.0 / rubyTextureSize) //either rubyTextureSize or rubyInputSize
|
|
#define OutSize vec4(rubyOutputSize, 1.0 / rubyOutputSize)
|
|
|
|
#ifdef PARAMETER_UNIFORM
|
|
// All parameter floats need to have COMPAT_PRECISION in front of them
|
|
uniform COMPAT_PRECISION float BLURSCALEX;
|
|
//uniform COMPAT_PRECISION float BLURSCALEY;
|
|
uniform COMPAT_PRECISION float LOWLUMSCAN;
|
|
uniform COMPAT_PRECISION float HILUMSCAN;
|
|
uniform COMPAT_PRECISION float BRIGHTBOOST;
|
|
uniform COMPAT_PRECISION float MASK_DARK;
|
|
uniform COMPAT_PRECISION float MASK_FADE;
|
|
#else
|
|
#define BLURSCALEX 0.45
|
|
//#define BLURSCALEY 0.20
|
|
#define LOWLUMSCAN 5.0
|
|
#define HILUMSCAN 10.0
|
|
#define BRIGHTBOOST 1.25
|
|
#define MASK_DARK 0.25
|
|
#define MASK_FADE 0.8
|
|
#endif
|
|
|
|
/*
|
|
The following code allows the shader to override any texture filtering
|
|
configured in DOSBox. if 'output' is set to 'opengl', bilinear filtering
|
|
will be enabled and OPENGLNB will not be defined, if 'output' is set to
|
|
'openglnb', nearest neighbour filtering will be enabled and OPENGLNB will
|
|
be defined.
|
|
|
|
If you wish to use the default filtering method that is currently enabled
|
|
in DOSBox, use COMPAT_TEXTURE to lookup a texel from the input texture.
|
|
|
|
If you wish to force nearest-neighbor interpolation use NN_TEXTURE.
|
|
|
|
If you wish to force bilinear interpolation use BL_TEXTURE.
|
|
|
|
If DOSBox is configured to use the filtering method that is being forced,
|
|
the default hardware implementation will be used, otherwise the custom
|
|
implementations below will be used instead.
|
|
|
|
These custom implementations rely on the `rubyTextureSize` uniform variable.
|
|
The code could calculate the texture size from the sampler using the
|
|
textureSize() GLSL function, but this would require a minimum of GLSL
|
|
version 130, which may prevent the shader from working on older systems.
|
|
*/
|
|
|
|
#if defined(OPENGLNB)
|
|
#define NN_TEXTURE COMPAT_TEXTURE
|
|
#define BL_TEXTURE blTexture
|
|
vec4 blTexture(in sampler2D sampler, in vec2 uv)
|
|
{
|
|
// subtract 0.5 here and add it again after the floor to centre the texel
|
|
vec2 texCoord = uv * rubyTextureSize - vec2(0.5);
|
|
vec2 s0t0 = floor(texCoord) + vec2(0.5);
|
|
vec2 s0t1 = s0t0 + vec2(0.0, 1.0);
|
|
vec2 s1t0 = s0t0 + vec2(1.0, 0.0);
|
|
vec2 s1t1 = s0t0 + vec2(1.0);
|
|
|
|
vec2 invTexSize = 1.0 / rubyTextureSize;
|
|
vec4 c_s0t0 = COMPAT_TEXTURE(sampler, s0t0 * invTexSize);
|
|
vec4 c_s0t1 = COMPAT_TEXTURE(sampler, s0t1 * invTexSize);
|
|
vec4 c_s1t0 = COMPAT_TEXTURE(sampler, s1t0 * invTexSize);
|
|
vec4 c_s1t1 = COMPAT_TEXTURE(sampler, s1t1 * invTexSize);
|
|
|
|
vec2 weight = fract(texCoord);
|
|
|
|
vec4 c0 = c_s0t0 + (c_s1t0 - c_s0t0) * weight.x;
|
|
vec4 c1 = c_s0t1 + (c_s1t1 - c_s0t1) * weight.x;
|
|
|
|
return (c0 + (c1 - c0) * weight.y);
|
|
}
|
|
#else
|
|
#define BL_TEXTURE COMPAT_TEXTURE
|
|
#define NN_TEXTURE nnTexture
|
|
vec4 nnTexture(in sampler2D sampler, in vec2 uv)
|
|
{
|
|
vec2 texCoord = floor(uv * rubyTextureSize) + vec2(0.5);
|
|
vec2 invTexSize = 1.0 / rubyTextureSize;
|
|
return COMPAT_TEXTURE(sampler, texCoord * invTexSize);
|
|
}
|
|
#endif
|
|
|
|
void main()
|
|
{
|
|
|
|
//This is just like "Quilez Scaling" but sharper
|
|
COMPAT_PRECISION vec2 p = vTexCoord * rubyTextureSize;
|
|
COMPAT_PRECISION vec2 i = floor(p) + 0.50;
|
|
COMPAT_PRECISION vec2 f = p - i;
|
|
p = (i + 4.0*f*f*f)*invDims;
|
|
p.x = mix( p.x , vTexCoord.x, BLURSCALEX);
|
|
COMPAT_PRECISION float Y = f.y*f.y;
|
|
COMPAT_PRECISION float YY = Y*Y;
|
|
|
|
#if defined(FINEMASK)
|
|
COMPAT_PRECISION float whichmask = fract( gl_FragCoord.x*-0.4999);
|
|
COMPAT_PRECISION float mask = 1.0 + float(whichmask < 0.5) * -MASK_DARK;
|
|
#else
|
|
COMPAT_PRECISION float whichmask = fract(gl_FragCoord.x * -0.3333);
|
|
COMPAT_PRECISION float mask = 1.0 + float(whichmask <= 0.33333) * -MASK_DARK;
|
|
#endif
|
|
COMPAT_PRECISION vec3 colour = BL_TEXTURE(Source, p).rgb;
|
|
|
|
COMPAT_PRECISION float scanLineWeight = (BRIGHTBOOST - LOWLUMSCAN*(Y - 2.05*YY));
|
|
COMPAT_PRECISION float scanLineWeightB = 1.0 - HILUMSCAN*(YY-2.8*YY*Y);
|
|
|
|
#if defined(BLACK_OUT_BORDER)
|
|
colour.rgb*=float(tc.x > 0.0)*float(tc.y > 0.0); //why doesn't the driver do the right thing?
|
|
#endif
|
|
|
|
FragColor.rgb = colour.rgb*mix(scanLineWeight*mask, scanLineWeightB, dot(colour.rgb,vec3(maskFade)));
|
|
|
|
}
|
|
#endif
|