Please show off what crt shaders can do!

I really have to take my hat off again to guest.r, who programmed the shader, and explicit the Trinitron mask (and surely to HSM for the great bezel reflection).

The Trinitron mask @ 4K in combination with the mask strength setting @ 0.95 and the Trinitron Low Strength mcut setting @ aslo 0.95 make it virtually indistinguishable to the real stuff.

In my Google searchings for pictures, displaying games on a real Trinitron CRT with the Aperture Grille mask, this shader comes very close.

This is a photograph of an actual Sony Trinitron TV:

And here I made a photo of my 4K OLED displaying the same game sequence of Final Fight:

If I compare this to the shaders I used before and which almost only recreated the scanlines, the difference is huge. The mask is the most important of the shader and guest.r did an amazing job on this! And the good thing is, that the shader does not make the image too dim.

With the shader, my OLED @ 50% brightness achieves a 100 cd/m² / nits White Full field pattern ingame in the service menu of an arcade game according to my Luxmeter, which is the same as a real CRT.

I can recommend anyone who loves Retro Games to buy a 4K Monitor or TV. With a 1080p display or even 1440p, you just can’t resemble the CRT masks properly.


These are cellphone camera photos of my 4K IPS TV using my CyberLab Mega Bezel Death To Pixels (Arcade - Sharp PVM Edition) preset!

Hats off to @guest.r and @HyperspaceMadness !!!


I will fine tune every single setting in the shader, till it is close to a Sony CRT as it can get. It may take some weeks, due to the sheer amount of settings though :blush:

There is one thing I still have a “problem” with. If I use the shader with 3D emulators like Duckstation for Playstation etc. I always like to bump up the internal resolution to 2x or 3x.

If I do this, then the Trinitron mask is no longer as fine, as in 240p lowres stuff. Is there any solution how I can play upscaled 3D Games with the same fine Trinitron mask as in 240p lowres ?

I also tried the setting “Downscale to native Resolution” in Duckstation, as it first upscales the image to 2x for example and then downscales it again to 240p. The Trinitron mask then is fine again, but I cannot see the jump in Resolution like in Upscaling without the Downscaling function.

1 Like

If you do a 2x upscale, you can try setting the Opposite Dir Multiplier (Y Downsample for H Scanline) setting to 50 (default is 100). It’s under [CORE RES RESAMPLING].

You can also try using ScaleFX in addition to or instead of the upscaling built into the core but you might have to tweak your Scanline Dir Multiplier (X-Prescale for H Scanline) setting.

You might also have to tweak your CRT Mask Type and Mask Size settings to be able to see a similar Trinitron effect at the upscaled resolutions. If the mask is lost due to the upscale you might have to increase the Mask Size to bring it back.

1 Like

Thank you Cyber, I will try it out !

1 Like

Hi, here’s my WIP glowy and soft crt-guest-advanced. Still gotta work on those scanlines, I think.


in duckstation I like to upscale 4x and then use the downsampling function so the output is going back to be usable with crt shaders and the 3d models look much better than simply using native res… give it a try!

for me, unfortunately, HSM bezels dont run with duckstation (it always crash) so I use “simple” versions of shaders without the bezel and refeltions :frowning:

1 Like

If you can post a log in the Mega Bezel thread, maybe we can see what is causing this.:thinking:

Have you tried with no internal res scaling? (Because it doesn’t crash for me.)

1 Like

This is definitely the way to do it if possible :smiley:.

When higher internal res upscaling is used and no Downsample is used in the core the image passed to the shader system is too big for a good crt effect and also too big for the shader chain & GPU :grimacing:

If you don’t have the Downsample option in the core then you should try the DEREZ-480p preset which will bring the high res core output down to 480p before it goes through the shader pipeline which should look good with a crt shader.


Nice work.

At 1080p you can use mask 0 for a fairly accurate representation of an aperture grille, but it’s 540 TVL at that resolution, so it’s closer to a PVM than a consumer Trinitron. Check out my recent shots, that’s about as accurate as it gets at 1080p IMO.

If you want shadow mask instead of aperture grille then you want 4K at a minimum and probably 8K, and the screen size should be small enough that the pixels are invisible to the naked eye even when just inches away from the screen.

At 4K you should use one of the 4K masks if you want the lcd subpixels to correspond to crt RGB phosphors. The masks get a bit weird at this resolution in order to maintain regular subpixel spacing, so don’t freak out. They might look a bit weird in screenshots but it’s what it looks like IRL that really matters.

IMO, after playing with mask strength 100% for a while, anything less than 100% just muddies the image. The subpixel detail and beam dynamics are incredible at 100% strength, it literally allows for finer details than what is normally possible at 1080p.


So, I do not remember why but I had duckstation running with d3d11, I switched to Vulkan and all is working now: 5x internal res + box downsample using basic reflect (night) preset, I tried standard but the framerates locks at 30 (if fast-forward goes well over 60), standard HD works but I get some hic-ups. I will stick with the basic reflect witch is good for me and have all the customizations I need.


I had the same problem with Duckstation crashing and also realized, that under the settings I had to switch to Vulkan too. Good, that we figured it out, as Duckstation is the best PSX Emulator out there.


Sometime in the coming weeks, the newest version of the shader will be released. New magic by our friendly neighborhood shader magician, @HyperspaceMadness, will enable the Standard to perform even better than the Basic with Reflection. (Which will actually be removed from the hierarchy.)


I don’t know why I mess with mame_hsl. It’s real odd to deal with but it has a look I like; A sort of 3D beaded feel that feels right on MSDOS and Arcade game. I finally got the colors and contrast to not be crushed.


I feel like that mame shader is underrated, I believe it has untapped potential just needs to be tweaked the right way. I just wish the bloom settings was still there


I too wish it had bloom and I can’t figure out a combo of shaders to make it happen.


I just love the Shock Troopers 2 screen, would you mind sharing the settings?

1 Like

the old mame_hlsl shader with bloom passes intact still exists, it’s just buried down in shaders_slang/crt/shaders/mame_hlsl/shaders/old and has a couple of bad paths (fixed in a PR that’s awaiting merge in the main repo).

I think it’s harder to get good results out of that old one, though.


Sure. This is for a 1080 screen. The composite pass is an unneeded extra. Screen Offset Y is .001 but shows up as 0 in retroarch. (The offset helps give that 3D pill look.)

shaders = "13"
shader0 = "shaders_slang/misc/grade.slang"
filter_linear0 = "true"
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/crtsim/composite.slang"
filter_linear1 = "true"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = ""
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/mame_hlsl/shaders/mame_ntsc_encode.slang"
filter_linear2 = "true"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = ""
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/mame_hlsl/shaders/mame_ntsc_decode.slang"
filter_linear3 = "true"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = "NTSCPass"
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/mame_hlsl/shaders/mame_color.slang"
filter_linear4 = "true"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
alias4 = "ColorPass"
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/mame_hlsl/shaders/mame_prescale.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = "PrescalePass"
float_framebuffer5 = "false"
srgb_framebuffer5 = "false"
scale_type_x5 = "viewport"
scale_x5 = "1.000000"
scale_type_y5 = "viewport"
scale_y5 = "1.000000"
shader6 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_deconverge.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = "DeconvergePass"
float_framebuffer6 = "false"
srgb_framebuffer6 = "false"
scale_type_x6 = "viewport"
scale_x6 = "1.000000"
scale_type_y6 = "viewport"
scale_y6 = "1.000000"
shader7 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_scanline.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = "ScanlinePass"
float_framebuffer7 = "false"
srgb_framebuffer7 = "false"
scale_type_x7 = "viewport"
scale_x7 = "1.000000"
scale_type_y7 = "viewport"
scale_y7 = "1.000000"
shader8 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_defocus.slang"
filter_linear8 = "true"
wrap_mode8 = "clamp_to_border"
mipmap_input8 = "false"
alias8 = "FocusPass"
float_framebuffer8 = "false"
srgb_framebuffer8 = "false"
scale_type_x8 = "viewport"
scale_x8 = "1.000000"
scale_type_y8 = "viewport"
scale_y8 = "1.000000"
shader9 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_post.slang"
filter_linear9 = "true"
wrap_mode9 = "clamp_to_border"
mipmap_input9 = "false"
alias9 = "PostPass"
float_framebuffer9 = "false"
srgb_framebuffer9 = "false"
scale_type_x9 = "viewport"
scale_x9 = "1.000000"
scale_type_y9 = "viewport"
scale_y9 = "1.000000"
shader10 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_phosphor.slang"
filter_linear10 = "true"
wrap_mode10 = "clamp_to_border"
mipmap_input10 = "false"
alias10 = "PhosphorPass"
float_framebuffer10 = "false"
srgb_framebuffer10 = "false"
scale_type_x10 = "viewport"
scale_x10 = "1.000000"
scale_type_y10 = "viewport"
scale_y10 = "1.000000"
shader11 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_chroma.slang"
filter_linear11 = "true"
wrap_mode11 = "clamp_to_border"
mipmap_input11 = "false"
alias11 = "ChromaPass"
float_framebuffer11 = "false"
srgb_framebuffer11 = "false"
scale_type_x11 = "viewport"
scale_x11 = "1.000000"
scale_type_y11 = "viewport"
scale_y11 = "1.000000"
shader12 = "shaders_slang/crt/shaders/mame_hlsl/shaders/mame_distortion.slang"
filter_linear12 = "true"
wrap_mode12 = "clamp_to_border"
mipmap_input12 = "false"
alias12 = "DistortionPass"
float_framebuffer12 = "false"
srgb_framebuffer12 = "false"
scale_type_x12 = "viewport"
scale_x12 = "1.000000"
scale_type_y12 = "viewport"
scale_y12 = "1.000000"
g_gamma_out = "2.300000"
g_crtgamut = "0.000000"
g_vignette = "0.000000"
g_cntrst = "-0.050000"
g_mid = "0.000000"
g_satr = "0.200000"
LUT_Size1 = "32.000000"
LUT_Size2 = "32.000000"
Tuning_Sharp = "0.100000"
Tuning_Persistence_R = "0.000000"
Tuning_Persistence_G = "0.000000"
Tuning_Persistence_B = "0.000000"
Tuning_Bleed = "0.000000"
Tuning_Artifacts = "0.000000"
NTSCLerp = "0.000000"
chromatoggle = "1.000000"
phosphortoggle = "1.000000"
screenoffset_y = "0.001000"
shadowcount_x = "6.000000"
mask_offset_y = "0.054000"
ccvalue = "3.579545"
scantime = "88.999428"
notch_width = "2.500000"
yfreqresponse = "5.199997"
col_rr = "0.750000"
col_gg = "0.850000"
col_bb = "0.550000"
converge_x_r = "1.500000"
converge_x_b = "-1.500000"
converge_y_r = "1.250000"
converge_y_b = "-1.250000"
scanlinealpha = "0.600000"
scanlineheight = "0.000000"
scanlinevariation = "4.000000"
scanlinebrightscale = "3.000000"
scanlinebrightoffset = "0.000000"
defocus_x = "0.200000"
defocus_y = "0.500000"
ygain_r = "0.502600"
ygain_g = "1.000000"
ygain_b = "0.282200"
chromaa_x = "0.570000"
chromaa_y = "0.330000"
chromac_x = "0.135000"
chromac_y = "0.080000"
distort_corner_amount = "0.000000"
noise_amt = "0.500000"
textures = "MaskTexture;NTSCArtifactSampler;shadowMaskSampler;SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4"
MaskTexture = "shaders_slang/crt/shaders/mame_hlsl/resources/aperture-grille.png"
MaskTexture_wrap_mode = "clamp_to_border"
MaskTexture_mipmap = "false"
NTSCArtifactSampler = "shaders_slang/crt/shaders/crtsim/artifacts.png"
NTSCArtifactSampler_linear = "true"
NTSCArtifactSampler_wrap_mode = "clamp_to_border"
NTSCArtifactSampler_mipmap = "false"
shadowMaskSampler = "shaders_slang/crt/shaders/crtsim/mask.png"
shadowMaskSampler_linear = "true"
shadowMaskSampler_wrap_mode = "clamp_to_border"
shadowMaskSampler_mipmap = "false"
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"

Thank you for the info. I’ll check it out but mame_hlsl is already annoying enough I don’t think I want to mess around with its older brother. I’d be curious what all those color options are supposed to emulate.

1 Like