How can I port this shader to godot?

Godot Version

4.2 stable

Question

I know the very basics up until now, but this specific shader is a bit too hard for me.

I managed to get the main “Image” shader into something that sort of works, but after attaching it to a color rect, it does not seem to display the image behind it and I’m not sure why.

shader_type canvas_item;

#define gaussian(a,b)	exp2((a)*(a)*-(b))

uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;

uniform float SCREEN_CURVE_RADIUS = 5;
uniform float SCREEN_CORNER_RADIUS = 0.1;
uniform float BRIGHTNESS = 1.5;
uniform float PIXEL_SHARPNESS = 2.0;
uniform float LINE_SHARPNESS = 6.0;
uniform float MASK_STRENGTH = 0.15;

uniform bool CURVE_SCREEN = true;
uniform bool SCANLINES = true;
uniform bool SHADOW_MASK = true;
uniform bool LIGHT_EFFECTS = true;

uniform float TILES = 2.0;
const vec2 IRES = vec2(16, 9) * 5.0;

vec3 ACESFilm(vec3 x) {
    return clamp((x*(2.51*x + 0.03)) / (x*(2.43*x + 0.59) + 0.14), 0.0, 1.0);
}

vec2 curveScreen(vec2 uv) {
    float r = 3.14159265 * 0.5 / SCREEN_CURVE_RADIUS;
    float d = 1.0-cos(uv.x*r)*cos(uv.y*r);		// distance to screen
    float s = cos(r);							// scale factor to re-fit window
    return uv / (1.0-d) * s;
}

float discardCorners(vec2 pos) {
    pos = abs(pos);
    pos.x = pos.x*1.333-0.333;											// 4:3 aspect ratio correction
    if (min(pos.x, pos.y) < 1.0-SCREEN_CORNER_RADIUS) return 1.0;		// not near corner -- break early
    float d = distance(pos, vec2(1.0 - SCREEN_CORNER_RADIUS));
    return float(d < SCREEN_CORNER_RADIUS);
}

vec3 getSample(vec2 pos, vec2 off) {
	//get nearest emulated sample
	vec2 ir = IRES * TILES;
    pos = floor(pos*ir) + vec2(0.5) + off;
	vec3 col = vec3(0.0);
	if ( pos.x>=0.0 && pos.x<=ir.x*2.0 && pos.y>=0.0 && pos.y<=ir.y ) {
        col = texelFetch(SCREEN_TEXTURE, ivec2(pos), 0).rgb;
        col = pow( ( (col + 0.055) / 1.055), vec3(2.4) );		// SRGB => linear
	}
	return col;
}

vec3 getScanline( vec2 pos, float off ) {
	// 3-tap gaussian filter to get colour at arbitrary point along scanline
    vec2 ir = IRES * TILES;
    float d = 0.5-fract(pos.x*ir.x);
	vec3 ca = getSample( pos, vec2(-1.0, off ) );
	vec3 cb = getSample( pos, vec2( 0.0, off ) );
	vec3 cc = getSample( pos, vec2( 1.0, off ) );
	float wa = gaussian( d-1.0, PIXEL_SHARPNESS );
	float wb = gaussian( d,     PIXEL_SHARPNESS );
	float wc = gaussian( d+1.0, PIXEL_SHARPNESS );
	return ( ca*wa + cb*wb + cc*wc ) / ( wa+wb+wc);
}

vec3 getScreenColour(vec2 pos) {
	//Get influence of 3 nearest scanlines
    vec2 ir = IRES * TILES;
    float d = 0.5-fract(pos.y*ir.y);
	vec3 ca = getScanline( pos,-1.0 );
	vec3 cb = getScanline( pos, 0.0 );
	vec3 cc = getScanline( pos, 1.0 );
    float wa = gaussian( d-1.0, LINE_SHARPNESS );
	float wb = gaussian( d,     LINE_SHARPNESS );
	float wc = gaussian( d+1.0, LINE_SHARPNESS );
    return ( ca*wa + cb*wb + cc*wc );
}

vec3 SlotMask_PixelPerfect(vec2 pos, float resolution_y) {
    //pos /= 1.0 + floor( resolution_y / 1440.0 );
    pos /= 1.0 + floor(resolution_y / (IRES.y*15.0));
    float glow = 0.5;
    float f = mod(pos.x,3.0);
	vec3 col = vec3(float(f <= 1.0), float(f > 1.0 && f <= 2.0), float(f > 2.0));
    col += vec3(float(f<1.5 || f>=2.5), float(f>0.5 && f<=2.5), float(f>1.5 || f<=0.5)) * glow;
    col *= (mod(pos.y+(fract(pos.x/6.0)>0.5?1.5:0.0),3.0)<1.0) ? glow : 1.0;
    col /= 1.0+glow;
    return col;
}

void fragment(){
	vec2 uv =  FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy;
	vec2 pos = SCREEN_UV;
	pos = pos*2.0 - 1.0;
    pos.x *= (1.0 / SCREEN_PIXEL_SIZE).x/(1.0 / SCREEN_PIXEL_SIZE).y*(IRES.y/IRES.x);
	
	if (CURVE_SCREEN) pos = curveScreen(pos);
	
	if(max( abs(pos.x), abs(pos.y) )<1.0) {							// skip everything if we're beyond the screen edge
    	
        vec3 col = vec3(1.0);
        
        if (CURVE_SCREEN) col *= discardCorners(pos);
        
        if (LIGHT_EFFECTS) col *= 1.0 - sqrt(length(pos)*0.25);						// vignette
        
        pos = pos*0.5 + 0.5;

       if (SCANLINES) col *= getScreenColour( pos );
       else col *= getSample( pos, vec2(0.0) );
        
       if (SHADOW_MASK) {
        vec3 shadowmask = SlotMask_PixelPerfect(FRAGCOORD.xy, (1.0 / SCREEN_PIXEL_SIZE).y);
        col *= mix( vec3(1.0-MASK_STRENGTH), vec3(1.0+MASK_STRENGTH), shadowmask);
        }
        
        if (LIGHT_EFFECTS) {
			col *= BRIGHTNESS;
	    	col = ACESFilm(col);
        }
    
    	col = pow(col, vec3(1.0/2.4)) * 1.055 - 0.055;			// linear => SRGB
    
    	COLOR = vec4(col, 1.0);
    }
}

And when it comes to “Buffer B” I can’t get it to a working state because I’m not sure if this is supposed to be a spatial shader or not. As well as how this is supposed to be in godot!

const vec3 palette_a[5] = vec3[5](OUTLINE,
                                  vec3( 0.6, 0.0, 0.3 ),
                                  vec3( 1.0, 0.2, 0.1 ),
                                  vec3( 1.0, 0.7, 0.2 ),
                                  HIGHLIGHT );

the color rect with the shader:

the background:

it does seems to show something, at very least. is it blank for you there? or all black?

I had started to suspect that it is my computer’s fault :confused:
I can see the scanlines, but not the penguins. It’s just gray for me!

i think the issues is the shown screen on the colorrect shader actually a portion on top left. it shows zoomed on the top left, ignoring anything below or far right


so i tried tweaking the IRES value to:
const vec2 IRES = vec2(16, 9) *50.0;

The original shadertoy shader though, is using IRES for the resolution of the fake CRT, so changing it leaves the background in the same position but changes the resolution. In this godot port, changing IRES, moves the image!