A new little shader i did (glsl)

Wow! That is amazing man, keep it up! To me it’s worth it to have most if not all of Blargg’s implementation as a shader if even for preservation purposes.

I’m tagging @Nesguy because he also was very interested in a new NTSC shader so I’m sure he’ll be very appreciative of your efforts thus far as well as going forward.

Who knows, maybe there can be some dialog, input and further understanding of how these things work to be gained from his knowledge and expertise on the subject matter?

2 Likes

Here is a single pass ntsc like blastem with comb filter and all, runs 800 fps on hd630 on 1x scale.

#version 110

#pragma parameter comb_filter "Comb Filter Strength" 0.6 0.0 1.0 0.05
#pragma parameter lpass "Low Pass" 0.25 0.0 1.0 0.01

#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)

#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float WHATEVER;
#else
#define WHATEVER 0.0
#endif

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    TEX0.xy = TexCoord.xy*1.0001;
}

#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 vTexCoord TEX0.xy
#define Source Texture
#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 comb_filter;
uniform COMPAT_PRECISION float lpass;

#else
#define comb_filter 0.0
#define lpass 0.0

#endif

#define PI   3.14159265358979323846

const mat3 RGBYUV = mat3(0.299, 0.587, 0.114,
                        -0.299, -0.587, 0.886, 
                         0.701, -0.587, -0.114);

const mat3 YUV2RGB = mat3(1.0, 0.0, 1.13983,
                          1.0, -0.39465, -0.58060,
                          1.0, 2.03211, 0.0);
vec2 dx,dy;
float compo0 ( float p, vec3 phase)
{
vec3 yiq = COMPAT_TEXTURE(Source,vTexCoord+p*dx).rgb*RGBYUV;
return dot(vec3(1.0),yiq*phase);
}

float compo1 ( float p, vec3 phase)
{
vec3 yiq = COMPAT_TEXTURE(Source,vTexCoord+p*dx-dy).rgb*RGBYUV;
return dot(vec3(1.0),yiq*phase);
}

void main() {
dx = vec2(SourceSize.z*0.5,0.0);
dy = vec2(0.0,SourceSize.w*0.25);
vec3 final = vec3(0.0);
float sum = 0.0;

for (int n=-3; n<4; n++)
{
float p = float(n);   
float w = exp(-lpass*p*p);
float carrier    =  (vTexCoord.x * SourceSize.x / 170.667 * InputSize.x + p)*PI*0.5;
float carrier_up =  (vTexCoord.x * SourceSize.x / 170.667 * InputSize.x + p)*PI*0.5 + PI;
vec3 phase    = vec3(1.0,cos(carrier)   ,sin(carrier));
vec3 phase_up = vec3(1.0,cos(carrier_up),sin(carrier_up));

float line_cur = compo0(p,phase);
float line_up  = compo1(p,phase_up);

vec3 rgb = vec3(0.0);
// luma
rgb.r = (line_up+line_cur)/2.0;
// chroma
rgb.gb = vec2(line_cur-(line_up+line_cur)/2.0*comb_filter);
final += w*rgb*phase;
sum += w;
}
final /= sum;
FragColor.rgb = final*YUV2RGB;
}
#endif
4 Likes

Wow! Coming along nicely! I see that you’re trying and starting to match the real CRT shot even closer!

1 Like

That early megadrive very poor encoder is causing the rainbow bands (NTSC only). It should look like crt-consumer (with convergence lifted) otherwise with a good encoder with minimal chroma leaking.

https://www.reddit.com/r/SEGAGENESIS/comments/u8fnsg/quick_comparison_of_composite_video_on_genesis_vs/

2 Likes

crt-geom-mini GLSL, after some changes i did to match my CRT better on filtering at least, as it has a slot mask (20 inches)

3 Likes

So I got a pi zero 2w, cool device-2 small fingers size lol, pretty fast for 20 euros, running zxbaremulator on composite hack here (poor soldering by me on the other side heh) .

All dither results in another color but not any visible artifacts otherwise (that’s running on 240p pal setting sdtv_mode=18, ntsc has a lot more rolling on retropie) . Pretty much what you will see if you increase “convergence” on crt-consumer, zfast-composite, crt-sines etc. Of course I also have retropie installed on another SD card. Probably the video mode is not exactly what a real zx spectrum would output.

4 Likes

@DariusG, I noticed something when using crt-pocket. These artifacts appear when ntsc colors are enabled:

Looks like it’s clipping some color values.

Just passing by to let you know about it.

Anyway, congrats for all your shaders!

4 Likes

Probably needs a clamp at some point, will check that.

3 Likes

Unrelated to this, but I just wanted to lyk that since we last talked, one of your updates to crt-consumer.glsl has fixed the uneven glow I was experiencing, now both axis are working as intended.

2 Likes

I like that “shrink shader” project - here is a mod of kaizer i uploaded to “window” folder glsl, using quilez at Y axis, modded the filter parameters and precalculated 5 steps/texture reads (4 as step 1 is multplied to 0 so erased) using desmos. Use a linearize pass before it, anything like “texture = texture * texture” and then this as 2nd pass. Here we are trying to mimic crt-geom and the result of speed is near fake-crt-geom-potato, stresses the hd630 laptop gpu 34% while crt-geom is 90% and fake-crt-geom-potato is 32%, crt-geom-mini is 46%.

#version 110

/*
   Kaizer-window customizable by DariusG 2024.
   
   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#pragma parameter scanlines "Scanlines Str." 0.5 0.0 0.5 0.05

#define pi 3.1415926
#define tau 6.2831852
#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;
COMPAT_VARYING vec2 scale;
COMPAT_VARYING float maskpos;

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)

#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float WHATEVER;
#else
#define WHATEVER 0.0
#endif

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    TEX0.xy = TexCoord.xy*1.0001;
    scale = SourceSize.xy/InputSize.xy;
    maskpos = TEX0.x*OutputSize.x*scale.x*pi;
}

#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;
COMPAT_VARYING vec2 scale;
COMPAT_VARYING float maskpos;

// compatibility #defines
#define vTexCoord TEX0.xy
#define Source Texture
#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 scanlines;

#else
#define scanlines 0.5
#endif

// Configuration.

float kaizer_x (float p)
{
    // Compute sinc filter.
    float k = sin(1.3* ((p - 1.0) / 2.0));
    return k;
}

vec2 Warp(vec2 coord)
{
        vec2 CURVATURE_DISTORTION = vec2(0.12, 0.18);
        // Barrel distortion shrinks the display area a bit, this will allow us to counteract that.
        vec2 barrelScale = vec2(0.9724,0.9586);
        coord -= vec2(0.5);
        float rsq = coord.x*coord.x + coord.y*coord.y;
        coord += coord * (CURVATURE_DISTORTION * rsq);
        coord *= barrelScale;
        
        coord += vec2(0.5);
        
        return coord;
}

void main()
{
vec3 res = vec3(0.0);
vec2 dx = vec2(SourceSize.z*0.25,0.0); //sharpness
vec2 xy = vTexCoord;
xy -= dx*3.0;
vec2 pos = Warp(vTexCoord*scale);
vec2 corn = min(pos, 1.0-pos);    // This is used to mask the rounded
     corn.x = 0.0001/corn.x;      // corners later on
pos /= scale;

vec2 near = floor(pos*SourceSize.xy)+0.5;
vec2 f = pos*SourceSize.xy - near;

xy.y = (near.y + 4.0*f.y*f.y*f.y)*SourceSize.w;    

//kaizer precalculated
res += COMPAT_TEXTURE(Source,xy).rgb*-0.6052;
res += COMPAT_TEXTURE(Source,xy+2.0*dx).rgb*0.6052;
res += COMPAT_TEXTURE(Source,xy+3.0*dx).rgb*0.96356;
res += COMPAT_TEXTURE(Source,xy+4.0*dx).rgb*0.92896;
    
res /= 1.8925;

    float a = dot(vec3(0.25),res);
    float s = mix(scanlines,scanlines*0.5,a);
    res *= s*sin((pos.y*SourceSize.y-0.25)*tau)+1.0-s;
    
    res *= 0.2*sin(maskpos)+0.8;
    res *= mix(1.45,1.05,a);
    if (corn.y <= corn.x || corn.x < 0.0001 )res = vec3(0.0);
    FragColor.rgb = sqrt(res);
}
#endif

crt-geom

kaizer mod, ofc i can mod the filter to look more blurry like geom but preferred a slightly sharper image.

4 Likes

Nice. What are you using to profile gpu usage?

Btw, that screenshot shows well the lanczos ringing signature. One can see the black halos around the sword and fonts.

One thing that I use to compare crt shaders is reducing the response to almost impulse response by applying the shader on a white dot over a black background and zoom enough to see how the shader deform the white pixel. If you’re trying to mimic the visual from another shader, that’s an easy way to acomplish that.

2 Likes

I use “Intel gpu top” I think that’s the name now I am away from laptop. That’s a good way to compare, you can also use 240p convergence test that has white dots.

1 Like

240p tests are good for many things, but you can’t zoom enough to see a pixel half the size of your screen. I only get this using a tiny sprite.

You can use the 240p test suite’s ‘backlit zone test’ with a custom aspect ratio and integer scaling to crank it up to ~15x on x and y to get something like this:

3 Likes

I prefer using game screens preferably one that uses a gradient line from dark to light so i can also tweak the scanline beam dynamics, if i use a dot i can match the filter only but yes if one wants to match the filter it’s fine.

In general Lanczos is the closest to match a how a real crt dot is projected on a real crt but without the halo artifacts

1 Like

Yes, though it’s small. I prefer loading a tiny 15x15 png and zoom in to see how the filter works on both dimensions. I use others too, with neighbor pixels to see how they interact.

Believe me, with bigger sizes you can see micro differences between them. Not only that, micro artifacts that are almost invisible at normal sizes, can be seen and fixed at huge sizes.

1 Like

Yeah, just loading pics in the built-in image viewer is more direct and altogether easier. Plus, you can have a series of test images and cycle through them that way without needing to mess with 240p test suite’s menus (that is, while zoomed in super-close).

The only case where you’d really want to use 240p test suite would be for motion stuff.

2 Likes

Indeed, for motion and for looking at brightness and contrast tests, they are very useful.

2 Likes

One game I would use to check how my shader looks or compare to a real crt is Ultracore on Sega Genesis. Many gradients, small glowy pixels here and there. Great to compare gamma, colors too.

4 Likes

a small update with an image to compare as suggested, curvature code is a bit different etc

geom

kaizer

geom

kaizer

and the code

  #version 110

        /*
           Kaizer-window customizable by DariusG 2024.
           
           Permission is hereby granted, free of charge, to any person obtaining a copy
           of this software and associated documentation files (the "Software"), to deal
           in the Software without restriction, including without limitation the rights
           to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
           copies of the Software, and to permit persons to whom the Software is
           furnished to do so, subject to the following conditions:

           The above copyright notice and this permission notice shall be included in
           all copies or substantial portions of the Software.

           THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
           THE SOFTWARE.
        */

        #pragma parameter scanlines "Scanlines Str." 0.5 0.0 0.5 0.05

        #define pi 3.1415926
        #define tau 6.2831852
        #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;
        COMPAT_VARYING vec2 scale;
        COMPAT_VARYING float maskpos;

        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)

        #ifdef PARAMETER_UNIFORM
        uniform COMPAT_PRECISION float WHATEVER;
        #else
        #define WHATEVER 0.0
        #endif

        void main()
        {
            gl_Position = MVPMatrix * VertexCoord;
            TEX0.xy = TexCoord.xy*1.0001;
            scale = SourceSize.xy/InputSize.xy;
            maskpos = TEX0.x*OutputSize.x*scale.x*pi;
        }

        #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;
        COMPAT_VARYING vec2 scale;
        COMPAT_VARYING float maskpos;

        // compatibility #defines
        #define vTexCoord TEX0.xy
        #define Source Texture
        #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 scanlines;

        #else
        #define scanlines 0.5
        #endif

        // Configuration.

        float kaizer_x (float p)
        {
            // Compute sinc filter.
            float k = sin(1.3* ((p - 1.0) / 2.0));
            return k;
        }

        vec2 Warp(vec2 coord)
        {
                vec2 CURVATURE_DISTORTION = vec2(0.12, 0.25);
                // Barrel distortion shrinks the display area a bit, this will allow us to counteract that.
                vec2 barrelScale = vec2(0.985,0.945);
                coord -= vec2(0.5);
                float rsq = coord.x*coord.x + coord.y*coord.y;
                coord += coord * (CURVATURE_DISTORTION * rsq);
                coord *= barrelScale;
                if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
                        coord = vec2(-1.0);             // If out of bounds, return an invalid value.
                else
                {
                        coord += vec2(0.5);
                }

                return coord;
        }
        void main()
        {
        vec3 res = vec3(0.0);
        vec2 dx = vec2(SourceSize.z*0.35,0.0); //sharpness

        vec2 pos = Warp(vTexCoord*scale);
        vec2 corn = min(pos, 1.0-pos);    // This is used to mask the rounded
             corn.x = 0.0001/corn.x;      // corners later on
        pos /= scale;
        vec2 xy = pos;
        xy -= dx*3.0;
        vec2 near = floor(pos*SourceSize.xy)+0.5;
        vec2 f = pos*SourceSize.xy - near;

        xy.y = (near.y + 16.0*f.y*f.y*f.y*f.y*f.y)*SourceSize.w;    

        //kaizer precalculated
        res += COMPAT_TEXTURE(Source,xy).rgb*-0.6052;
        res += COMPAT_TEXTURE(Source,xy+2.0*dx).rgb*0.6052;
        res += COMPAT_TEXTURE(Source,xy+3.0*dx).rgb*0.96356;
        res += COMPAT_TEXTURE(Source,xy+4.0*dx).rgb*0.92896;
            
        res /= 1.8925;
            float a = dot(vec3(0.2),res);
            float s = mix(scanlines,scanlines*0.5,a);
            float scan = s*sin((pos.y*SourceSize.y-0.25)*tau)+1.0-s;
            float mask = 0.2*sin(maskpos)+0.8;
            res *= scan*mask;
            res *= mix(1.45,1.05,a);
            if (corn.y <= corn.x || corn.x < 0.0001 )res = vec3(0.0);
            FragColor.rgb = sqrt(res);
        }
        #endif
1 Like