CRT-Yah! A new shader chain

Hi there,

I hope it’s fine when I use my first post to present a new shader chain, that I submitted a few days ago. With so many great CRT shaders already available, the “market” seems quite saturated. However, I hope that it will still bring something new to the table.

CRT-Yah! - pronounced like the German Ja! (Yes!) and an acronym for “Yet Another Hyllian” - started as a small personal modification of the original CRT-Hyllian about three years ago. My goal was not really clear from the beginning and I stopped working on it several times.

In the end, the result was a shader chain with many additional features, but as few settings as possible, without being limited to a certain look.Another important aspect is that most effects are automatically scaled depending on the input and output resolution, also their orientation is adjusted automatically.

It goes without saying that this shader chain would not have been possible without the previous work of Hyllian, The Maister, Hunter K. and others. Credits are given in the individual files. If you spots an oversight, please let me know.

Features:

  • Scanlines (of course): In its core still based on the original interpolation method, but the parametrization has been changed. One noticeable change is the continuous variable interpolation between Box, Hermite, Catmull-Rom, Mitchell-Netravali and “quasi” B-Spline filter.

  • Mask: A high-resolution procedural generated mask for Aperture Grill, Slot Mask and Shadow Mask, with various sub-pixel variants. The mask can be applied additive or multiplicative.

  • Color Adjustments: Simple Saturation, Contrast, Brightness settings and some that let you change small nuances, like Color Overflow, Color Burn (for Scanlines) and Color Bleed (for Mask).

    One other important setting is the Brightness Compensation, which approximates the brightness change caused by different mask and scanlines settings.

  • Color Profile*: This is based on the LUT shader by Hunter K. and you can switch continuous between NTSC, Normal and Trinitron.

  • Color Temperature: Switch continuous between the white points D55 (warm), D65 (normal) and D75 (cold).

  • Color Deconverge*: This is based on the MAME implementation, with simplified settings for linear and radial deconverge. The direction of the linear deconverge is fixed in direction of the scanlines with red and blue diverging into opposite directions while green stays unchanged.

  • Phosphor Burn-in*: This is based on an implementation by Westley M. Martinez, where you can configure the amount and the decay of the burn-in effect.

  • Halation*: This effect uses the blur shader code by Hunter K. and you can configure the blur amount and diffusion.

  • NTSC Effect*: This is based on the NTSC-Adaptive shader by Hunter K. which itself was based in the NTSC implementation by The Maister. My modification supports both vertical and horizontal orientation within the same code base, which allows to switch orientation on-the-fly. The settings are striped down to a profile (Separate Y/C, Composite and Radio Frequency) and the quality (Two Phase, Three Phase, + Field Merge).

  • Other Effects: Curvature, Vignetting, Noise, Corner Radius/Blur, Scanlines Offset/Jitter

You might have noticed that some features are marked with a star. There is also a single-pass variant of the shader in which those effects are missing.

Screenshots:

The shader chain comes with four presets.

The overall brightness of all presets is quit similar due to the brightness compensation. Here is another direct comparison for the “pure” preset, which is especially dark when turned off.

As mentioned above, the scaling of effects is done automatically, but you can adjust the scaling manually as well. This example shows how the “pure” preset will look in different screen resolution.

Here is another example, where the scaling setting has been manually reduced to fit the low-resolution.

In this example the high-resolution input has been automatically down-scaled and then again manually up-up-scaled.

Yet another example, that uses the high-resolution mode of the SNES to display a semitransparent UI.

This example uses the same default setting, but the orientation of the effects like scanlines has been automatically adjusted.

The following shots demonstrate the procedural generated masks with the “pure” preset. (open in new tab)

And here are some other screenshots with the default “medium” preset. Enjoy!

20 Likes

Hi. Nice crt addition! Congrats! The user params list is very organized and has some very interesting new ways of tweakings.

I don’t know if it’s just me, but I couldn’t run the shaders on Retroarch 1.19.1. It works on latest 1.21.0, though, so I think it uses some new feature or it benefits from some fixed bug in latest RA. I noticed some of your parameters have some unknown chars in Windows systems, maybe this is what’s causing incompatibility with older RA versions, which throws some semantic errors in my logs.

Example of unknown char in “parameters-single-pass.h”:

2 Likes

Thanks for the notice, it seems to be an issue with some unicode characters.

I used RA 1.16 for quite some time under Windows and didn’t notice any issues. But maybe this problem was added after I switched to 1.21.

I’ll take a look and fix it.

3 Likes

Based on my tests, the unicode characters in the parameters file were not the problem.

The used FrameTimeDelta uniform raised a rather cryptic compile error. A fix is on the way.

2 Likes

There is an update available which fixes a compile error when using DX11. Thanks to @joepogo for reporting this issue.

3 Likes

@Jezze I just discovered crt_yah and it is phenomenal. In motion, it is definitely one of those rare shaders that give me real, high end crt vibes.

A little review off the top of my head:

Amazing:

-Image quality and CRT “feel” are top notch. Up there with the heavyweights like Guest, Hyllian and Royale.

-Automatic scaling and rotation based on input source. This is huge for shmup zealots like myself.

-Manual scaling setting allows full control over vertical resolution.

-Moiré and other artifacts very well controlled, even in non integer scenarios.

-Settings neatly organized, and easy to access.

-Brightness compensation works beautifully.

-Good performance, similar to guest-advanced-fast.

Observations:

-When other shaders are added to the single pass version, the (lovely btw, more shaders need it) scanline jitter effect stops working, and it doesn’t work at all with the multipass version (which instead displays a loss of vertical sharpness at the default setting of -0.25). Edit: as of nov-25, this is only a problem with d3d, at least on my AMD computer. Works as intended with vulkan and glcore. Further testing from nVidia/Intel users needed.

-Black levels appear slightly higher than with other shaders in certain circumstances due to the way the chain works, since they are linked to mask/scanline and noise values, to prevent 100% black beams. Updated versions allow users to choose between this black level system or a more traditional one.

-Default contrast setting is aggressive. Toning it down recommended.

:drooling_face:

Seriously people. Do yourselves a favour and take crt_yah for a spin.

5 Likes

Yeah i checked it some time ago and it’s a brilliant shader, only problem is when i enable composite on my snapdragon 865, it totally kneels. That’s an extremely heavy implementation, as sd865 supposed to be an emulation powerhouse. It even runs Guest.r-advanced full version well. Probably doing a trillion of passes to draw sd865 to extinction lol

3 Likes

It is brilliant indeed! I guess it got lost in the arcane list of crt shaders and the author decided to not push it much. Which is a real shame.

And yeah, more demanding than g-adv-full sounds pretty heavy :O. On my computer (Ryzen 7 7735HS) it runs almost exactly like g-adv-fast though according to a quick test, which is fine I think, and in line with what I expect from a modern CRT shader running at 1440p.

1 Like

Thanks for the praise and the valuable criticism. It’s nice to read some more feedback. :smiley:

I didn’t promote my shader chain more, because I’m actually very late to the “game” (well not really if you know my history with MAME) and there are already a lot of great shaders out there. But still I’m quite proud of it, esp. the automatic scaling/rotation of effects, because I don’t know of any other example.

In regards to your feedback:

Jitter: I can take a look into why the jitter stops working when additional passes are added. Can you share your .slangp files for easier reproduction? Btw. do you say the jitter does not work in the multi-pass version at all, also when no additional pass is added?

Gamma / Black Level: Unlike other shaders, it does not really let you control the Gamma. Instead the Gamma is fixed to 2.4 and you have an additional contrast setting. Also the Black Level is controlled by the scanlines strength (and noise amount). The higher these settings the more the Black Level is lifted, to ensure there are no totally black beams. Maybe the Gamma and the Black Level could get their own settings.

Default Settings: I actually like the default settings, but I guess everyone has its own taste. :wink: I would be glad to look at your settings and tweak the defaults or add them as a separate preset. Maybe you can elaborate a bit on the “misconfigurations”.

Composite: My variant of the adaptive NTSC shader can be quite demanding on performance, because it up-scales the source by 4x4. The regular adaptive NTSC shaders do only up-scale by 1x4 or 4x1 depending which on you choose. The reason I use 4x4 is to be independent from the screen orientation. So, the same code works for landscape and portrait mode. Maybe I can figure out something to improve it.

4 Likes

Hi there. I do have an updated phosphor shader here:

https://github.com/anikom15/scanline-classic/blob/dev/src/phosphor-luma.slang

It is much more accurate and real-world modeling, based on Yen, Shionoya, Yamamoto’s Phosphor Handbook. However, it’s also more complicated, requiring two shaders, and it’s under GPL instead of BSD-3 (which we used for the MAME shaders) if that is an impediment (I do suggest releasing your shader under GPL if possible).

You can find an example of how it’s supposed to be used here:

https://github.com/anikom15/scanline-classic/blob/dev/trinitron-ntsc.slangp

If you have any questions about its operation, or would like me to simplify it, let me know.

2 Likes

Hi there and thanks for the offer to use your much more advanced version! I really appreciate it and keep it in mind. For now I’ll stick with the older, simpler version. It’s elegant and, in my humble opinion, still looks great.

2 Likes

@anikom15 what would be the difference between this more advanced method and older ones? I will add the shader to my chains to see for myself, but it would be cool to read about it.

Never too late, and your yah is too good to go unnoticed. In fact, I think it’s one of the best, and I’m not exactly new to the game either. I do not know about your history with MAME but I’m not surprised you have one. A shader like yours doesn’t come out of the proverbial blue. And yes, the autoscaling/rotation is a literal godsend, love it. I also really like your approach to manual vertical scaling by the way, I didn’t mention that in my “review”. You can definitely be more than proud of it. Listen, even I feel a bit proud about it myself and the only thing I’ve done is use it and try to give it some well deserved exposure.

Now @Jezze I have done my homework :slight_smile:

First gamma:

I see, it’s an interesting approach, actually! Thing is, maybe someone obsessed with blacks being black (namely myself lol) and who doesn’t mind totally black beams might want to push the CRT effect without seeing gamma shifts in dark areas. A pretty simple solution to make everyone happy could be a switch named “Black Levels”, where 0 = Traditional. 1 = Jezze. Setting it to 0 brings black level to absolute zero, unties it from other settings and enables a gamma slider. 1 obviously uses your method. Default value is 1 :wink:

Yeah I figured out that one of the reasons why the black levels were higher than expected is because noise has a default value of 0.25, and that messes up gamma quite a bit. The more general efffect you apply, the more noise is added, and of course the more black levels suffer. I would suggest leaving the noise feature in there, because it is a very cool effect for certain games, but setting a default value of 0 for it.

Ok, examples. First image is guest-adv in srgb mode, which is an excellent point of reference. Second is a quick gamma correction I applied using yah’s own settings and nothing else. Not perfect but good enough for demonstration purposes. Third is yah’s default values. As you can see midtones are noticeably darker on the third one. It will be even more apparent in a real world scenario as Sonic the Hedgehog will show next.

This time first image is raw, no shaders. Second is again guest-adv srgb. As you can see, colour and gamma are essentially identical. Third is the same gamma correction from before. Gamma is better but colours are off (in this particular case, noticeable mostly in the water). Fourth is yah’s default. Both colours and gamma are off. Strange artifacts show up in the water too.

Now I’m not a programmer so I can’t help with the code, but my background is photography and video, and I have quite a bit of experience when it comes to grading stuff, so I can probably lend a hand in finding out what’s going on.

And finally

Please do! And that’s correct, it doesn’t work with the multipass version. The setting only sort of deconverges the Y axis.

Regarding single pass, here’s a simple chain with color mangler prepended. Jitter not working, deconvergence again. Loss of vertical sharpness can be easily fixed with BEAM_FILTER = “0.000000”, but jitter is lost. (Edit: as stated earlier, this doesn’t happen in Vulkan),

shaders = "2"
feedback_pass = "0"
shader0 = "shaders_slang/misc/shaders/color-mangler.slang"
alias0 = ""
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
shader1 = "shaders_slang/crt/shaders/crt-yah/crt-yah.single-pass.slang"
alias1 = ""
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
filter_linear1 = "true"
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
gamma_boost_r = "0.200000"
gamma_boost_g = "-0.100000"
gamma_boost_b = "-0.100000"
r = "0.900000"
g = "1.050000"
b = "1.050000"
gb = "0.050000"
GLOBAL_MASTER = "1.750000"
COLOR_TEMPERATUE = "-0.200000"
COLOR_SATURATION = "1.400000"
COLOR_CONTRAST = "0.300000"
COLOR_BRIGHTNESS = "0.750000"
COLOR_OVERFLOW = "2.000000"
SCANLINES_STRENGTH = "1.000000"
BEAM_WIDTH_MIN = "0.000000"
BEAM_WIDTH_MAX = "1.500000"
BEAM_SHAPE = "0.750000"
BEAM_FILTER = "-0.250000"
MASK_BLEND = "0.250000"
MASK_COLOR_BLEED = "1.000000"
CRT_CURVATURE_AMOUNT = "0.050000"
CRT_CORNER_RAIDUS = "0.020000"
CRT_CORNER_SMOOTHNESS = "0.500000"

Phew that was quite the post wasn’t it.

1 Like

I recently discovered crt-yah too and I really like it. Just one question, is there a way to sharpen the composite look of the shader?

1 Like

Alright figured it out (single pass). It was much simpler than I thought it would be. The following values, with everything else at yah’s defaults

COLOR_CONTRAST = "-0.250000"
COLOR_BRIGHTNESS = "0.400000"
CRT_NOISE_AMOUNT = "0.000000"

Gave me this image

Which is nearly indistinguishable from the raw reference I posted before in terms of gamma/colour. It was mostly the contrast setting (defaults at 0.50) that was giving me issues.

Now I’m not saying this should be anyone’s final target, by any means! My settings for actually playing will be much more aggressive than that (see second image on this post for reference), and everyone will of course have their own. What I’m trying to provide here (first image) is a natural looking starting point that matches the original characteristics of the source :slight_smile:

My personal preferences hover around something like this (I actually modified the shader parameter file itself a little bit to achieve them, for more mask and more scanlines)

Looks absolutely stunning to me. Yah is a beast

3 Likes

@Dante There is no sharpness setting for the NTSC pass yet.

@Squalo I looked into the jitter issue, but I have to say that it works for me. However, the -0.25 setting is very subtle. Another factor that could contribute to the phenomenon you observed is the frequency of 60 Hz, which maybe too high

Can you check crt-yah.fs.h in line 249 and change the following:

GetUniformFrameCount(60) to GetUniformFrameCount(30)

Hope this gives you better results.

There could also be a bug in GetUniformFrameCount, because I only test it on 60 Hz monitors. GetUniformFrameCount tries to normalize refresh rates, so that the effect always has the same speed regardless of the used monitor. Internally it tries to estimate the frame rate by looking at the FrameTimeDelta, which is error prone. Unfortunately libretro does not provide a global uniform for the monitor refresh rate. (Btw. what is your refresh rate?)

I also looked at the gamma issue. And as you just mentioned it’s the contrast setting that is messing around. I’ll probably expose the gamma setting for the user.

However, I don’t think the noise has too much of an impact on the black level. The Scanlines Strength and Mask Intensity contribute much more. That’s also the reason why you still see the mask in the black parts of Sonic, his pupils and nose.

1 Like

The main difference is that the old method operates in RGB space with one dimension for each phosphor, so the red phosphor can only change color in the red scale, etc.

The new method treats each phosphor in its own 3-dimensional color space, using xy chromaticity to define each phosphor. So as the phosphor decays, it can shift color. This gives us color correction of the raw RGB input to sRGB (or BT.2020 or whatever) for free, in addition to the phosphor effect.

In addition, you can specify single-phosphor and two-phosphor mode. The single-phosphor mode is especially useful for simulating monochrome displays (a simple linear matrix conversion will not give the correct colors across grayscale). The two-phosphor mode allows for some cool effects (e.g. start purple then fade to yellow) but AFAIK was only ever used for industrial applications. There are also more parameters to control the decay time more finely.

2 Likes

@Jezze I promise you, as soon as I prepend something else to the shader, jitter stops working completely and the only effect I get from changing the setting is that Y “deconvergence” I mentioned earlier. My refresh is Free-Sync 100hz, but if that were a factor I wouldn’t be able to see the effect at all right? And it works perfectly fine when yah is doing its thing alone. It only stops when I prepend more stuff. Could it be a driver issue? I will test dx, glcore and vulkan and report back. I will try the GetUniformFrameCount thing right away too.

Could anyone else please give it a go? Lend us a hand here! @Nesguy I’m looking at you, I see you out there haha

As for the gamma, here’s a request that you might not expect: please do not remove your method from the chain. It’s really ingenious and out-of-the-box-cool and the more I understand how it functions the more I like it, especially now that I can make shadows darker by turning noise off. If that switch between traditional/Jezze I suggested earlier is not a possibility, then leave it as it is currently :slight_smile:

Also please bear in mind that my “gamma complaints” have a lot to do with the default settings but very little with the actual black level method itself, as all the images that I posted clearly demonstrate. Particularly the last one, which is extremely faithful to the raw reference and the only really meaningful change is the default contrast value. My point was to change those defaults so that people who start using yah get a more faithful-to-source image right off the bat, and can start tweaking from there.

@anikom15 wow cool stuff :open_mouth: Can it simply be added to a chain so that any crt shader will take advantage or does it need to be implemented by the dev?

1 Like

Currently in bed recovering from ankle surgery, going nuts not able to use my computer. You’ve been busy! Enjoying the posts. I’ve got a few things to test once I’m moving around again.

5 Likes

Aw shit. Wish you a quick, full recovery my man. Yeah been busy indeed, it’s the least that we the powertweakers can do isn’t it. I’m going through all the shaders again at 4K and 1440P (happy to finally leave 720p behind), really enjoying the new computer. And discovering fresh (for me at least) ones like @DariusG 's consumer, @beans 's beans, @Jezze 's yah, @Hyllian 's hyllian 2.0 or that weirdo whkrmrgks0 (which is actually quite interesting). So many toys to play with!

Well you have one more now. Come back soon! :slight_smile:

2 Likes

@Squalo I’m not sure how familiar you are with Git, but I added a “Black Lightening” parameter, with a default value of 1, which maintains the previous behavior. However, it can be lowered to 0 to disable the black level lightening effect caused by scanlines, mask and noise.

Here is the branch with the changes: https://github.com/ImJezze/slang-shaders/tree/crt-yah

Let me know if this is what you had in mind.

1 Like