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