Basic Vector Sprite Upscaling

This is basically a simpler version of the HQ4X shader I posted: NekotoArts HQ4X Shader

 

Instead of using HQX it just uses a vector between similar pixels and draws a line.

This can be cleaner than the HQ4X Shader and less muddy in some cases.

 

Still though, I’m not sure how useful this is.

Shader code
shader_type canvas_item;

/*
Made with a whole lot of nightmare fuel.
Logic's Under Pressure and Bobby Tarantino II Albums was in rotation during
the making process.
Hope you enjoy!

You're auto-friended to me if you're a Logic fan
RattPack all day!
*/

//upscaling multiplier amount
const float SCALE = 10.0;

//image mipmap level, for base upscaling
const int ML = 0;

//equality threshold of 2 colors before forming lines
uniform float THRESHOLD = 0.1;

//anti aliasing scaling, smaller value make lines more blurry
uniform float AA_SCALE = 2.0;


//draw diagonal line connecting 2 pixels if within threshold
vec4 diag(vec4 sum, vec2 uv, vec2 p1, vec2 p2, sampler2D iChannel0, float LINE_THICKNESS) {
    vec4 v1 = texelFetch(iChannel0,ivec2(uv+vec2(p1.x,p1.y)),ML),
        v2 = texelFetch(iChannel0,ivec2(uv+vec2(p2.x,p2.y)),ML);
    if (length(v1-v2) < THRESHOLD) {
    	vec2 dir = p2-p1,
            lp = uv-(floor(uv+p1)+.5);
    	dir = normalize(vec2(dir.y,-dir.x));
        float l = clamp((LINE_THICKNESS-dot(lp,dir))*AA_SCALE,0.,1.);
        sum = mix(sum,v1,l);
    }
    return sum;
}

void fragment()
{
	float LINE_THICKNESS = 0.4;
	vec2 ip = UV;
	ip = UV * (1.0 / TEXTURE_PIXEL_SIZE);
	
		//start with nearest pixel as 'background'
		vec4 s = texelFetch(TEXTURE,ivec2(ip),ML);
		
		//draw anti aliased diagonal lines of surrounding pixels as 'foreground'
		s = diag(s,ip,vec2(-1,0),vec2(0,1), TEXTURE, LINE_THICKNESS);
		s = diag(s,ip,vec2(0,1),vec2(1,0), TEXTURE, LINE_THICKNESS);
		s = diag(s,ip,vec2(1,0),vec2(0,-1), TEXTURE, LINE_THICKNESS);
		s = diag(s,ip,vec2(0,-1),vec2(-1,0), TEXTURE, LINE_THICKNESS);
		
		COLOR = s;
}
Tags
2d, sprites, upscale, upscaling
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from NekotoArts

Spatial Ice Shader

HQ4X Shader (like in Emulators)

Wind Waker Water – NO Textures needed!

Related shaders

AMD FSR Upscaling (EASU)

2D Cel / Toon Shader v2 (Basic)

Burn Sprite

Subscribe
Notify of
guest

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
YodaMax
YodaMax
2 years ago

Would be awesome to make this a screen filter (upscale the whole screen and not just the sprite)

JAs
JAs
1 year ago

I second this request please! How can we make this into a screen based texture?

Firerabbit
1 year ago

The number of texture samples can be reduced from 9 to 5 by sampling outside of the “diag” function.

ShaderCode:

shader_type canvas_item;

/*
Made with a whole lot of nightmare fuel.
Logic's Under Pressure and Bobby Tarantino II Albums was in rotation during
the making process.
Hope you enjoy!

You're auto-friended to me if you're a Logic fan
RattPack all day!
*/

//upscaling multiplier amount
const float SCALE = 10.0;

//image mipmap level, for base upscaling
const int ML = 0;

//equality threshold of 2 colors before forming lines
uniform float THRESHOLD = 0.001;

//anti aliasing scaling, smaller value make lines more blurry
uniform float AA_SCALE = 11.0;


//draw diagonal line connecting 2 pixels if within threshold
vec4 diag(vec4 sum, vec2 uv, vec2 p1, vec2 p2, vec4 c1, vec4 c2, float LINE_THICKNESS) {
    if (length(c1 - c2) < THRESHOLD) {
        vec2 dir = p2-p1,
            lp = uv-(floor(uv+p1)+.5);
        dir = normalize(vec2(dir.y,-dir.x));
        float l = clamp((LINE_THICKNESS-dot(lp,dir))*AA_SCALE,0.,1.);
        sum = mix(sum,c1,l);
    }
    return sum;
}

void fragment()
{
    float LINE_THICKNESS = 0.4;
    vec2 ip = UV;
    ip = UV * (1.0 / TEXTURE_PIXEL_SIZE);
    
        vec4 texCenter = texelFetch(TEXTURE,ivec2(ip),ML);
        vec4 texLeft = texelFetch(TEXTURE,ivec2(ip+vec2(-1,0)),ML);
        vec4 texUp = texelFetch(TEXTURE,ivec2(ip+vec2(0,1)),ML);
        vec4 texRight = texelFetch(TEXTURE,ivec2(ip+vec2(1,0)),ML);
        vec4 texDown = texelFetch(TEXTURE,ivec2(ip+vec2(0,-1)),ML);
        
        //start with nearest pixel as 'background'
        vec4 s = texCenter;
        
        //draw anti aliased diagonal lines of surrounding pixels as 'foreground'
        s = diag(s,ip,vec2(-1,0),vec2(0,1), texLeft, texUp, LINE_THICKNESS);
        s = diag(s,ip,vec2(0,1),vec2(1,0), texUp, texRight, LINE_THICKNESS);
        s = diag(s,ip,vec2(1,0),vec2(0,-1), texRight, texDown, LINE_THICKNESS);
        s = diag(s,ip,vec2(0,-1),vec2(-1,0), texDown, texLeft, LINE_THICKNESS);
        
        COLOR = s;
}
cheesycoke
1 year ago

I really love the look this creates! But does anyone know how to get rid of those weird diamond artifacts like the ones you see in the example dude’s sandals?