Also, just a thought, but would it be outside of the scope of this shader to incorporate the “base mask” feature that GDV uses to simulate the parts of the mask not being hit by the electron beam being ever-so-slightly visible? It’s something that occurs on every CRT as far as I can tell, and it’s unobtrusive. Sure, it does lower contrast somewhat, but CRTs never had perfect contrast to begin with.
Great stuff thanks for the detailed list - its really good to understand what people want or are finding problematic. I need to add two things to the list: fix the shader so that it works (by removing the scaling viewport line in the .slangp) and rotation of the scanlines and mask for those that cant tate mode their monitors.
So the problem with good paper white and peak luminance is that 200 and 1000 works perfectly for the S95B that I have. HDR displays vary wildly in this respect it seems.
What is Super XBR and what does it do by the way.
Super-XBR is an enhancement shader that removes some aliasing at the edges of sprites and graphics but in a very subtle manner that attempts to preserve the original pixel artists intention as much as possible.
It can actually help make things look nicer for example when the TVL is too low for the phosphor triads to fit properly within the boundaries of a font for example, the excess gives an unpleasant ladder effect and chromatic aberration.
It’s very subtle and it scales inversely with resolution so for higher res consoles like Turbo Duo, Sega Genesis, PSX and many 90’s arcade systems it’s a set it and forget it, while on the lower resolution systems like SNES it works well on games that use lots of blending, sharing and melding in their graphics for example Super Metroid, Contra III and Hagane but it doesn’t look so well in games that use more cartoony line art graphics like most 2D Mario Games. Those look best without XBR.
For me all NES games look better without it.
For most 16-bit era and 90’s arcade games it’s an ideal companion.
I’m basing this on the default settings by the way. It is fully adjustable.
This is how it looks when I use it.
These screenshots also demonstrate the Dot Mask Pattern that I’d like to be able to make using Sony Megatron Color Video Monitor and they use all of the Shaders (via Mega Bezel) that I asked you to integrate and combine if possible in my previous post.
It really bridges the gap between certain Turbo Duo games and SNES games. While Turbo Duo was capable of higher resolution and more simultaneous onscreen colours, it’s palette was limited compared to SNES and many games just looked a tad rougher around the edges on Turbo Duo compared to on SNES.
I’m not completely sure as to why though. Someone did a comparison of the excellent PC-Engine port of Street Fighter II Champion Edition Vs the Sega Genesis port and the SNES port and the SNES port seemed to have a higher colour count than the other 2.
I’m really having a lot of fun with this shader!
These pictures are a bit far off from how things look in real life.
@guest.r Those blue horizontal lines are not visible in real life. The waterfall looks perfectly translucent in practice and I can look up and down without the said lines appearing.
Edit: maybe if I had set my camera speed to 1/30 instead of 1/60 I might have gotten a proper capture.
This is true. Is this 200 and 1000 using you’re display’s “HDR Game mode” equivalent setting or the brightest HDR setting because if I’m using “HDR Bright” I could get away with much lower Paperwhite settings?
Maybe a note or hint can be included with a suggested starting point for LG OLED users which is more in line with what you’ve seen reported here by users of those displays.
how’s this? https://github.com/libretro/slang-shaders/commit/eca36c3b049e5351c6c4ba9c2d82d4d45a896066
Yes ok I saw these in grade shader and wasnt sure at the time what they were referring to. I see that they are the primaries of the actual phosphors used which couldnt exactly match the standards primaries. Ill add these in as it should he easy to do.
I think you can achieve that by upping the gamma cutoff to the cutoff that the standard defined. I think I documented at the top. This should give you non black blacks which is what you might be after?
Oh perfect! Thanks @hunterk youre an absolute star. Ive been meaning to do this but every spare moment Im pouring into my other project and I needed to get my head back into git submission from where I left it off ages ago. I do need to find time to add a few features requested above. Thanks again very much appreciated!!!
Ah right xBR is a stylistic shader as in it doesnt simulate something that happens in an original hardware setup - its trying to give us something new. Thats great its interesting to see new stuff. I do like the original xBR shader look - reminds me a bit Windwaker and that cell shading look (but in 2d obviously) Ill have to give it a spin! Very ingenious to put this in the pre phase - looks great.
Yeah the TV is HDR game mode. Maybe what I need to do is separate out those two options and state you must change these? Still nobody really goes into the shader options as they ping pong about in the shader directory.
This looks great! Its really getting that old TV look. Im going to have to give this a whirl too. I just need to shove all the above in front of that 20L4 preset and it should work?
You might as well add the NTSC-FCC 1953 Standard Phosphor as well if that is workable:
0.67 0.33 0.21 0.71 0.14 0.08 (xR yR xG yG xB yB)
or
- const mat3 k1953_to_XYZ = mat3(
- 0.588010f, 0.179133f, 0.183224f,
- 0.289661f, 0.605640f, 0.104699f,
- 0.000000f, 0.068241f, 1.020817f);
since that is the only other major (color) CRT phosphor set/color system that will be missing after you add the others.
They were only definitively used in some early/1950s color TVs, but they may have also been used for at least some Brazilian/PAL-M and SECAM displays? (This sort of thing is way out in the weeds. The original standards for both PAL-M and SECAM specified the same primaries as NTSC 1953, but that doesn’t mean they actually used the phosphors given that most if not all manufacturers of actual NTSC TVs were ignoring the standard and using different, brighter but narrower gamut phosphors by the the late 1950s.)
I don’t know how much clearer you need to be than you already have been. Maybe put it right up on the top?
This is all pioneering stuff so users might just need to take the time to get used to doing things a different way.
The ones who really care would.
I wouldn’t necessarily say it’s trying to give us something new. I think it’s tries to recreate the “natural” antialising and blending of the edges that occurs on a real CRT. When pushed, it can produce false positives and posterization type artifacts.
I’d say that it helps give a nice finishing touch and can actually match and even exceed the aesthetic quality of sprites and other graphics compared to real hardware on a CRT, especially when they are blown up on a large screen TV, while preserving the original retro style of the graphics if used sparingly.
It doesn’t have to look cel shaded at all. It can work more like upscaler for 2D graphics.
I’m really glad to see that you don’t mind implementing it!
Yes, as far as I could tell there were no issues by simply doing that. Load the preset, then load whichever Megatron preset you want after, and it’ll work. Again, though, I’m wondering if there’s a way to get rid of those excess Stock passes? I tried deleting them, but it seems to fail if they’re not there. I assume it’s because the last pass is expecting some passes before it with a specific alias in the preset, which is what I did if you’ll notice (the shaders are Stock, but the aliases are exactly the same as they were in guest’s original preset).
First off, just to get it out of the way: the version of your decoupling with crt-guest-advanced-ntsc-pass1.slang included blows out/massively overbrightens the image in HDR on my system. Changing float_framebuffer8 to false (or changing srgb_framebuffer8 to true) appears to fix this at a glance, but i suppose there may be unintended consequences?
That said:
crt-guest-advanced-ntsc-pass1.slang calls LinearizePass, the previous shader in the chain (alias7 in your decoupling) in three places.
If those calls are replaced with NtscPass (alias4/custom-fast-sharpen.slang in your decoupling) the resulting preset loads, but doing that would obviously break crt-guest-advanced-ntsc.
Either continuing to use stocks for the dummy alias or adding a “crt-guest-advanced-ntsc-pass1a.slang” with those alias calls changed seem like the best solutions? What would you suggest hunterk?
The preset seems to load without issue with the stocks you have at shader5 and shader6 removed outright.
(ntsc-pass3.slang calls PrePass0/alias0 a couple times as well, but that is explicitly pulling a pre ntsc-pass1.slang version of the frame, so i think that just needs to be left alone barring more extensive changes to the shader scripting.)
Whoops, accidentally posted that as a reply to the thread rather than to you specifically.
Yup sounds like a plan! This should be dead simple to add - the only problem is fitting it all into one line in the RA UI!
Yeah Id guess the same, although a bit dirty I doubt it has too detrimental effect as these (I think!) boil down to copies of 320x240p textures at most as far as I remember. Great stuff Im going to give these a spin on some NES stuff.
So are you saying you couldnt get it to load? If so thats odd as @GPDP1 has got screenshots so there must be some difference in what youre both doing. I want to give this a try when I find some time so hopefully it works in one of the ways above.
If it’s too dirty or hacky, maybe you can just stick to the leaner, lighter version @GPDP1 isolated first, at least for now.
After spending more time tweaking, I doubt I even need the other filtering and sharpening stuff anymore. I would even argue that it’s a separate module from the NTSC stuff.
Just the Super-XBR stuff plus the 5 pass NTSC stuff should work wonders.
I’ve finished with my first release of my CyberLab Megatron Death To Pixels 4K HDR Preset Pack by the way! Just to upload and share it now. Will probably do a quick Mega share followed by GitHub later.
Here it is:
Click the link below to download
Oh, no, it loads fine. I was just helping @GPDP1 workshop cutting out the stock passes.
I think the blown out brightness in HDR thing is only a problem when the decoupled NTSC preset is loaded by itself without Megatron appended, due to the last shader in the chain (crt-guest-advanced-ntsc-pass1.slang) having float_framebuffer8 set to true in the version of the preset GPDP1 posted. My adaptive ntsc-adaptive presets display the same behavior if i change their final float_framebuffer from false to true.
shaders = "6"
shader0 = "shaders_slang/crt/shaders/guest/advanced/stock.slang"
filter_linear0 = "false"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = "PrePass0"
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
scale_type_x0 = "source"
scale_x0 = "1.000000"
scale_type_y0 = "source"
scale_y0 = "1.000000"
shader1 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass1.slang"
filter_linear1 = "false"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "NPass1"
float_framebuffer1 = "true"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "4.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang"
filter_linear2 = "true"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = ""
float_framebuffer2 = "true"
srgb_framebuffer2 = "false"
scale_type_x2 = "source"
scale_x2 = "0.500000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang"
filter_linear3 = "true"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = ""
float_framebuffer3 = "false"
srgb_framebuffer3 = "false"
scale_type_x3 = "source"
scale_x3 = "1.000000"
scale_type_y3 = "source"
scale_y3 = "1.000000"
shader4 = "shaders_slang/crt/shaders/guest/advanced/custom-fast-sharpen.slang"
filter_linear4 = "true"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
alias4 = "NtscPass"
float_framebuffer4 = "false"
srgb_framebuffer4 = "false"
scale_type_x4 = "source"
scale_x4 = "1.000000"
scale_type_y4 = "source"
scale_y4 = "1.000000"
shader5 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1a.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = "Pass1"
float_framebuffer5 = "false"
srgb_framebuffer5 = "false"
scale_type_x5 = "viewport"
scale_x5 = "1.000000"
scale_type_y5 = "source"
scale_y5 = "1.000000"
With “crt-guest-advanced-ntsc-pass1a.slang” being:
#version 450
/*
CRT - Guest - NTSC - Pass1 (Decoupled)
Copyright (C) 2018-2022 guest(r) - [email protected]
Incorporates many good ideas and suggestions from Dr. Venom.
I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
layout(push_constant) uniform Push
{
vec4 SourceSize;
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount;
float SIGMA_HOR;
float HSHARPNESS;
float S_SHARP;
float HARNG;
float HSHARP;
float spike;
float internal_res;
} params;
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
} global;
#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0
#pragma parameter HSHARPNESS " Horizontal Filter Range" 1.5 1.0 8.0 0.05
#define HSHARPNESS params.HSHARPNESS
#pragma parameter SIGMA_HOR " Horizontal Blur Sigma" 0.90 0.1 7.0 0.05
#define SIGMA_HOR params.SIGMA_HOR
#pragma parameter S_SHARP " Substractive Sharpness" 0.9 0.0 2.0 0.10
#define S_SHARP params.S_SHARP
#pragma parameter HSHARP " Sharpness Definition" 1.2 0.0 2.0 0.10
#define HSHARP params.HSHARP
#pragma parameter HARNG " Substractive Sharpness Ringing" 0.4 0.0 4.0 0.10
#define HARNG params.HARNG
#pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10
#define spike params.spike
#define COMPAT_TEXTURE(c,d) texture(c,d)
#define TEX0 vTexCoord
#define OutputSize params.OutputSize
#define gl_FragCoord (vTexCoord * OutputSize.xy)
#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;
vTexCoord = TexCoord * 1.00001;
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D NtscPass;
float invsqrsigma = 1.0/(2.0*SIGMA_HOR*SIGMA_HOR);
float gaussian(float x)
{
return exp(-x*x*invsqrsigma);
}
void main()
{
vec2 prescalex = vec2(textureSize(NtscPass, 0)) / params.OriginalSize.xy;
vec4 SourceSize = params.OriginalSize * vec4(2.0*prescalex.x, prescalex.y, 0.5/prescalex.x, 1.0/prescalex.y);
float f = fract(SourceSize.x * vTexCoord.x);
f = 0.5 - f;
vec2 tex = floor(SourceSize.xy * vTexCoord)*SourceSize.zw + 0.5*SourceSize.zw;
vec3 color = 0.0.xxx;
float scolor = 0.0;
vec2 dx = vec2(SourceSize.z, 0.0);
float w = 0.0;
float swsum = 0.0;
float wsum = 0.0;
vec3 pixel;
float hsharpness = HSHARPNESS;
vec3 cmax = 0.0.xxx;
vec3 cmin = 1.0.xxx;
float sharp = gaussian(hsharpness) * S_SHARP;
float maxsharp = 0.20;
float FPR = hsharpness;
float fpx = 0.0;
float sp = 0.0;
float sw = 0.0;
float ts = 0.025;
vec3 luma = vec3(0.2126, 0.7152, 0.0722);
float LOOPSIZE = ceil(2.0*FPR);
float CLAMPSIZE = round(2.0*LOOPSIZE/3.0);
float n = -LOOPSIZE;
do
{
pixel = COMPAT_TEXTURE(NtscPass, tex + n*dx).rgb;
sp = max(max(pixel.r,pixel.g),pixel.b);
w = gaussian(n+f) - sharp;
fpx = abs(n+f-sign(n)*FPR)/FPR;
if (abs(n) <= CLAMPSIZE) { cmax = max(cmax, pixel); cmin = min(cmin, pixel); }
if (w < 0.0) w = clamp(w, mix(-maxsharp, 0.0, pow(clamp(fpx,0.0,1.0), HSHARP)), 0.0);
color = color + w * pixel;
wsum = wsum + w;
sw = max(w, 0.0) * (dot(pixel,luma) + ts);
scolor = scolor + sw * sp;
swsum = swsum + sw;
n = n + 1.0;
} while (n <= LOOPSIZE);
color = color / wsum;
scolor = scolor / swsum;
color = clamp(mix(clamp(color, cmin, cmax), color, HARNG), 0.0, 1.0);
scolor = clamp(mix(max(max(color.r, color.g),color.b), scolor, spike), 0.0, 1.0);
FragColor = vec4(color, scolor);
}
Note that, just like unmodified ntsc-adaptive, these presets fail to blend pseudo-hires dithering on Super Famicom titles like Kirby’s Dream Land 3. In those cases, scale_type_x1 = “source” needs to be changed to scale_type_x1 = “absolute”, and scale_x1 = “4.000000” needs to be changed to scale_x1 = “1024”.