ScaleNx - Artifact Removal and Algorithm Improvement


Ah! Nice! That preserves a lot more detail. Good stuff!


Looks awesome.

Edit: Just a remark, but I don’t know if this is possible to fix. xBR as well as ScaleFX have one issue with regard to Super Mario Bros and the SNES All stars version. The eye of mario is always conncted with his beard. Here an example:


not really. it’s an issue of the small resolution and low colorpalette. scaling filters don’t know what the meaning of a specific pixel pattern is. from the perspective of the shader it looks like pixels that must be connected due to the identical color.


I can’t tell which one is the improved image. I see ‘spikes’ in one of them and I see the 2nd image clears them up BUT they appear in other places. (Small improvements in the background harrison ford, the mini one, and in the upper indiana jones title, but regressions in the small text below)


it’s still WIP.


This is not a trivial task i think, it’s rather a major improvement. My thoughts about the scalefx were more about making it safer to use a high color tresholds because it really makes the fonts looks perfect. Just a thought.

In the meanwhile i made the “soft” version a bit better within the basic preset. This situation is very rare (level2 hi-res slopes) and one of the occasions where a level2 aa method shows up better. For the intermezzo…and a happy new year. :slight_smile:


I kinda sensed it a metric change can improve something. First i tried all sorts of metrics in the ScaleFX first pass, but the one it’s currently using is really good. The problem was somewhere else i guessed. I played around a bit with the code in the first pass and made some changes:

// corner strength
float4 str(float4 crn, float4 ort){
    //return (crn > THR) ? max(2*crn - (ort + ort.wxyz), 0) : 0;
    return (crn > THR) ? max(2*crn - (2.0/pow(2.0,SFX_CRN))*pow(pow(ort, float4(1.0/SFX_CRN))+pow(ort.wxyz,float4(1.0/SFX_CRN)),float4(SFX_CRN)), 0) : 0;    

The results:

Perhaps it could be tested some more…

Edit: i’ll include the test file. The 0.70 - 0.80 metric seems to fix some issues.


thanks for the input. I’m already sitting on a new version here though with a revamped strength function. here is the same screen with the current unreleased WIP-version:

I was wondering though what shader you used for the level2 AA preset you mentioned earlier.


That looks great overall, but Mario’s eyes go all goofy in the shaded shot.


this will always be a problem with these kind of shaders. unless you go hardcore with something like neural networks that can detect objects and structures (which won’t happen in realtime), these pixelart interpolation filters will assume that neighboring pixels with similar colors are part of an edge. now especially in low-res games like with the NES or GameBoy this often leads to wrong decisions because something like the eyes and mouth or mustache of a character are literally next to each other. can’t really do anything about it without breaking the shader functionality. with higher res games where details are more seperated, this becomes less of an issue.


Dualy noted.

Sorry if I sounded at all condescending. The stuff you put together is incredibly impressive. I actually use the ScaleNX for my “HDMI” preset lol.

I just noticed that it was only 2 pixels out of that whole beautiful thing and thought I’d throw it out there haha.


Here is the preset for level2-aa:!EkZz2D7b!505uRO4ibmixV-LZPnO7r3GuocnCggmGDWdccoRZ0PI

It made no sense to use a single shader approach since it would slow things down by two degrees. Included is the new sharpsmoother (would be nice if it could be updated in the repo) with a tweaked preset and parameters. Sorry for a bit late reply, but i tested some things, like the “smoother” version, sharper version etc… but the included setups are well balanced.

Cheers for the upcoming versions of the already brilliant scalefx. :grinning:


Whaddaya buyin’? Whaddaya sellin’?

How’s the slang progress?


I haven’t started. I was waiting for Sp00ky’s new scalefx version. If the reshade ports are finished and ready to go, though, I can get started porting them.


Okay, do the default conversion first if you please:!AtiprQEJpQrYgTlKIHt82hbtCpA7

The improvements version could be updated and uploaded at a later time.



Alright, they’re in. You can find the individual shaders in the ‘reshade’ directory and the presets in the ‘presets’ directory.


Thanks! Can you redo the image-adjustment shader that was in the pack I uploaded? It was modded to do away with the extra stuff. Also, internal rez, scanlines dark, and bloom intensity isn’t showing up with the correct values in the shader menu. Thanks in advance.


It’s using the same image-adjustment shader as the rest of them, which is how it’s going to have to be to be included in the repo. We can’t have a bunch of identical shaders mucking up the repo for no reason. You’re welcome to comment those unwanted parameters out in your local copy, though, of course.

I’m not seeing any of those other parameter values listed in the presets you posted, and I just copied the parameter entries straight from there, too, so if they were hardcoded, they should have come through. Can you just list what values they should have and I’ll add them to the preset?


Yes, that’s why they were grouped together. in the upload, scalefx color thresh had 2 entries. Now for slang, it only has one entry and it looks like it’s offsetting the rest of the values in the entire list.

For instance: #pragma parameter target_gamma “Target Gamma” 2.2 0.1 5.0 0.1 That’s what it is saying in the shader file. Yet it is displaying the 2nd color thresh’s setting for target gamma in the menu: 0.35 [0.00, 1.00] Instead of it’s own: 2.2 [0.1, 5.0]

So with that 2nd scalefx thresh missing, the other entries steal the previous entry’s settings instead of displaying their own. Seems like everything is working correctly visually, just not aligned with their own values in the menu. So it might be something with the scalefx files. I do see an extra scalefx 7 that wasn’t in the original. Don’t know if it’s that in particular.

Edit: Retroarch exe/dll update fixed the issue.


So I tried to reduce the extra things I didn’t need in the image adjustment shader but when I make the changes, nothing is in the menu. What did I do wrong?:

#version 450

layout(push_constant) uniform Push
	vec4 SourceSize;
	uint FrameCount;
	float overscan_percent_x;
	float overscan_percent_y;
	float ZOOM;
	float XPOS;
	float YPOS;
	float TOPMASK;
	float BOTMASK;
	float LMASK;
	float RMASK;
} registers;

layout(std140, set = 0, binding = 0) uniform UBO
   mat4 MVP;
} global;

#pragma parameter overscan_percent_x "Horizontal Overscan %" 0.0 -25.0 25.0 0.4
#pragma parameter overscan_percent_y "Vertical Overscan %" 0.0 -25.0 25.0 0.4
#pragma parameter ZOOM "Zoom Factor" 1.0 0.0 4.0 0.01
#pragma parameter XPOS "X Modifier" 0.0 -2.0 2.0 0.0012
#pragma parameter YPOS "Y Modifier" 0.0 -2.0 2.0 0.0012
#pragma parameter TOPMASK "Overscan Mask Top" 0.0 0.0 1.0 0.0025
#pragma parameter BOTMASK "Overscan Mask Bottom" 0.0 0.0 1.0 0.0025
#pragma parameter LMASK "Overscan Mask Left" 0.0 0.0 1.0 0.0025
#pragma parameter RMASK "Overscan Mask Right" 0.0 0.0 1.0 0.0025

//   Image Adjustment
//   Author: hunterk
//   License: Public domain

#include "colorspace-tools.h"

// based on "Improved texture interpolation" by Iñigo Quílez
// Original description:
vec3 sharp(sampler2D tex, vec2 texCoord){
	vec2 p = texCoord.xy;
	p = p * registers.SourceSize.xy + vec2(0.5, 0.5);
	vec2 i = floor(p);
	vec2 f = p - i;
	f = f * f * f * (f * (f * 6.0 - vec2(15.0, 15.0)) + vec2(10.0, 10.0));
	p = i + f;
	p = (p - vec2(0.5, 0.5)) *;
	return texture(tex, p).rgb;

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;

void main()
   gl_Position = global.MVP * Position;
   vec2 shift = vec2(0.5);
   vec2 overscan_coord = ((TexCoord - shift) / registers.ZOOM) * (1.0 - vec2(registers.overscan_percent_x / 100.0, registers.overscan_percent_y / 100.0)) + shift;
   vTexCoord = overscan_coord + vec2(registers.XPOS, registers.YPOS);

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;

void main()
//overscan mask
if (vTexCoord.y > registers.TOPMASK && vTexCoord.y < (1.0 - registers.BOTMASK))
   conColor = conColor;
   conColor = vec3(0.0);

if (vTexCoord.x > registers.LMASK && vTexCoord.x < (1.0 - registers.RMASK))
   conColor = conColor;
   conColor = vec3(0.0);
   FragColor = vec4(conColor, 1.0);