Yet, another CRT Shader

I understand the pb. Still, for my own use (in nes games) I’ll keep an alternate version with only line 140 modified to : float3 c13 = tex2D(s_p, tc + 1.5*dx).xyz; With this I don’t see too much loss of quality (even with a value of 1.2), and the ring artifact is less visible that way.

But then again, maybe I should stop starring at the screen so closely ! At any rate, thanks for having a look at it.

New version released. I’ve implemented horizontal dilation and put it as an optional (ON by default).

Comparison (click here if you want to see a better comparison -> http://screenshotcomparison.com/comparison/72290 ): crt-hyllian without dilation:

crt-hyllian with dilation:

In the repo: https://github.com/libretro/common-shad … hyllian.cg

Pastebin: http://pastebin.com/PeE9qtrd

Not bad. :slight_smile: Perhaps it’s a bit too strong, I can see some pixels almost disappearing (like on Balrog, er… Vega, er… the spanish guy trouser in sf2ce!).

Of course I compare it with CGWG. It seems to my eyes his shader makes brighter pixels larger by a half while doing a softering touch in the transition with the next pixel. And it’s a bit larger on the darker pixels (or is it the lanczo “circling”?) while comparing with yours.

Well it’s hard to say and some real CRT could do a bit of both! The easiest difference to catch is on fonts: your shader stays crisp wile CGWG is fuzzier on the edges (perhaps more like a CRT?).

I can assure you no pixel will disappear. :stuck_out_tongue:

I confess I didn’t recalibrate too much the parameters. Maybe a better calibration might enhance the visuals to your taste.

Looks really good! It really brightens things up.

A small update. Now you can control the level of dilation by changing a param.


// Control the dilation intensity by changing this param below when DILATION is enabled. Use always values between 0.0 and 1.0.
#define DILATION_STRENGTH      0.9

I have used 0.9, as I thought 1.0 was too much. 0.0 means no dilation at all.

Here: http://pastebin.com/bxz4zvr2

Nice. It’s just different than CGWG. 1.0 can be stronger for some games (sf2ce) and then it’s OK for another one (super mario world).

regarding bloom. hmm, it’s kinda difficult to implement it within the retroarch limitations. it seems the usual way to do it is to bright-pass the image, apply gaussian blur with several kernel sizes, additively blend them all and then add it onto the original image. I tried to implement it (gaussian blur from crt-halation), though it’s inconvenient to combine it with other shaders. my bloom atempt uses 5 passes (bright-pass, gaussVert, gaussHori, dilation, combine), maybe someone can have a look it and see how to combine it with hyllians crt-shader. here is a screenshot example (2 bad sreenshotcomparison is down): https://imgur.com/a/ncHkU

download: https://anonfiles.com/file/c497a1f21e79 … b3a95cec38

Sp00kyFox: Wow awesome, can’t it be done within a single shader? In my opinion the bloom should be broader, affecting almost the whole image (not only highlights) but fainter effect so nothing is overexposed. The CRT pass would then tone the brightening down a bit as it is already doing, currently I am using an output gamma of 3.0, so with bloom this wouldn’t be much needed.

Here a testy (kinda adding a dreamy effect):

yeah… that’s why I mentioned it’s kinda hard to implement. every gaussian blur needs two passes to (verical & horizontal) to be performant. and to get a proper broader effect one would need to do several gaussian blurs like I explained in my previous post. but this way you have over a dozen shader passes in no time (what is the actual limit of retroarch??). if you are interested, this is the tutorial I used: http://kalogirou.net/2006/05/20/how-to- … rendering/

like I said I only used one gaussian blur. I don’t really see a way to do it in one pass without gettiing pretty bad performance. I guess one could try it to really confirm it. maybe if I’m bored sometimes. or are there any other known good approximations for the bloom effect?

ps: play around with the str effect in pass4 und the exponent of the pow function in pass0 (which I set to 10.0):

return E * pow(smoothstep(0.0, 1.0, dot(E, y_weights)), 10.0);

Good addition, Sp00kyFox. As I thought, in one pass it would be prohibitive. So, I’m glad you spare me from putting it and bloat the crt shader. :stuck_out_tongue:

It must be a separate implementation.

By the way, how this compares to the other Bloom shader already available in mudlord’s folder?

I don’t do any programming, but can’t you use a blurry scaler and skip the blur pass? I don’t think you need a very blurry image, when I said broader I meant that the bright-pass should encompass more values like from 92 to 255, and then apply only 50% of the effect (as to prevent overexposure). You can resize down using a gaussian kernel (instead of bicubic) to x2 or x4 scale factor.

mudlords version is kinda it’s “own thing”. it seems it also bright passes the image but then adds the result with several different offsets to the original image, you can clearly see the outlines.

you really should learn to read a little bit of code. not understanding it but giving tipps how to do it doesn’t really work ^^ blur pass or blurry scaler is the same. and the bright pass is already including all values. what it does is pretty much this here:

brightpass_pixel = pixel * luminance(pixel)^exp; (in my code exp is 10.0)

which leaves bright pixels bright but makes dark ones even darker. like I said you can lower exp if you want. the strength of the bloom effect is determined in pass4 with str. if you want it at 50% set it to 0.5.

I’m sorry if you felt bothered by that, depending on what you use scaling and blurring might be different tools and comprehend different methods that would gain you some speed or not. I would have sweared you wrote “scale down, blur, etc” before editing the post? in which case you made it look as different calls.

What you wrote seems pretty much favouring highlight burns. I think that no exponential is enough, but I can’t really test since your shader doesn’t work here.

I don’t think the brightpass step is necessary and you can just blur the whole frame instead, since It shouldn’t really be only the bright lines that bloom out, but any line that is brighter than its neighbors, right?

at the moment there aren’t any scanlines anway, I wrote it to be standalone. so withouth the brightpass the whole image just gets brighter in the end (you could transfer the brightpass in the first gauss pass though and save one pass). with scanlines I imagined I’d calculate the bloom based on the original image and add that on top of the crt image. well, how to combine it properly is another story. it’s pretty inconvient to use it with other shaders since you have to modify the following passes accordingly. didn’t tried it with crt-hyllian yet. maybe you can help me out here? I’d like to see other approaches and implementations :wink:

Ah, yeah, that makes sense about the scanlines.

Sure, I’m happy to help with combining. That’s one area where byuu’s ‘quark’ format is nice: the previous pass access is relative instead of absolute, so you can say ‘three passes before this one’ and add another effect before that without breaking anything.

Added Cubic filter configuration:


// Some known filters use these values:

//    B = 0.0, C = 0.0  =>  Hermite cubic filter.
//    B = 1.0, C = 0.0  =>  Cubic B-Spline filter.
//    B = 0.0, C = 0.5  =>  Catmull-Rom Spline filter. This is the default used in this shader.
//    B = C = 1.0/3.0   =>  Mitchell-Netravali cubic filter.
//    B = 0.3782, C = 0.3109  =>  Robidoux filter.
//    B = 0.2620, C = 0.3690  =>  Robidoux Sharp filter.
//    B = 0.36, C = 0.28  =>  My best config for ringing elimination in pixel art (Hyllian).


// For more info, see: http://www.imagemagick.org/Usage/img_diagrams/cubic_survey.gif

// Change these params to configure the horizontal filter.
const static float  B =  0.0; 
const static float  C =  0.5;  

const static float4x4 invX = float4x4(
           (-B - 6.0*C)/6.0,         (3.0*B + 12.0*C)/6.0,     (-3.0*B - 6.0*C)/6.0,             B/6.0,
  (12.0 - 9.0*B - 6.0*C)/6.0, (-18.0 + 12.0*B + 6.0*C)/6.0,                      0.0, (6.0 - 2.0*B)/6.0,
-(12.0 - 9.0*B - 6.0*C)/6.0, (18.0 - 15.0*B - 12.0*C)/6.0,      (3.0*B + 6.0*C)/6.0,             B/6.0,
            (B + 6.0*C)/6.0,                           -C,                      0.0,               0.0);

Link: https://raw.githubusercontent.com/libre … hyllian.cg

Found my B and C values using desmos:

https://www.desmos.com/calculator/lcueniurwu

Great addition. I think I will use 0.5,0.0, it’s a bit softer but has no ringing and well, crt was never supposed to be pixel sharp.

If you are interested on ringing cancelation it is being handled on many fronts, madvr or even dither on avisynth, the snippet of the dither workaround is something along these lines:

main=resize (width, height....)
nrng=resize (width, height,kernel ="gauss", a1=100)

main.repair (nrng)
merge (main, last)

Now if this is fast or slow on your programming language I don’t know, hope it helps.

edit: cool link desmos, I normally use catmul-rom on my encodes it gives great quality with very low hit on performance.

Here a comparison with “no ring” workaround from Leos from previous page v14, and current with bicubic b=0.5,c=0.0 v15: *note, the scanline mergings are fixed for integer true, which I missed to set on these screenshots.

A good rule of thumb is this: C = (1 - B)/2 or, the other way, B = 1 - 2C

So, in your case, I would increase C to 0.25. Using C = 0 will make your output suffer from blocking artifacts.

See this screenshot for a better understanding: http://www.imagemagick.org/Usage/img_di … survey.gif

Another tip: disable phosphor when calibrating, because it masks some artifacts. Enable phosphor only when you are satisfied with the cubic filter.