The Guest-Advanced NTSC thread

You can tweak the filter for a sharper look without much trouble.

Ofc. adaptive sharpening is also available…the screenie doesn’t use it though.

Edit: will add an additional sharpness option though.

Edit2: after some testing it’s clearly that increasing substractive sharpness beyond 1.0 does an even a better and clearer filtering than curve altering. Here are some comparison shots:

Substractive sharpness 1.0:

Substractive sharpness 1.5:

You can also set the amount of ringing for contrast.

1 Like

That’s probably why I have Subtractive Sharpness set to 1.5 in my “Sharpening” presets for use with my custom Blargg NTSC filter settings.

It does an excellent job!

I only hope that things aren’t broken by these changes because things are basically perfect for me right now in my presets.

I know if it happens I’ll just have to retweak and refactor in the changes but I would definitely welcome improvements included in a way that appends and extends, rather than replaces.

For example a new setting or additional range value for a new mode and maybe a phased approach before removing modes or changing settings behaviour.

Now this is just my honest opinion, no pressure but I sometimes cringe when I have things looking the way I want, for example right now and then there’s a new update.

I know that this is part of the price of progress and it has been explained to me before why this is the way things are done.

Just though I’d share my feelings sincerely. I know Guest-CRT-Advanced is not for me alone.

Thanks again for this invaluable software, I really appreciate what we are able to experience because of it.

3 Likes

could you please share a preset as sample of this screenshot boss ? great job

2 Likes

Preset: crt-guest-advanced-ntsc.slangp (neweat version)

quality = "0.000000"
HSHARPNESS = "1.750000"
SIGMA_HOR = "1.000000"
S_SHARP = "1.500000"
HARNG = "0.700000"
shadowMask = "-1.000000"
2 Likes

Very helpful! Seems like Hsharp should be at 0.00 for the best results.

The challenge is preventing the brights from blooming too much when using high brightness settings, because too much bloom has a negative effect on sharpness. I think I’ve managed to strike a good balance.

shaders = "18"
shader0 = "shaders_slang/misc/grade.slang"
filter_linear0 = "false"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = ""
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/stock.slang"
filter_linear1 = "false"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "StockPass"
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "1.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "shaders_slang/crt/shaders/guest/advanced/afterglow0.slang"
filter_linear2 = "true"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = "AfterglowPass"
float_framebuffer2 = "false"
srgb_framebuffer2 = "false"
scale_type_x2 = "source"
scale_x2 = "1.000000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "shaders_slang/crt/shaders/guest/advanced/pre-shaders-afterglow.slang"
filter_linear3 = "true"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = "PrePass0"
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/ntsc/ntsc-pass1.slang"
filter_linear4 = "false"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
alias4 = "NPass1"
float_framebuffer4 = "true"
srgb_framebuffer4 = "false"
scale_type_x4 = "source"
scale_x4 = "4.000000"
scale_type_y4 = "source"
scale_y4 = "1.000000"
shader5 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = ""
float_framebuffer5 = "true"
srgb_framebuffer5 = "false"
scale_type_x5 = "source"
scale_x5 = "0.500000"
scale_type_y5 = "source"
scale_y5 = "1.000000"
shader6 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = ""
float_framebuffer6 = "false"
srgb_framebuffer6 = "false"
scale_type_x6 = "source"
scale_x6 = "1.000000"
scale_type_y6 = "source"
scale_y6 = "1.000000"
shader7 = "shaders_slang/crt/shaders/guest/advanced/custom-fast-sharpen.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = "NtscPass"
float_framebuffer7 = "false"
srgb_framebuffer7 = "false"
scale_type_x7 = "source"
scale_x7 = "1.000000"
scale_type_y7 = "source"
scale_y7 = "1.000000"
shader8 = "shaders_slang/crt/shaders/guest/advanced/stock.slang"
filter_linear8 = "true"
wrap_mode8 = "clamp_to_border"
mipmap_input8 = "true"
alias8 = "PrePass"
float_framebuffer8 = "false"
srgb_framebuffer8 = "false"
scale_type_x8 = "source"
scale_x8 = "1.000000"
scale_type_y8 = "source"
scale_y8 = "1.000000"
shader9 = "shaders_slang/crt/shaders/guest/advanced/avg-lum-ntsc.slang"
filter_linear9 = "true"
wrap_mode9 = "clamp_to_border"
mipmap_input9 = "true"
alias9 = "AvgLumPass"
float_framebuffer9 = "false"
srgb_framebuffer9 = "false"
scale_type_x9 = "source"
scale_x9 = "1.000000"
scale_type_y9 = "source"
scale_y9 = "1.000000"
shader10 = "shaders_slang/crt/shaders/guest/advanced/linearize-ntsc.slang"
filter_linear10 = "true"
wrap_mode10 = "clamp_to_border"
mipmap_input10 = "false"
alias10 = "LinearizePass"
float_framebuffer10 = "true"
srgb_framebuffer10 = "false"
scale_type_x10 = "source"
scale_x10 = "1.000000"
scale_type_y10 = "source"
scale_y10 = "1.000000"
shader11 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang"
filter_linear11 = "true"
wrap_mode11 = "clamp_to_border"
mipmap_input11 = "false"
alias11 = "Pass1"
float_framebuffer11 = "true"
srgb_framebuffer11 = "false"
scale_type_x11 = "viewport"
scale_x11 = "1.000000"
scale_type_y11 = "source"
scale_y11 = "1.000000"
shader12 = "shaders_slang/crt/shaders/guest/advanced/gaussian_horizontal.slang"
filter_linear12 = "true"
wrap_mode12 = "clamp_to_border"
mipmap_input12 = "false"
alias12 = ""
float_framebuffer12 = "true"
srgb_framebuffer12 = "false"
scale_type_x12 = "absolute"
scale_x12 = "640"
scale_type_y12 = "source"
scale_y12 = "1.000000"
shader13 = "shaders_slang/crt/shaders/guest/advanced/gaussian_vertical.slang"
filter_linear13 = "true"
wrap_mode13 = "clamp_to_border"
mipmap_input13 = "false"
alias13 = "GlowPass"
float_framebuffer13 = "true"
srgb_framebuffer13 = "false"
scale_type_x13 = "absolute"
scale_x13 = "640"
scale_type_y13 = "absolute"
scale_y13 = "480"
shader14 = "shaders_slang/crt/shaders/guest/advanced/bloom_horizontal.slang"
filter_linear14 = "true"
wrap_mode14 = "clamp_to_border"
mipmap_input14 = "false"
alias14 = ""
float_framebuffer14 = "true"
srgb_framebuffer14 = "false"
scale_type_x14 = "absolute"
scale_x14 = "640"
scale_type_y14 = "absolute"
scale_y14 = "480"
shader15 = "shaders_slang/crt/shaders/guest/advanced/bloom_vertical.slang"
filter_linear15 = "true"
wrap_mode15 = "clamp_to_border"
mipmap_input15 = "false"
alias15 = "BloomPass"
float_framebuffer15 = "true"
srgb_framebuffer15 = "false"
scale_type_x15 = "absolute"
scale_x15 = "640"
scale_type_y15 = "absolute"
scale_y15 = "480"
shader16 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang"
filter_linear16 = "true"
wrap_mode16 = "clamp_to_border"
mipmap_input16 = "false"
alias16 = ""
float_framebuffer16 = "true"
srgb_framebuffer16 = "false"
scale_type_x16 = "viewport"
scale_x16 = "1.000000"
scale_type_y16 = "viewport"
scale_y16 = "1.000000"
shader17 = "shaders_slang/crt/shaders/guest/advanced/deconvergence-ntsc.slang"
filter_linear17 = "true"
wrap_mode17 = "clamp_to_border"
mipmap_input17 = "false"
alias17 = ""
float_framebuffer17 = "false"
srgb_framebuffer17 = "false"
scale_type_x17 = "viewport"
scale_x17 = "1.000000"
scale_type_y17 = "viewport"
scale_y17 = "1.000000"
g_crtgamut = "0.000000"
g_vignette = "0.000000"
WP = "-100.000000"
pre_bb = "0.900000"
sega_fix = "1.000000"
quality = "-1.000000"
ntsc_bright = "0.900000"
SHARPEN = "1.000000"
HSHARPNESS = "1.750000"
SIGMA_HOR = "1.000000"
S_SHARP = "1.500000"
HSHARP = "0.000000"
HARNG = "0.700000"
glow = "0.000000"
brightboost = "2.500000"
brightboost1 = "2.499998"
gsl = "1.000000"
beam_min = "1.400000"
beam_max = "1.300000"
beam_size = "0.000000"
scans = "1.000000"
scangamma = "5.000000"
maskstr = "1.000000"
mcut = "1.000000"
gamma_out = "1.949999"
textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4"
SamplerLUT1 = "shaders_slang/crt/shaders/guest/advanced/lut/trinitron-lut.png"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT1_mipmap = "false"
SamplerLUT2 = "shaders_slang/crt/shaders/guest/advanced/lut/inv-trinitron-lut.png"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT2_mipmap = "false"
SamplerLUT3 = "shaders_slang/crt/shaders/guest/advanced/lut/nec-lut.png"
SamplerLUT3_linear = "true"
SamplerLUT3_wrap_mode = "clamp_to_border"
SamplerLUT3_mipmap = "false"
SamplerLUT4 = "shaders_slang/crt/shaders/guest/advanced/lut/ntsc-lut.png"
SamplerLUT4_linear = "true"
SamplerLUT4_wrap_mode = "clamp_to_border"
SamplerLUT4_mipmap = "false"
8 Likes

It really seems you managed to get a satisfactory appearance, adding sharpness while preserving the dithering dissolving. I’m also curious how you like the adaptive sharpness feature. My educated guess is that tv engineers had years of experience and motivation to do something similar on crt’s, maybe even with better results.

2 Likes

NESGuy’s preset June02th + Sega Genesis Rainbow:

Borders set to FULL in GenesisPlusGX core

shaders = "18"
shader0 = "shaders_slang/crt/shaders/tvout-tweaks.slang"
filter_linear0 = "false"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = ""
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
shader1 = "shaders_slang/crt/shaders/mame_hlsl/shaders/old/shaders/mame_hlsl/shaders/mame_ntsc.slang"
filter_linear1 = "true"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "StockPass"
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "1.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "shaders_slang/crt/shaders/guest/advanced/afterglow0.slang"
filter_linear2 = "true"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = "AfterglowPass"
float_framebuffer2 = "false"
srgb_framebuffer2 = "false"
scale_type_x2 = "source"
scale_x2 = "1.000000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "shaders_slang/crt/shaders/guest/advanced/pre-shaders-afterglow.slang"
filter_linear3 = "true"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = "PrePass0"
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/ntsc/ntsc-pass1.slang"
filter_linear4 = "false"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
alias4 = "NPass1"
float_framebuffer4 = "true"
srgb_framebuffer4 = "false"
scale_type_x4 = "source"
scale_x4 = "4.000000"
scale_type_y4 = "source"
scale_y4 = "1.000000"
shader5 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = ""
float_framebuffer5 = "true"
srgb_framebuffer5 = "false"
scale_type_x5 = "source"
scale_x5 = "0.500000"
scale_type_y5 = "source"
scale_y5 = "1.000000"
shader6 = "shaders_slang/crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = ""
float_framebuffer6 = "false"
srgb_framebuffer6 = "false"
scale_type_x6 = "source"
scale_x6 = "1.000000"
scale_type_y6 = "source"
scale_y6 = "1.000000"
shader7 = "shaders_slang/crt/shaders/guest/advanced/custom-fast-sharpen.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = "NtscPass"
float_framebuffer7 = "false"
srgb_framebuffer7 = "false"
scale_type_x7 = "source"
scale_x7 = "1.000000"
scale_type_y7 = "source"
scale_y7 = "1.000000"
shader8 = "shaders_slang/crt/shaders/guest/advanced/stock.slang"
filter_linear8 = "true"
wrap_mode8 = "clamp_to_border"
mipmap_input8 = "true"
alias8 = "PrePass"
float_framebuffer8 = "false"
srgb_framebuffer8 = "false"
scale_type_x8 = "source"
scale_x8 = "1.000000"
scale_type_y8 = "source"
scale_y8 = "1.000000"
shader9 = "shaders_slang/crt/shaders/guest/advanced/avg-lum-ntsc.slang"
filter_linear9 = "true"
wrap_mode9 = "clamp_to_border"
mipmap_input9 = "true"
alias9 = "AvgLumPass"
float_framebuffer9 = "false"
srgb_framebuffer9 = "false"
scale_type_x9 = "source"
scale_x9 = "1.000000"
scale_type_y9 = "source"
scale_y9 = "1.000000"
shader10 = "shaders_slang/crt/shaders/guest/advanced/linearize-ntsc.slang"
filter_linear10 = "true"
wrap_mode10 = "clamp_to_border"
mipmap_input10 = "false"
alias10 = "LinearizePass"
float_framebuffer10 = "true"
srgb_framebuffer10 = "false"
scale_type_x10 = "source"
scale_x10 = "1.000000"
scale_type_y10 = "source"
scale_y10 = "1.000000"
shader11 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang"
filter_linear11 = "true"
wrap_mode11 = "clamp_to_border"
mipmap_input11 = "false"
alias11 = "Pass1"
float_framebuffer11 = "true"
srgb_framebuffer11 = "false"
scale_type_x11 = "viewport"
scale_x11 = "1.000000"
scale_type_y11 = "source"
scale_y11 = "1.000000"
shader12 = "shaders_slang/crt/shaders/guest/advanced/gaussian_horizontal.slang"
filter_linear12 = "true"
wrap_mode12 = "clamp_to_border"
mipmap_input12 = "false"
alias12 = ""
float_framebuffer12 = "true"
srgb_framebuffer12 = "false"
scale_type_x12 = "absolute"
scale_x12 = "640"
scale_type_y12 = "source"
scale_y12 = "1.000000"
shader13 = "shaders_slang/crt/shaders/guest/advanced/gaussian_vertical.slang"
filter_linear13 = "true"
wrap_mode13 = "clamp_to_border"
mipmap_input13 = "false"
alias13 = "GlowPass"
float_framebuffer13 = "true"
srgb_framebuffer13 = "false"
scale_type_x13 = "absolute"
scale_x13 = "640"
scale_type_y13 = "absolute"
scale_y13 = "480"
shader14 = "shaders_slang/crt/shaders/guest/advanced/bloom_horizontal.slang"
filter_linear14 = "true"
wrap_mode14 = "clamp_to_border"
mipmap_input14 = "false"
alias14 = ""
float_framebuffer14 = "true"
srgb_framebuffer14 = "false"
scale_type_x14 = "absolute"
scale_x14 = "640"
scale_type_y14 = "absolute"
scale_y14 = "480"
shader15 = "shaders_slang/crt/shaders/guest/advanced/bloom_vertical.slang"
filter_linear15 = "true"
wrap_mode15 = "clamp_to_border"
mipmap_input15 = "false"
alias15 = "BloomPass"
float_framebuffer15 = "true"
srgb_framebuffer15 = "false"
scale_type_x15 = "absolute"
scale_x15 = "640"
scale_type_y15 = "absolute"
scale_y15 = "480"
shader16 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang"
filter_linear16 = "true"
wrap_mode16 = "clamp_to_border"
mipmap_input16 = "false"
alias16 = ""
float_framebuffer16 = "true"
srgb_framebuffer16 = "false"
scale_type_x16 = "viewport"
scale_x16 = "1.000000"
scale_type_y16 = "viewport"
scale_y16 = "1.000000"
shader17 = "shaders_slang/crt/shaders/guest/advanced/deconvergence-ntsc.slang"
filter_linear17 = "true"
wrap_mode17 = "clamp_to_border"
mipmap_input17 = "false"
alias17 = ""
float_framebuffer17 = "false"
srgb_framebuffer17 = "false"
scale_type_x17 = "viewport"
scale_x17 = "1.000000"
scale_type_y17 = "viewport"
scale_y17 = "1.000000"
TVOUT_RESOLUTION = "288.000000"
ntscsignal = "1.000000"
avalue = "0.000000"
bvalue = "0.000000"
scantime = "51.900009"
signaloffset = "0.000000"
WP = "-100.000000"
pre_bb = "0.900000"
sega_fix = "1.000000"
quality = "-1.000000"
ntsc_bright = "0.900000"
HSHARPNESS = "1.750000"
SIGMA_HOR = "1.000000"
S_SHARP = "1.500000"
HSHARP = "0.000000"
HARNG = "0.700000"
glow = "0.000000"
brightboost = "2.500000"
brightboost1 = "2.499998"
gsl = "1.000000"
beam_min = "1.400000"
beam_max = "1.300000"
beam_size = "0.000000"
scans = "1.000000"
scangamma = "5.000000"
maskstr = "1.000000"
mcut = "1.000000"
gamma_out = "1.949999"
textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4"
SamplerLUT1 = "shaders_slang/crt/shaders/guest/advanced/lut/trinitron-lut.png"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT1_mipmap = "false"
SamplerLUT2 = "shaders_slang/crt/shaders/guest/advanced/lut/inv-trinitron-lut.png"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT2_mipmap = "false"
SamplerLUT3 = "shaders_slang/crt/shaders/guest/advanced/lut/nec-lut.png"
SamplerLUT3_linear = "true"
SamplerLUT3_wrap_mode = "clamp_to_border"
SamplerLUT3_mipmap = "false"
SamplerLUT4 = "shaders_slang/crt/shaders/guest/advanced/lut/ntsc-lut.png"
SamplerLUT4_linear = "true"
SamplerLUT4_wrap_mode = "clamp_to_border"
SamplerLUT4_mipmap = "false"

Edit:

This is some proof comparison with a guy that played his Genesis composite connected through Composite video some years ago. Left side is new preset.

Edit 2:

This is another experimental sub-preset from the NESguy’s one. Version 2.0

#reference "NESguy june02+Rainbow.slangp"
TVOUT_RESOLUTION = "416.000000"
TVOUT_COMPOSITE_CONNECTION = "1.000000"
TVOUT_RESOLUTION_Y = "352.000000"
TVOUT_RESOLUTION_I = "67.199997"
TVOUT_RESOLUTION_Q = "81.599998"
scantime = "51.800011"

Another comparison from this new preset with a Composite-video Sonic 2 gameplay Left side is new preset.

4 Likes

The downscaling of the mask/scanlines darkens the image a lot, making such comparisons difficult.

Shader settings are scale/resolution dependent; it’s very important that the correct scale is used in order to get the intended result (even moreso when using 100% strength masks)

Personally, I don’t think the rainbow effects are intended by the artist or necessary for the art, but it’s fun trying to get as close to the real hardware as possible.

4 Likes

I haven’t had a chance to play with the adaptive sharpness too much, I’ll check it out.

2 Likes

@guest.r

I tried the adaptive sharpness, it’s very nice but you have to add something else to reduce the blooming of highlights, some additional sharpness is required even with adaptive sharpness.

Is there some code alteration (or better yet, settings) that will alter the blooming with higher brightness settings? Limiting the bloom has been tricky, especially when trying to get the NTSC preset bright enough. I think some slightly different bloom dynamics might be required for NTSC. (just to clarify, I’m not talking about the “bloom” setting per se, but the tendency for bright pixels to bleed into adjacent darker pixels)

The text in this shot looks great!

The white text in this shot is blooming a bit too much.

2 Likes

There is a quick fix for this by using ordinary (positive parameter value) for 3-phase filtering. I mentioned earlier that 3-phase filtering can’t resolve dithering patterns in the first place, which produces a situation with maybe more viable options.

More reading on this topic:

The 256px presets use 3-phase NTSC output, which is what the NES, SNES, and N64 output, while the 320px presets reflect the more common NTSC output from the Mega Drive, PC-Engine, PlayStation, and most consumer video electronics.

The ‘blooming’ roots in a moderately wide luma filter of the ntsc shaders. I’m suggesting ordinary sharpen effect, maybe with a portion of resolution scaling. Assuming no color artifacting option is used stronger resolution scaling would also do well.

3 Likes

Thanks! I’ll give those suggestions a shot. However, I’m not sure the issue is specific to guest-NTSC. Here’s a shot from guest-advanced, and the white text is just a tad too bloomy. Not sure what the best way to tackle this is. It’s honestly very close as-is, I’m being very nitpicky. Perhaps a bit more subtractive sharpness?

settings for this shot

shaders = "12"
shader0 = "shaders_slang/misc/grade.slang"
filter_linear0 = "false"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = ""
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/stock.slang"
filter_linear1 = "false"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "StockPass"
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "1.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "shaders_slang/crt/shaders/guest/advanced/afterglow0.slang"
filter_linear2 = "false"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = "AfterglowPass"
float_framebuffer2 = "false"
srgb_framebuffer2 = "false"
scale_type_x2 = "source"
scale_x2 = "1.000000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "shaders_slang/crt/shaders/guest/advanced/pre-shaders-afterglow.slang"
filter_linear3 = "false"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "true"
alias3 = "PrePass"
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/avg-lum.slang"
filter_linear4 = "true"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "true"
alias4 = "AvgLumPass"
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/linearize.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = "LinearizePass"
float_framebuffer5 = "true"
srgb_framebuffer5 = "false"
scale_type_x5 = "source"
scale_x5 = "1.000000"
scale_type_y5 = "source"
scale_y5 = "1.000000"
shader6 = "shaders_slang/crt/shaders/guest/advanced/gaussian_horizontal.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = ""
float_framebuffer6 = "true"
srgb_framebuffer6 = "false"
scale_type_x6 = "absolute"
scale_x6 = "800"
scale_type_y6 = "source"
scale_y6 = "1.000000"
shader7 = "shaders_slang/crt/shaders/guest/advanced/gaussian_vertical.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = "GlowPass"
float_framebuffer7 = "true"
srgb_framebuffer7 = "false"
scale_type_x7 = "absolute"
scale_x7 = "800"
scale_type_y7 = "absolute"
scale_y7 = "600"
shader8 = "shaders_slang/crt/shaders/guest/advanced/bloom_horizontal.slang"
filter_linear8 = "true"
wrap_mode8 = "clamp_to_border"
mipmap_input8 = "false"
alias8 = ""
float_framebuffer8 = "true"
srgb_framebuffer8 = "false"
scale_type_x8 = "absolute"
scale_x8 = "800"
scale_type_y8 = "absolute"
scale_y8 = "600"
shader9 = "shaders_slang/crt/shaders/guest/advanced/bloom_vertical.slang"
filter_linear9 = "true"
wrap_mode9 = "clamp_to_border"
mipmap_input9 = "false"
alias9 = "BloomPass"
float_framebuffer9 = "true"
srgb_framebuffer9 = "false"
scale_type_x9 = "source"
scale_x9 = "1.000000"
scale_type_y9 = "source"
scale_y9 = "1.000000"
shader10 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced.slang"
filter_linear10 = "true"
wrap_mode10 = "clamp_to_border"
mipmap_input10 = "false"
alias10 = ""
float_framebuffer10 = "true"
srgb_framebuffer10 = "false"
scale_type_x10 = "viewport"
scale_x10 = "1.000000"
scale_type_y10 = "viewport"
scale_y10 = "1.000000"
shader11 = "shaders_slang/crt/shaders/guest/advanced/deconvergence.slang"
filter_linear11 = "true"
wrap_mode11 = "clamp_to_border"
mipmap_input11 = "false"
alias11 = ""
float_framebuffer11 = "false"
srgb_framebuffer11 = "false"
scale_type_x11 = "viewport"
scale_x11 = "1.000000"
scale_type_y11 = "viewport"
scale_y11 = "1.000000"
g_crtgamut = "0.000000"
g_vignette = "0.000000"
WP = "-100.000000"
pre_bb = "0.900000"
glow = "0.000000"
gamma_c = "1.160000"
brightboost = "2.500000"
brightboost1 = "2.499998"
gsl = "1.000000"
beam_size = "0.000000"
vertmask = "0.400000"
scans = "1.999999"
maskstr = "1.000000"
mcut = "1.000000"
mclip = "0.000000"
textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4"
SamplerLUT1 = "shaders_slang/crt/shaders/guest/advanced/lut/trinitron-lut.png"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT1_mipmap = "false"
SamplerLUT2 = "shaders_slang/crt/shaders/guest/advanced/lut/inv-trinitron-lut.png"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT2_mipmap = "false"
SamplerLUT3 = "shaders_slang/crt/shaders/guest/advanced/lut/nec-lut.png"
SamplerLUT3_linear = "true"
SamplerLUT3_wrap_mode = "clamp_to_border"
SamplerLUT3_mipmap = "false"
SamplerLUT4 = "shaders_slang/crt/shaders/guest/advanced/lut/ntsc-lut.png"
SamplerLUT4_linear = "true"
SamplerLUT4_wrap_mode = "clamp_to_border"
SamplerLUT4_mipmap = "false"
3 Likes

What do you think about a filter that doesn’t apply the mask to pure white or some customizable threshold? It could increase brightness/contrast with 100% mask.

1 Like

I think i understand this specific problem a bit better now. When brightboost values are very high, the contrast between text and background becomes smaller. Substractive sharpness with some ringing could help indeed, because local contrast is established.

Such mask distribution is already implemented with masks 5-10 and 13. Negative main mask strength is to be used in this case.

3 Likes

Didn’t know that. Thanks! :grin:

3 Likes

You can also play a bit with smart edge interpolation to reduce the ‘blooming’. One of the motivations for this effect was to be able to have clearer edges. In this specific ‘Super Mario case’ the threshold has to be reduced or it will miss the sharpen effect.

3 Likes

aperture_3_6_rgb

Thoughts on this 4K mask? It’s comparable to RRGGBBX but might be a bit brighter. I got it from MAME’s crt-geom shader.

aperture_3_6_rgb

2 Likes

You can also check it out in crt-hyllian, which uses the big subpixel mask function. It’s mask 8:

3 Likes

Well I’m always game for fiddling around with a new mask, albeit it’s not as interesting now that my screen isn’t being sharpened out of order.

1 Like

@guest.r, I came across a shadertoy from a guy Astherix that simulates “analog overshoot”. It seems to work well for sharpening up NTSC output at the cost of some ringing. It might help with @Nesguy’s test cases. Here’s a slang version:

#version 450

// Analog Overshoot
// by Astherix

// adapted for slang from NTSC Codec (w/Overshoot & Noise) shadertoy
// https://www.shadertoy.com/view/flG3zd

// This filter is a highly tweakable NTSC codec. It encodes and decodes an NTSC
// signal, generating artifacts. Licensed LGPLv3

// Adapted from https://www.shadertoy.com/view/7dyXWG

// This pass applies a simulation of analog overshoot, which
// is the cause for ghosting artifacts around edges (sharp transitions)
// This is not necessary for encoding/decoding.

layout(push_constant) uniform Push
{
	vec4 SourceSize;
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float hermite_tension, hermite_bias, decode_chroma_luma_separately;
} params;

// These two define the tension and bias parameters
// of the Hermite interpolation function
// Values closer to 0.5 on both parameters yield
// more aggressive ghosting artifacts
#pragma parameter hermite_tension "Hermite Tension" 0.25 0.01 1.0 0.01
#pragma parameter hermite_bias "Hermite Bias" 0.75 0.01 1.0 0.01

#pragma parameter decode_chroma_luma_separately "Decode Chroma and Luma Separately" 1.0 0.0 1.0 1.0
#define DECODE_CHROMA_LUMA_SEPARATELY params.decode_chroma_luma_separately

#define HERMITE_TENSION params.hermite_tension
#define HERMITE_BIAS params.hermite_bias

#define iChannel0 Source
#define iResolution params.OutputSize.xy
#define fragCoord (vTexCoord.xy * params.OutputSize.xy)
#define fragColor FragColor
#define iFrame params.FrameCount
#define iTime (params.FrameCount / 60)

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

#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.0001;
}

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

float hermite(float y0, float y1, float y2, float y3, float m, float tension, float bias) {
    float m2 = m*m,
          m3 = m2*m,
          m0 =      (y1-y0)*(1.0+bias)*(1.0-tension)/2.0;
          m0 = m0 + (y2-y1)*(1.0-bias)*(1.0-tension)/2.0;
    float m1 =      (y2-y1)*(1.0+bias)*(1.0-tension)/2.0;
          m1 = m1 + (y3-y2)*(1.0-bias)*(1.0-tension)/2.0;
    float a0 =  2.0*m3 - 3.0*m2 + 1.0,
          a1 =      m3 - 2.0*m2 + m,
          a2 =      m3 -     m2,
          a3 = -2.0*m3 + 3.0*m2;
     
     return (a0*y1+a1*m0+a2*m1+a3*y2);
}

void main()
{
   vec2 px = vTexCoord.xy * params.OutputSize.zw;
    // Normalized pixel coordinates (from 0 to 1)
    vec2 fcm1 = vec2(fragCoord.x-4.0, fragCoord.y)/iResolution.xy,
         fcm0 = vec2(fragCoord.x-0.0, fragCoord.y)/iResolution.xy,
         fcp1 = vec2(fragCoord.x+4.0, fragCoord.y)/iResolution.xy,
         fcp2 = vec2(fragCoord.x+8.0, fragCoord.y)/iResolution.xy;
    
    vec3 y0 = texture(Source, fcm1).xyz,
         y1 = texture(Source, fcm0).xyz,
         y2 = texture(Source, fcp1).xyz,
         y3 = texture(Source, fcp2).xyz;
    
    vec3 o = y1;
         
    float l = HERMITE_TENSION, y = HERMITE_BIAS;
         
    vec3 f = vec3(
        hermite(y0.x, y1.x, y2.x, y3.x, l, y, y * 50.0),
        hermite(y0.y, y1.y, y2.y, y3.y, l, y, y * 50.0),
        hermite(y0.z, y1.z, y2.z, y3.z, l, y, y * 50.0)
    );
    
    fragColor = mix(vec4(f, 1.0), vec4(y1, 1.0), DECODE_CHROMA_LUMA_SEPARATELY);
}  

The rest of their NTSC shader is quite interesting, as well, since it’s the only one I’ve seen that doesn’t require using float framebuffers, but it also breaks on my GPU/driver (intel+mesa; works fine on d3d11, though) and doesn’t handle the “tv” NTSC test ROM as well as some of the others.

4 Likes