Did you test with ntsc-cgwg-tm and append fake-crt-geom-potato? I changed the code above a bit too.
ntsc-cgwg-tm runs at 10-15fps. The updated blur looks noop when applied, and if I add fake-crt-geom-potato on top it (the blur) just looks like one pixel ghosting. The best blur so far was the first version, but it didnât pair well with scanlines on top.
Some shots of my Amiga when I connect via TV modulator and composite. Some notes: image flickers only when chroma or Luma change suddenly on edges on a big margin, eg a gray and then a saturated green, or black and then a bright pixel follows. Colors appear a bit washed out, there is a minimal color bleed. All dithered pixels appear as an extra solid color.
This was invented on a very old PC, slow by todayâs standards and it ran fine back in the day. No GPU acceleration required and it does the job just as well as any other solution.
If youâre interested and up to the task, what would help a lot is if someone could break out the other Blargg Filters for different systems like NES or Genesis and expose the controls as has been done with the Blargg NTSC SNES Filter.
The problem is not much a performance thing, but that whatever scanlines you add on top breaks the underlying blur as depicted in the above images, Blargg included. Iâm not to knowledgeable in the inner workings of the shader pipeline but something is going on that breaks it (edit: on Android).
Iâve been using Blargg Filters in conjunction with Simple Scanline Filters for over a decade and more recently with Shaders and this is not what Iâve experienced.
It doesnât break the blending at all. On the contrary I use it to add blending and transparency to any of my existing shader presets, including the sharpest ones. Scanline emulation is not affected. Iâll post some pics to demonstrate.
Yep, sorry, just edited to add âon Androidâ. DariusG posted a screenshot demonstrating that this is not an issue on PC.
Try this and tell me, itâs more of a hack.
#version 110
#define iTime float(FrameCount)
#define pi 3.141592
#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;
// compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
void main()
{
gl_Position = MVPMatrix * VertexCoord;
TEX0.xy = TexCoord.xy;
}
#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;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float GLITCH;
#else
#define GLITCH 0.1
#endif
#define bend 0.2
#define blurx 0.75
#define blury 0.25
void main()
{
//simple filter, adjustable
vec2 OGL2pos = vTexCoord*SourceSize.xy;
vec2 cent = floor(OGL2pos)+0.5;
vec2 coords = cent*SourceSize.zw;
vec2 pos;
pos.x = mix(coords.x, vTexCoord.x, blurx);
pos.y = mix(coords.y, vTexCoord.y, blury);
//////
vec3 res = texture2D(Source,pos).rgb;
vec3 leftp = texture2D(Source,pos-vec2(SourceSize.z,0.0)).rgb;
vec3 rightp = texture2D(Source,pos+vec2(SourceSize.z,0.0)).rgb;
vec3 dither = (leftp+rightp)/2.0;
res =dither*0.5+res*0.5;
vec3 check = texture2D(Source, pos + vec2(SourceSize.z,0.0)).rgb;
float lum = dot(vec3(0.3,0.6,0.1),res);
float lum2 = dot(vec3(0.3,0.6,0.1),check);
if (abs(lum-lum2) > 0.5) res +=0.2*abs(sin(iTime));
FragColor = vec4(res,1.0);
}
#endif
Thanks, at this point it probably would be easier to merge the blur and scanlines into the same pass? Iâm trying to merge zfast_crt_composite and my zfast_crt_geo. You can give it a try as scanlines is not my field really.
EDIT: @DariusG this is how it looks now, a bit pixelated and blur/ghosting flickers a bit horizontally. Here using zfast_crt_geo but also on fake-crt-geom-potato
Did you use linear on both pass? Here is a comparison with and without (and flickering where ever there is a big luma difference, i wanted to check myself how it works in real conditions on Amiga )
Here is another job i did, PIxel-Art-AA. Not bad Makes that Amstrad games look a bit higher res lol
Not bad on normal resolution too if you combine with something like crt-geom.
#version 110/*
Pixel art AA shader by DariusG 2023
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.
*/
#define iTime float(FrameCount)
#define pi 3.141592
#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;
// compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
void main()
{
gl_Position = MVPMatrix * VertexCoord;
TEX0.xy = TexCoord.xy;
}
#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;
// compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define OutSize vec4(OutputSize, 1.0 / OutputSize)
#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float GLITCH;
#else
#define GLITCH 0.1
#endif
/*
A B C
D E F
G H I
*/
void main()
{
vec2 pos = vTexCoord;
float dx = SourceSize.z;
float dy = SourceSize.w;
vec3 A = COMPAT_TEXTURE(Source,vTexCoord + vec2(dx,-dy)).rgb;
vec3 B = COMPAT_TEXTURE(Source,vTexCoord + vec2(0.0,-dy)).rgb;
vec3 C = COMPAT_TEXTURE(Source,vTexCoord + vec2(-dx,-dy)).rgb;
vec3 D = COMPAT_TEXTURE(Source,vTexCoord + vec2(dx,0.0)).rgb;
vec3 E = COMPAT_TEXTURE(Source,vTexCoord).rgb;
vec3 F = COMPAT_TEXTURE(Source,vTexCoord + vec2(-dx,0.0)).rgb;
vec3 G = COMPAT_TEXTURE(Source,vTexCoord + vec2(dx,dy)).rgb;
vec3 H = COMPAT_TEXTURE(Source,vTexCoord + vec2(0.0,dy)).rgb;
vec3 I = COMPAT_TEXTURE(Source,vTexCoord + vec2(-dx,dy)).rgb;
#define lumweight vec3(0.3,0.6,0.1)
#define lum(c) dot(lumweight,c)
//pattern type 1
float L = (D == B && B == C && E != D) ? 1.0 : 0.0;
float R = (A == B && A == F && E != F) ? 1.0 : 0.0;
float DL = (D == H && D == I && E != D) ? 1.0 : 0.0;
float DR = (F == G && F == H && E != F) ? 1.0 : 0.0;
E = (L == 1.0 && lum(E)<lum(D) || DL == 1.0 && lum(E)<lum(D)) ? (E+D)/2.0 : E;
E = (R == 1.0 && lum(E)<lum(F) || DR == 1.0 && lum(E)<lum(F)) ? (E+F)/2.0 : E;
//pattern type 2
float GL = (E == H && E == F && E != D) ? 1.0 : 0.0;
float GR = (E == D && E == H && E != F) ? 1.0 : 0.0;
float GDL = (E == B && E == F && E != D) ? 1.0 : 0.0;
float GDR = (E == D && E == B && E != F) ? 1.0 : 0.0;
E = (GL == 1.0 && lum(E)>lum(D) || GDL == 1.0 && lum(E)>lum(D)) ? (E+D)/2.0 : E;
E = (GR == 1.0 && lum(E)>lum(F) || GDR == 1.0 && lum(E)>lum(F)) ? (E+F)/2.0 : E;
//pattern type 3
float SL = (B == D && B == G && E != D) ? 1.0 : 0.0;
float SR = (B == F && B == I && E != F) ? 1.0 : 0.0;
float SDL = (H == D && H == A && E != D) ? 1.0 : 0.0;
float SDR = (H == F && H == C && E != F) ? 1.0 : 0.0;
E = (SL == 1.0 && lum(E)<lum(D) || SDL == 1.0 && lum(E)<lum(D)) ? (E+D)/2.0 : E;
E = (SR == 1.0 && lum(E)<lum(F) || SDR == 1.0 && lum(E)<lum(F)) ? (E+F)/2.0 : E;
FragColor = vec4(E,1.0);
}
#endif
Itâs fun to experiment and discover new things.
Made a PR to add âpixel_art_AAâ and âwide_pixels_AAâ on interpolation folder and a preset âC64_res_enchanceâ. Copy from github:
âThree shaders that will enchance C64 resolution. Works as following: First cut wide pixel in half with âwide_pixels_AAâ, one part works as AA, then âpixel art AAâ where it will detect certain patterns like eq a pixel âstairwayâ and AA it in certain points. Then apply a pass of âcrt-sinesâ to get that CRT look. âPixel art AAâ works as standalone too in normal resolution games and âwide_pixels_AAâ can work as 1st pass on any CRT shader on that C64-Amstrad games.â
Moved shaders to a folder readable without root and checking why that pink tint. This way I corrected all GLES errors on current PR.
Directly loading, correcting and checking the results
Check the difference running pass 2 of ântsc-256â on Linux and Android. Seems Y has moved to IQ on Android? Pass 1 is identical.
Seems this fixes it, donât ask me why⌠thatâs GLES lol. Instead of yiq2rgb, rgb2yiq and multiply by 2.0 or 2.5 (better)
Did an attempt to convert blargg, what a maze jumping here and there in the code in arrays, pointers etc etc. Probably would be super slow if someone manages to convert that
Donât forget CPU is way way faster than GPU at calculations. Probably a Pentium 4 would be way faster calculating floats than an average/low end GPU today. At least thatâs what I think. Example you load ntsc shader at a low end GPU cellphone and it crawls⌠Then you load a CPU video filter, blargg that is WAY more complex and runs well.
That blargg filter is probably around or more than 2.000 lines of code ( there are #includes there that expand code a zillion of lines)
Thatâs some real dedication right there man! I think this something that you just have to have the passion and motivation for and if itâs one person who has that, it might be you.