It’s quite known that crt shaders with scanlines have it’s issues on common (1080p) displays if the scaling factor is not integer. I tried to mitigate the problem with supersampling, but the downsampling algorithm in general use tends to give results that don’t use all the benefits of higher resolution shading. At least in my case.

My first aim was to create a vertical supersampling downsampler which uses very high viewport multipliers, like 8k. Surprisingly my testings revealed, that it’s much more convenient to use internal integer scaling (scale = source) and the results were very comparable. It’s still recommended to supersample, like 1.5x, but the speed loss is much lower.

The shader used for this is quite simple, it calculates an average subpixel sum of the “perfect pixel” range. Should work well enough. The drawbacks i found was the scanlines could lose some edginess and it can’t make hi-res contents shine.

I’ll post the shader and a test preset here, so please test it a bit.

**scanline_downsampler.glsl**: - in test preset, goes to …crt/shaders/guest

```
/*
Vertical Downsampling Shader (for even scanlines)
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.
*/
#pragma parameter steps "Interpolation Steps" 4.0 1.0 12.0 1.0
#pragma parameter srange "Sampling Range" 0.75 0.05 2.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;
#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float steps;
uniform COMPAT_PRECISION float srange;
#else
#define steps 4.00
#define srange 0.75
#endif
void main()
{
float ratio = srange*InputSize.y/OutputSize.y;
vec3 color = vec3(0.0);
float dy = 1.0/TextureSize.y;
float wsum = 0.0;
float dif = ratio/steps;
for (float i = -ratio; i <= ratio; i = i + dif)
{
color += COMPAT_TEXTURE(Texture, TEX0.xy + vec2(0.0, dy * i)).rgb;
wsum = wsum + 1.0;
}
color = color / wsum;
FragColor = vec4(color,1.0);
}
#endif
```

Test preset:

```
shaders = 3
shader0 = shaders/guest/d65-d50.glsl
filter_linear0 = false
scale_type0 = source
scale0 = 1.0
shader1 = shaders/guest/crt-guest-sm.glsl
scale_type_x1 = viewport
scale_type_y1 = source
scale_x1 = 1.0
scale_y1 = 8.0
shader2 = shaders/guest/scanline_downsampler.glsl
filter_linear2 = true
scale_type_x2 = viewport
scale_type_y2 = viewport
scale_x2 = 1.0
scale_y2 = 1.0
```

You can play around with the scaling factor, higher value tends to bring better results. If someone want’s to improve the algorithm and/or create a better one, please be my guest.

Edit: small bugfix