New CRT shader from Guest + CRT Guest Advanced updates

It is not feasible in the sense that you can’t emulate white brightness without white.

You can keep full mask for dark tones, but you have to “mitigate” it sooner or later as the source image approaches brightest ones. I believe @beans did that in a scientific manner.

4 Likes

Looks interesting tho, Is the code straight forward enough to be imported to Slang with ease?

Good contrast is worth much, regardless. You can play with guest-advanced with these setups:

scan-brightness2

A 4k display or odd integer scaling is very much preferred though.

4 Likes

The scanline implementation in crt-beans is also energy-preserving, but it uses an entirely different method than Lottes uses. The only limitation is that if you want to maintain scanline separation at full brightness, you basically turn down the brightness a bit (which it seems Lottes does as well with the 7/8 brightness limit). In crt-beans that is configurable, so you can have no brightness drop at all if you are ok with the scanlines merging at full brightness.

Actually, it is basically impossible to configure the scanlines in crt-beans to not be energy-preserving. You might be able to do it if you mess with the horizontal width after the filter pass (not applicable to the VGA preset) or if you set the parameters out of bounds. That was one of my goals during development.

The mask implementation in crt-beans is also energy-preserving on average. The unmasked scanlines get blended in when the mask gets saturated, in such a way that brightness is preserved. There is a big comment in the code explaining the math behind it. I say “energy-preserving on average” because you can lose some energy on detail finer than the mask, but that’s the case on a real CRT as well.

@ANK I hadn’t seen those videos before, thank you for sharing them!

4 Likes

I have issues with this technique. To me, this is a roundabout way of simply rendering scanlines in linear space and upscaling. The upscaling effect, when done in linear space, will cause the brighter scanlines to expand when gamma correction is applied. It won’t be as dramatic as the technique we see here, but it will be similar and IMO is more related to how the beam on a CRT actually works (240p doesn’t add more energy to the electron beam compared to 480i). It also will not affect the gamma of the signal, so no tonemapping is needed. The overall image won’t be as bright, but most displays should still be bright enough to give an acceptable image.

1 Like

You know many of these topics, ideas and concepts, discussed in the videos were actually pioneered right here on Libretro Forums, with very little fanfare.

One only has to browse this thread, the Sony Megatron Colour Video Monitor thread, many of @Nesguy’s threads, the CyberLab Death To Pixels Shader Presets thread and the OLED Subpixels: How do they work thread and @HunterK’s Filthy Pants blog and you’ll see what I’m referring to.

Summary

Major developments in CRT Shader support for OLED and HDR

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

Sony Megatron Colour Video Monitor

https://forums.libretro.com/t/sony-megatron-colour-video-monitor/36109/1258?u=cyber

https://forums.libretro.com/t/plainoldpantss-shader-presets/45498/156?u=cyber

In early 2022 we knew very little about getting WOLED TVs to work properly with subpixel level CRT shaders. At first we were limited to using B&W masks and almost everyone thought the white subpixel was interfering with things and needed to be controlled, turned off or at least better understood.

Fast forward to today and now we know that the white subpixel isn’t active at all during subpixel Accurate CRT Shader rendering:

https://forums.libretro.com/t/sony-megatron-colour-video-monitor/36109/1579?u=cyber

https://forums.libretro.com/t/sony-megatron-colour-video-monitor/36109/2186?u=cyber

Here are some examples of “Energy Redistribution” scanline dynamics from months ago:

This is another method of “Energy Redistribution”.

https://forums.libretro.com/t/guest-advanced-4k-hdr1000-experiments/48725/41?u=cyber

4 Likes

I know. I’ve been using RetroArch and experimenting with shaders for quite a few years now, and I really appreciate all the hardwork that members such as you, @guest.r, @Nesguy, @hunterk, @anikom15, @beans and others put into researching, programming, and advancing the whole field of CRT shaders. I just wanted to share something I found interesting that might help with future developments.

3 Likes

Yes thank you for sharing because I had not seen it and I wasn’t aware of these hardware upscalers. It’s good to expand the knowledge and get a sense of what others are doing outside these forums. The aspects that relate to what @beans is doing with his shader seem physically sound. It may just be the wording that’s questionable (‘shift energy from empty area’); would have to dive into the algorithm to know for sure.

3 Likes

Devil's Crush (USA)-251127-015711

Here’s another font example where the “m” can easily get lost due to blending. I had to turn Artifacts up to 1.0 to make this one legible. I could have gotten it clearer but I didn’t want to go overboard and risk introducing more flickering artifacts in blended areas.

I haven’t noticed any distracting flickering in this game though and with Artifacts at 1.0 it should be very mild in games in which some noise/flicker might be visible in the blended areas.

I must admit it feels kind of weird having sharpness and clarity (especially of fonts) improve as artifacting increases.

1 Like

I suggest you use the version that works best for you, at least for now. Latest version is quite good for Genesis games like Ys 3, Landstalker, Phantasy Star (Stories) and some other games, which use “fonts”. Will see if i can improve it further.

2 Likes

Yeah I tend to just shrug when I see posts like this because it’s stuff we’ve been doing with guest-advanced and other shaders for years, although in a slightly different way. These upscaler folks get excited over the littlest things. :slightly_smiling_face:

edit- it’s always interesting to see a new method, though. Didn’t want to sound dismissive.

3 Likes

I appreciate when things are delivered in a clear and concise manner and Mr. Lottes seemed to have done this well here, however I don’t like when they fail to acknowledge the “litle guys” who are some of the true pioneers and the fact that this is nothing novel then it get’s passed off as the latest or next big thing with some even profiteering off of the knowledge and prior art as well.

We have to always be vigilant as well because while this is all fun and games and about community (at least for now), this segment of the gaming industry is now starting to realize it’s commercial potential.

How would you feel knowing one of your original ideas has now been patented by someone or some company who might have a name or more resources behind them?

Trust me, it’s not a nice feeling when someone does what you couldn’t do and steals your intellectual property then starts to make money off of it and you’re not even mentioned, recognized or paid a cent.

4 Likes

Will do.

Seeing that the implementation in 2025-11-11-r1 worked very well in this scenario especially in the PC-Engine games I’ve tested and the latest version is quite good for the Genesis games you have tested, would it be possible going forward to have the ability to toggle between 2025-11-11-r1 and 2025-11-16-r1 implementations if you can’t find a “one size fits all solution” or maybe have the older implementation active when NTSC Phase Mode 5 is selected but the newer mode active when NTSC Mode 2 or Auto are active or have either (Font Preservation) implementation tied to whichever NTSC modes work best with each one.

Just thinking out loud here, I know you are the Grandmaster of CRT Shader Programming!

On another note, I can send some more PC-Engine font exmples if you wish. I know back in the day I saw some examples of blurry fonts in at least one of the Ys games.

3 Likes

The “Show original image” option is a useful addition to the shader. And the other features I haven’t tested yet look promising. Impressive! @guest.r

4 Likes

I hope you are playing that last game with the bug fix.

2 Likes

Yes with bugfix version 1.1 :grin:

1 Like

Would you mind sharing your shader settings? : )

2 Likes

Sure

-> Snes preset

shaders = "18"
shader0 = "shaders_slang/crt/shaders/guest/advanced/stock.slang"
alias0 = ""
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
filter_linear0 = "false"
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"
alias1 = "StockPass"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
filter_linear1 = "false"
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"
alias2 = "AfterglowPass"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
filter_linear2 = "true"
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"
alias3 = "PrePass0"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
filter_linear3 = "true"
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"
alias4 = "NPass1"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
filter_linear4 = "false"
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"
alias5 = ""
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
filter_linear5 = "true"
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"
alias6 = ""
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
filter_linear6 = "true"
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"
alias7 = "NtscPass"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
filter_linear7 = "true"
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"
alias8 = "PrePass"
wrap_mode8 = "clamp_to_border"
mipmap_input8 = "true"
filter_linear8 = "true"
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"
alias9 = "AvgLumPass"
wrap_mode9 = "clamp_to_border"
mipmap_input9 = "true"
filter_linear9 = "true"
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"
alias10 = "LinearizePass"
wrap_mode10 = "clamp_to_border"
mipmap_input10 = "false"
filter_linear10 = "true"
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"
alias11 = "Pass1"
wrap_mode11 = "clamp_to_border"
mipmap_input11 = "false"
filter_linear11 = "true"
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/hd/gaussian_horizontal.slang"
alias12 = ""
wrap_mode12 = "clamp_to_border"
mipmap_input12 = "false"
filter_linear12 = "true"
float_framebuffer12 = "true"
srgb_framebuffer12 = "false"
scale_type_x12 = "absolute"
scale_x12 = "800"
scale_type_y12 = "source"
scale_y12 = "1.000000"
shader13 = "shaders_slang/crt/shaders/guest/advanced/gaussian_vertical.slang"
alias13 = "GlowPass"
wrap_mode13 = "clamp_to_border"
mipmap_input13 = "false"
filter_linear13 = "true"
float_framebuffer13 = "true"
srgb_framebuffer13 = "false"
scale_type_x13 = "absolute"
scale_x13 = "800"
scale_type_y13 = "absolute"
scale_y13 = "600"
shader14 = "shaders_slang/crt/shaders/guest/hd/bloom_horizontal.slang"
alias14 = ""
wrap_mode14 = "clamp_to_border"
mipmap_input14 = "false"
filter_linear14 = "true"
float_framebuffer14 = "true"
srgb_framebuffer14 = "false"
scale_type_x14 = "absolute"
scale_x14 = "800"
scale_type_y14 = "absolute"
scale_y14 = "600"
shader15 = "shaders_slang/crt/shaders/guest/advanced/bloom_vertical.slang"
alias15 = "BloomPass"
wrap_mode15 = "clamp_to_border"
mipmap_input15 = "false"
filter_linear15 = "true"
float_framebuffer15 = "true"
srgb_framebuffer15 = "false"
scale_type_x15 = "absolute"
scale_x15 = "800"
scale_type_y15 = "absolute"
scale_y15 = "600"
shader16 = "shaders_slang/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang"
alias16 = ""
wrap_mode16 = "clamp_to_border"
mipmap_input16 = "false"
filter_linear16 = "true"
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"
alias17 = ""
wrap_mode17 = "clamp_to_border"
mipmap_input17 = "false"
filter_linear17 = "true"
float_framebuffer17 = "false"
srgb_framebuffer17 = "false"
scale_type_x17 = "viewport"
scale_x17 = "1.000000"
scale_type_y17 = "viewport"
scale_y17 = "1.000000"
bth = "1.000000"
PR = "0.000000"
PG = "0.000000"
PB = "0.000000"
AS = "0.070000"
agsat = "1.000000"
auto_res = "1.000000"
cust_artifacting = "0.000000"
cust_fringing = "0.000000"
ntsc_fields = "1.000000"
ntsc_phase = "3.000000"
ntsc_scale = "1.500000"
ntsc_gamma = "0.250000"
ntsc_taps = "9.000000"
ntsc_charp3 = "10.000000"
ntsc_cscale1 = "2.250000"
ntsc_ring = "1.000000"
GAMMA_INPUT = "2.800000"
gamma_out = "2.200000"
interm = "0.000000"
HSHARPNESS = "1.000000"
SIGMA_HOR = "0.450000"
S_SHARP = "0.000000"
HSHARP = "0.000000"
MAXS = "0.000000"
HARNG = "0.000000"
m_glow = "1.000000"
m_glow_cutoff = "0.000000"
m_glow_low = "5.400000"
m_glow_high = "0.000000"
m_glow_dist = "4.000000"
SIZEH = "50.000000"
SIGMA_H = "15.000000"
SIZEV = "50.000000"
SIGMA_V = "13.125000"
SIZEHB = "1.000000"
SIGMA_HB = "0.575000"
SIZEVB = "1.000000"
SIGMA_VB = "0.250000"
glow = "0.010000"
halation = "0.787000"
hmask1 = "0.000000"
brightboost = "4.700000"
brightboost1 = "3.000000"
gsl = "-1.000000"
scanline1 = "70.000000"
scanline2 = "70.000000"
beam_min = "0.650000"
beam_max = "1.125000"
beam_size = "0.000000"
scans = "0.000000"
scan_falloff = "2.000000"
scangamma = "2.200000"
csize = "0.060000"
shadowMask = "2.000000"
maskstr = "1.000000"
mcut = "1.000000"
masksize = "3.000000"
mask_zoom = "-3.000000"
mask_layout = "1.000000"
maskDark = "0.000000"
maskLight = "1.000000"
mask_gamma = "1.000000"
pr_scan = "0.000000"
textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4"
SamplerLUT1 = "shaders_slang/crt/shaders/guest/advanced/lut/trinitron-lut.png"
SamplerLUT1_mipmap = "false"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT2 = "shaders_slang/crt/shaders/guest/advanced/lut/inv-trinitron-lut.png"
SamplerLUT2_mipmap = "false"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT3 = "shaders_slang/crt/shaders/guest/advanced/lut/nec-lut.png"
SamplerLUT3_mipmap = "false"
SamplerLUT3_linear = "true"
SamplerLUT3_wrap_mode = "clamp_to_border"
SamplerLUT4 = "shaders_slang/crt/shaders/guest/advanced/lut/ntsc-lut.png"
SamplerLUT4_mipmap = "false"
SamplerLUT4_linear = "true"
SamplerLUT4_wrap_mode = "clamp_to_border"
4 Likes

Very nice preset! I’m quite fond of how a simple Mask Zoom adjustment makes it a very nice 1440p option without tweaking other values.

2 Likes

Thanks! :slightly_smiling_face: Yes the crt mask zoom function is very nice, (-4) and (-5) allowed me to obtain the same image quality, so it’s very useful for lower resolution screens.

2 Likes