So what is this all about? I dislike all the built in shaders for Retroarch (which is what RetroPie is based off of) and I especially dislike them on the Picade as I’m so close to the screen and the screen is relatively low res. Essentially they all blur the image and/or introduce moire artifacts. Dont get me wrong some of the shaders on libretro amaze me in their complexity but I’m just not a fan.
I mean why try to emulate a CRT screen when you’re never going to get there, at least not on a low res screen (1080p and below).
There are some features I like from CRT’s, primarily the scanlines.
I have a Sony PVM, with a fully decked out MiSTer FPGA and original controllers and I love it but there are problems with such a setup such as vertical arcades, CRT’s weigh a ton and it’s a lot of work maintaining them - I’m still working up to recapping mine. Its just nice to have a fallback and its nice to have a Picade on your lap.
The thing is as you go up the PVM models and then onto the BVMs, Sony was trying to get as sharp an image as possible (ie as sharp scanlines as possible) and as straighter screen geometry possible. These are both what a Picade’s LCD screen excels at. What the Picade LCD does not excel at are resolutions that are not native and to a lesser extent brightness. We’ll come back to brightness later.
What do I want then? Something dead simple.
I just want to use an integer scaling and either ‘overscan’ or ‘underscan’ the image (to use CRT terminology) - whichever is subjectively preferable. I want this with simple whole pixel scanlines that are surrounded by black pixels.
So I created this shader that produces the above for all the different resolutions that the various cores and games use.
integer_scanlines.glsl:
#pragma parameter SCANLINE_WIDTH "Scanline Width" 1.0 0.0 8.0 1.0
#pragma parameter SCREEN_WIDTH "Screen Width" 1024.0 0.0 7680.0 1.0
#pragma parameter SCREEN_HEIGHT "Screen Height" 768.0 0.0 4320.0 1.0
#ifdef GL_ES
#define COMPAT_PRECISION mediump
precision mediump float;
#else
#define COMPAT_PRECISION
#endif
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float SCANLINE_WIDTH;
uniform COMPAT_PRECISION float SCREEN_WIDTH;
uniform COMPAT_PRECISION float SCREEN_HEIGHT;
#else
#define SCANLINE_WIDTH 1.0
#define SCREEN_WIDTH 1024.0
#define SCREEN_HEIGHT 768.0
#endif
// GLSL shader autogenerated by cg2glsl.py.
#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
COMPAT_VARYING float _frame_rotation;
COMPAT_VARYING vec4 _color1;
struct output_dummy {
vec4 _color1;
};
struct input_dummy {
vec2 _video_size;
vec2 _texture_size;
vec2 _output_dummy_size;
float _frame_count;
float _frame_direction;
float _frame_rotation;
};
vec4 _oPosition1;
vec4 _r0005;
COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;
COMPAT_VARYING float Scale;
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()
{
vec4 _oColor;
vec2 _otexCoord;
_r0005 = VertexCoord.x*MVPMatrix[0];
_r0005 = _r0005 + VertexCoord.y*MVPMatrix[1];
_r0005 = _r0005 + VertexCoord.z*MVPMatrix[2];
_r0005 = _r0005 + VertexCoord.w*MVPMatrix[3];
_oPosition1 = _r0005;
_oColor = COLOR;
_otexCoord = TexCoord.xy;
gl_Position = _r0005;
COL0 = COLOR;
TEX0.xy = TexCoord.xy;
vec2 ScreenSize = max(OutputSize, vec2(SCREEN_WIDTH, SCREEN_HEIGHT));
if((InputSize.x > ScreenSize.x) || (InputSize.y > ScreenSize.y))
{
Scale = 1.0;
}
else
{
float ScaleFactor = 2.0;
while(((InputSize.x * ScaleFactor) <= ScreenSize.x) && ((InputSize.y * ScaleFactor) <= ScreenSize.y))
{
ScaleFactor += 1.0;
}
Scale = ScaleFactor - 1.0;
}
}
#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
COMPAT_VARYING float _frame_rotation;
COMPAT_VARYING vec4 _color;
struct output_dummy {
vec4 _color;
};
struct input_dummy {
vec2 _video_size;
vec2 _texture_size;
vec2 _output_dummy_size;
float _frame_count;
float _frame_direction;
float _frame_rotation;
};
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;
COMPAT_VARYING float Scale;
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;
float mod_integer(float a, float b)
{
float m = a - floor((a + 0.5) / b) * b;
return floor(m + 0.5);
}
void main()
{
output_dummy _OUT;
vec2 InPixels = (TEX0.xy * TextureSize) * vec2(Scale);
if(mod_integer(floor(InPixels.y), Scale) < SCANLINE_WIDTH)
{
_OUT._color = COMPAT_TEXTURE(Texture, TEX0.xy);
}
else
{
_OUT._color = vec4(0.0,0.0,0.0,1.0);
}
FragColor = _OUT._color;
return;
}
#endif
I’ll explain how to use this in a reply but it must be used with integer scaling and ideally in a darkened room to make up for the lack of brightness.
Here’s a close up picture of what it looks like in Street Fighter II with 3x integer scale in both directions:
(Moire effect above is due to me taking a picture with my phone of the Picade screen and then halving the images resolution in this post)
2 posts - 1 participant
Read full topic