Crt-Royale Kurozumi Edition Help


Ohhh, I see it now. It’s only visible on my crappy monitor from certain angles.

That said, I don’t know how the shader would cause that. Do any other shaders do it? What about non-kurozumi royale?


As far as I’ve tried, nothing else does this. crt-aperture, crt-geom, crt-lottes, crt-hyllian, etc they don’t do this. (I just deleted my shaders directory and re-downloaded the files but I get the same result).

Regular Royale doesn’t do this (OpenGL, btw)


I’m not sure what exactly causes this but setting Mask - Type to 2 or Mask - Sample Mode to 1 fixes this.


Oh boy that looks weird, i try to replicate that and see what’s causing it. On which Video Driver/Shader version does this happen?


Hi, Kurozumi. I use Driver: gl and GLSL shader inside the presets folder. As NecroPoster said, changing Sample mode to 1 fixes it but I’m not sure why.


So reading through this entire thread, can someone posts the recommended settings for a 4k display with this shader? Or is the current preset good enough? I’m confused if mask_triad_size_desired still needs a value of 3, and the convergence offsets require values of 0.


@Forscythe : The thing is, Mask_triad_size_desired scales with the resolution itself. That’s what makes it a little bit tricky to make it look nice for everyone. So f.e. if you have a resolution of 800x600 a value of 3 would look way coarser as the same value on 1080p. On 1440p a value of 3 would give a again a higher TV line look than on 1080p, that goes on and on with the resolutions.

So the BVMs have a pretty high TV line count (the vertical mask pitch/subpixel lines), about 900 or 1000+ (Consumer TVs of the late 80s / 90s had 480, or later 600 tv lines), which makes a good aperture grille emulation almost impossible until you get pretty high resolutions, in this case 2160p or more.

The standard value of 1 is absolutely fine, even at 4k, but the most accurate one would be a value of 3 at that resolution. Especially on a pretty big 4kTV you would recognize the difference, on a 75inch TV it looks like this:

If you look closely you can see each scanline is made out of tiny little vertical lines, if you had a value of 1, you wouldn’t see it. That’s why i wrote wayback , on less then 4k i would use a value of 1, on 4k or more i would use 3 .

As for the convergence, the values that are in the shader should be pretty accurate. Every BVM i’ve seen, even if it was tweaked and calibrated to death, never had the 100% perfect convergence. The “signature thing” going on the BVMs (the ones i’ve seen; and macro screenshots often tell the same) if something white on 240p is displayed you almost always have a slightly green top and a magenta bottom, even if the convergence otherwise is spot on. The values that are in there will give you that.

It’s the same thing with diffusion and halation, the 1000 line 16:9 one doesn’t have much of it, but if you don’t turn it on at all, it makes the shader look artificial… It’s kinda a bit tricky to make it really “right”, and please everyone.

But in the end, hey, you gotta have fun, so edit it to your liking :slight_smile:

@c9f5fdda06 (and maybe @hunterk ): I tried every renderer with every shaderversion i could. It has to be the combination of OpenGL and GLSL i guess, because if i recall correctly the slang version and the glsl version should be always the same. So if it breaks on slang, it should break on glsl, too.

The thing is, the only renderer i could choose the glsl version is gl, otherwise the shader file would not show, if i want to load it. But on all renderers the slang version works fine, that’s why i’m assuming what i’ve stated before, and why i think there’s something different going on.

I tried it with the regular royale, it’s broken too, you didn’t see it if you tried it, i know, but it seems it’s not the sample mode itself, it’s the halation weight. On regular Royale it’s off (if you turn it on one click, the same bug instantly appears), and what’s really weird about it, the bug scales vertically up and down with the TV lines you choose.

As far as a workaround for now, if i would be stuck on opengl, i would choose the cg version for now, or turn the halation value down to zero on your machine. My problem honestly is, i always try to help, if i can, and i could fix it sometimes or create workaround settings (when glsl and slang didn’t work right at all), but for the really weird stuff, @hunterk was “the hero”.

I’ll run some more tests, but at the moment i guess we search a needle in the haystack



First I wanted to shout out a big thanks to Kurozumi and HunterK for all their great work on this truly beautiful shader. :slight_smile:

Now on to my (very minor) problem… I’m trying to find the CG version of this shader but I’m not having any luck. I’ve downloaded the latest CG shaders update but even when I perform a search for “kurozumi” within the shaders_cg folder under Windows Explorer I can’t find any files related to this. That being said, I came across a separate “Quick explanation of shaders?” thread that HunterK responded to, in which he stated that CG is being depreciated, so is it possible that the CG version has recently been removed?

Thanks, Visor


It’s still in the Cg shaders:


Hi HunterK,

Thanks; I ended up finding it under …\shaders\shaders_cg\cgp. For some reason my Windows 10 File Manager search option failed to find it, even though it had no problem finding the similar files under the shaders_glsl and shaders_slang folders. Very weird.

Do you recommend the best version of this shader to use, or are they all pretty much identical at this point? I have a GTX 1080 Ti so processing power wouldn’t be an issue.

Also, I’ve been trying to get these shaders to work on FBAlpha for rotated games. I’ve ensured that I’m using integer scaling for both axes. The result is very stripy and uneven as per the example in the link below:

Is this working properly for you, or is this a known issue with rotated screens using FBAlpha?

Thanks, Visor


Vertical games always look weird with it for some reason. I’m not really sure why. I use crt-hyllian instead, which can go vertical without looking wrong.

The Cg version has support for curvature, which I could never get working in the slang/GLSL versions, and the the GLSL version has a weird striping issue with one of the mask_sample_mode settings, but otherwise, they should be similar/the same.


Thanks; I’ve tried out the hyllian shader but I’m not a fan of the grid-like look. I’ll see if I can find some documentation or a forum thread somewhere as to how to turn off the grid.


In the shader set PHOSPHOR to 0.0 instead of 1.0, that ought to do it.


Hi torridgristle,

Thanks for the tip, although I can’t seem to get things working. I opened and found that most of the parameters were commented out (at least that’s what I think the hashtag/# in front of each command means?) There was a parameter called “#define PHOSPHOR 1.0” in the file so I changed it to 0.0 and tried running it with and without the hashtag, but it made no difference. Am I missing something?


You don’t have to go in and do it in the shader. You should be able to change it on-the-fly using the runtime parameters menu. if you really want to change it in the shader, though, look for the line that looks like:

#pragma parameter PHOSPHOR "CRT - Phosphor ON/OFF" 1.0 0.0 1.0 1.0

and change that first 1.0 to 0.0, like this:

#pragma parameter PHOSPHOR "CRT - Phosphor ON/OFF" 0.0 0.0 1.0 1.0


Hi HunterK,

Sorry, but how do I access the runtime parameters menu?

Meanwhile, I made the change to the shader file as you demonstrated (at least temporarily until I figure out this runtime paramters stuff) and things are much more to my liking, thank you. :slight_smile:

Also, I discovered under …\shaders\presets\FB Alpha that there were shader preset files for every game, for example dkong.cgp. I was able to set PHOSPHOR to 0.0 in there, but this is of course not very ideal as I don’t want to have to edit this for each individual game. I don’t want to set this for the core either, as I’m using crt-royale-kurozumi for all my horizontally-oriented arcade games.

Thanks, Visor


With a game loaded, go back into the quick menu, then go to shaders > parameters and it will list all of the things you can change from the menu.

If you want to change it for all of the presets you already have, Notepad++‘s find-and-replace function has a “replace in all open files” option, so you could open all of the vertical games’ presets at once and then replace it in all of them at the same time.


Ahhh thank you, that’s a much better way to do things. I think I remember seeing that parameters menu ages ago, but I’ve totally forgotten that it was there. :stuck_out_tongue:

I’ll give that Notepad++ trick a try if I need to as I build up my vertical game presets.

Thanks a bunch!


This shader is amazing! Combining it with the Mdapt shader to blend dither to produce effects only available on composite would produce the best of both worlds, achieving something that a pure RGB setup cannot.

I tried manually stacking the Kurozumi and Mdapt shaders but it does not seem to work, resulting in no shader effects being displayed at all. Does anyone know the right way to combine multiple shaders in a way that doesn’t break the Kurozumi shader?


Here’s a slang preset for it:

# Shader passes need to know details about the image in the mask_texture LUT
# files, so set the following constants in user-cgp-constants.h accordingly:
# 1.) mask_triads_per_tile = (number of horizontal triads in mask texture LUT's)
# 2.) mask_texture_small_size = (texture size of mask*texture_small LUT's)
# 3.) mask_texture_large_size = (texture size of mask*texture_large LUT's)
# 4.) mask_grille_avg_color = (avg. brightness of mask_grille_texture* LUT's, in [0, 1])
# 5.) mask_slot_avg_color = (avg. brightness of mask_slot_texture* LUT's, in [0, 1])
# 6.) mask_shadow_avg_color = (avg. brightness of mask_shadow_texture* LUT's, in [0, 1])
# Shader passes also need to know certain scales set in this .slangp, but their
# compilation model doesn't currently allow the .slangp file to tell them.  Make
# sure to set the following constants in user-cgp-constants.h accordingly too:
# 1.) bloom_approx_scale_x = scale_x2
# 2.) mask_resize_viewport_scale = float2(scale_x6, scale_y5)
# Finally, shader passes need to know the value of geom_max_aspect_ratio used to
# calculate scale_y15 (among other values):
# 1.) geom_max_aspect_ratio = (geom_max_aspect_ratio used to calculate scale_y5)

shaders = "17"

# Set an identifier, filename, and sampling traits for the phosphor mask texture.
# Load an aperture grille, slot mask, and an EDP shadow mask, and load a small
# non-mipmapped version and a large mipmapped version.
# TODO: Test masks in other directories.
textures = "mask_grille_texture_small;mask_grille_texture_large;mask_slot_texture_small;mask_slot_texture_large;mask_shadow_texture_small;mask_shadow_texture_large"
mask_grille_texture_small = "../crt/shaders/crt-royale/TileableLinearApertureGrille15Wide8And5d5SpacingResizeTo64.png"
mask_grille_texture_large = "../crt/shaders/crt-royale/TileableLinearApertureGrille15Wide8And5d5Spacing.png"
mask_slot_texture_small = "../crt/shaders/crt-royale/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacingResizeTo64.png"
mask_slot_texture_large = "../crt/shaders/crt-royale/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacing.png"
mask_shadow_texture_small = "../crt/shaders/crt-royale/TileableLinearShadowMaskEDPResizeTo64.png"
mask_shadow_texture_large = "../crt/shaders/crt-royale/TileableLinearShadowMaskEDP.png"
mask_grille_texture_small_wrap_mode = "repeat"
mask_grille_texture_large_wrap_mode = "repeat"
mask_slot_texture_small_wrap_mode = "repeat"
mask_slot_texture_large_wrap_mode = "repeat"
mask_shadow_texture_small_wrap_mode = "repeat"
mask_shadow_texture_large_wrap_mode = "repeat"
mask_grille_texture_small_linear = "true"
mask_grille_texture_large_linear = "true"
mask_slot_texture_small_linear = "true"
mask_slot_texture_large_linear = "true"
mask_shadow_texture_small_linear = "true"
mask_shadow_texture_large_linear = "true"
mask_grille_texture_small_mipmap = "false"  # Mipmapping causes artifacts with manually resized masks without tex2Dlod
mask_grille_texture_large_mipmap = "true"   # Essential for hardware-resized masks
mask_slot_texture_small_mipmap = "false"    # Mipmapping causes artifacts with manually resized masks without tex2Dlod
mask_slot_texture_large_mipmap = "true"     # Essential for hardware-resized masks
mask_shadow_texture_small_mipmap = "false"  # Mipmapping causes artifacts with manually resized masks without tex2Dlod
mask_shadow_texture_large_mipmap = "true"   # Essential for hardware-resized masks

shader0 = ../dithering/shaders/mdapt/passes/mdapt-pass0.slang
filter_linear0 = false
scale_type0 = source
scale0 = 1.0

shader1 = ../dithering/shaders/mdapt/passes/mdapt-pass1.slang
filter_linear1 = false
scale_type1 = source
scale1 = 1.0

shader2 = ../dithering/shaders/mdapt/passes/mdapt-pass2.slang
filter_linear2 = false
scale_type2 = source
scale2 = 1.0

shader3 = ../dithering/shaders/mdapt/passes/mdapt-pass3.slang
filter_linear3 = false
scale_type3 = source
scale3 = 1.0

shader4 = ../dithering/shaders/mdapt/passes/mdapt-pass4.slang
filter_linear4 = false
scale_type4 = source
scale4 = 1.0

# Pass0: Linearize the input based on CRT gamma and bob interlaced fields.
# (Bobbing ensures we can immediately blur without getting artifacts.)
shader5 = "../crt/shaders/crt-royale/src/crt-royale-first-pass-linearize-crt-gamma-bob-fields.slang"
filter_linear5 = "false"
scale_type5 = "source"
scale5 = "1.0"
srgb_framebuffer5 = "true"

# Pass1: Resample interlaced (and misconverged) scanlines vertically.
# Separating vertical/horizontal scanline sampling is faster: It lets us
# consider more scanlines while calculating weights for fewer pixels, and
# it reduces our samples from vertical*horizontal to vertical+horizontal.
# This has to come right after ORIG_LINEARIZED, because there's no
# "original_source" scale_type we can use later.
shader6 = "../crt/shaders/crt-royale/src/crt-royale-scanlines-vertical-interlacing.slang"
filter_linear6 = "true"
scale_type_x6 = "source"
scale_x6 = "1.0"
scale_type_y6 = "viewport"
scale_y6 = "1.0"
#float_framebuffer6 = "true"
srgb_framebuffer6 = "true"

# Pass2: Do a small resize blur of ORIG_LINEARIZED at an absolute size, and
# account for convergence offsets.  We want to blur a predictable portion of the
# screen to match the phosphor bloom, and absolute scale works best for
# reliable results with a fixed-size bloom.  Picking a scale is tricky:
# a.) 400x305 is a good compromise for the "fake-bloom" version: It's low enough
#     to blur high-res/interlaced sources but high enough that resampling
#     doesn't smear low-res sources too much.
# b.) 320x245 works well for the "real bloom" version: It's 1-1.5% faster, and
#     the only noticeable visual difference is a larger halation spread (which
#     may be a good thing for people who like to crank it up).
# Note the 4:8 aspect ratio assumes the input has cropped geom_overscan (so it's
# *intended* for an ~4:8 aspect ratio).
shader7 = "../crt/shaders/crt-royale/src/crt-royale-bloom-approx.slang"
alias7 = "BLOOM_APPROX"
filter_linear7 = "true"
scale_type7 = "absolute"
scale_x7 = "320"
scale_y7 = "240"
srgb_framebuffer7 = "true"

# Pass3: Vertically blur the input for halation and refractive diffusion.
# Base this on BLOOM_APPROX: This blur should be small and fast, and blurring
# a constant portion of the screen is probably physically correct if the
# viewport resolution is proportional to the simulated CRT size.
shader8 = "../blurs/blur5fast-vertical.slang"
filter_linear8 = "true"
scale_type8 = "source"
scale8 = "1.0"
srgb_framebuffer8 = "true"

# Pass4: Horizontally blur the input for halation and refractive diffusion.
# Note: Using a one-pass 9x19 blur is about 1% slower.
shader9 = "../blurs/blur5fast-horizontal.slang"
alias9 = "HALATION_BLUR"
filter_linear9 = "true"
scale_type9 = "source"
scale9 = "1.0"
srgb_framebuffer9 = "true"

# Pass5: Lanczos-resize the phosphor mask vertically.  Set the absolute
# scale_x15 == mask_texture_small_size.x (see IMPORTANT above).  Larger scales
# will blur, and smaller scales could get nasty.  The vertical size must be
# based on the viewport size and calculated carefully to avoid artifacts later.
# First calculate the minimum number of mask tiles we need to draw.
# Since curvature is computed after the scanline masking pass:
#   num_resized_mask_tiles = 2.0;
# If curvature were computed in the scanline masking pass (it's not):
#   max_mask_texel_border = ~3.5 * (1/3.5 + 4.0*sqrt(2.0) + 0.15 + 1.0);
#   max_mask_tile_border = max_mask_texel_border/
#       (min_resized_phosphor_triad_size * mask_triads_per_tile);
#   num_resized_mask_tiles = max(2.0, 1.5 + max_mask_tile_border * 2.0);
#   At typical values (triad_size >= 2.0, mask_triads_per_tile == 8):
#       num_resized_mask_tiles = ~3.8
# Triad sizes are given in horizontal terms, so we need geom_max_aspect_ratio
# to relate them to vertical resolution.  The widest we expect is:
#   geom_max_aspect_ratio = 4.0/3.5  # Note: Shader passes need to know this!
# The fewer triads we tile across the screen, the larger each triad will be as a
# fraction of the viewport size, and the larger scale_y15 must be to draw a full
# num_resized_mask_tiles.  Therefore, we must decide the smallest number of
# triads we'll guarantee can be displayed on screen.  We'll set this according
# to 3-pixel triads at 768p resolution (the lowest anyone's likely to use):
#   min_allowed_viewport_triads = 768.0*geom_max_aspect_ratio / 3.5 = 341.333333
# Now calculate the viewport scale that ensures we can draw resized_mask_tiles:
#   min_scale_x = resized_mask_tiles * mask_triads_per_tile /
#       min_allowed_viewport_triads
#   scale_y15 = geom_max_aspect_ratio * min_scale_x
#   # Some code might depend on equal scales:
#   scale_x16 = scale_y5
# Given our default geom_max_aspect_ratio and min_allowed_viewport_triads:
#   scale_y15 = 4.0/3.5 * 2.0/(341.33338 / 8.0) = 0.0625
# IMPORTANT: The scales MUST be calculated in this way.  If you wish to change
# geom_max_aspect_ratio, update that constant in user-cgp-constants.h!
shader10 = "../crt/shaders/crt-royale/src/crt-royale-mask-resize-vertical.slang"
filter_linear10 = "true"
scale_type_x10 = "absolute"
scale_x10 = "64"
scale_type_y10 = "viewport"
scale_y10 = "0.0625" # Safe for >= 341.338 horizontal triads at viewport size
#srgb_framebuffer10 = "false" # mask_texture is already assumed linear

# Pass6: Lanczos-resize the phosphor mask horizontally.  scale_x16 = scale_y5.
# TODO: Check again if the shaders actually require equal scales.
shader11 = "../crt/shaders/crt-royale/src/crt-royale-mask-resize-horizontal.slang"
alias11 = "MASK_RESIZE"
filter_linear11 = "false"
scale_type_x11 = "viewport"
scale_x11 = "0.0625"
scale_type_y11 = "source"
scale_y11 = "1.0"
#srgb_framebuffer11 = "false" # mask_texture is already assumed linear

# Pass7: Resample (misconverged) scanlines horizontally, apply halation, and
# apply the phosphor mask.
shader12 = "../crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang"
filter_linear12 = "true" # This could just as easily be nearest neighbor.
scale_type12 = "viewport"
scale12 = "1.0"
#float_framebuffer12 = "true"
srgb_framebuffer12 = "true"

# Pass 8: Compute a brightpass.  This will require reading the final mask.
shader13 = "../crt/shaders/crt-royale/src/crt-royale-brightpass.slang"
alias13 = "BRIGHTPASS"
filter_linear13 = "true" # This could just as easily be nearest neighbor.
scale_type13 = "viewport"
scale13 = "1.0"
srgb_framebuffer13 = "true"

# Pass 9: Blur the brightpass vertically
shader14 = "../crt/shaders/crt-royale/src/crt-royale-bloom-vertical.slang"
filter_linear14 = "true" # This could just as easily be nearest neighbor.
scale_type14 = "source"
scale14 = "1.0"
srgb_framebuffer14 = "true"

# Pass 10: Blur the brightpass horizontally and combine it with the dimpass:
shader15 = "../crt/shaders/crt-royale/src/crt-royale-bloom-horizontal-reconstitute.slang"
filter_linear15 = "true"
scale_type15 = "source"
scale15 = "1.0"
srgb_framebuffer15 = "true"

# Pass 11: Compute curvature/AA:
shader16 = "../crt/shaders/crt-royale/src/crt-royale-geometry-aa-last-pass.slang"
filter_linear16 = "true"
scale_type16 = "viewport"
mipmap_input16 = "true"
texture_wrap_mode16 = "clamp_to_edge"

parameters = "crt_gamma;lcd_gamma;levels_contrast;halation_weight;diffusion_weight;bloom_underestimate_levels;bloom_excess;beam_min_sigma;beam_max_sigma;beam_spot_power;beam_min_shape;beam_max_shape;beam_shape_power;beam_horiz_filter;beam_horiz_sigma;beam_horiz_linear_rgb_weight;convergence_offset_x_r;convergence_offset_x_g;convergence_offset_x_b;convergence_offset_y_r;convergence_offset_y_g;convergence_offset_y_b;mask_type;mask_sample_mode_desired;mask_specify_num_triads;mask_triad_size_desired;mask_num_triads_desired;aa_subpixel_r_offset_x_runtime;aa_subpixel_r_offset_y_runtime;aa_cubic_c;aa_gauss_sigma;geom_mode_runtime;geom_radius;geom_view_dist;geom_tilt_angle_x;geom_tilt_angle_y;geom_aspect_ratio_x;geom_aspect_ratio_y;geom_overscan_x;geom_overscan_y;border_size;border_darkness;border_compress;interlace_bff;interlace_1080i"
crt_gamma = "2.400000"
lcd_gamma = "2.400000"
levels_contrast = "0.740000"
halation_weight = "0.004600"
diffusion_weight = "0.001000"
bloom_underestimate_levels = "0.800000"
bloom_excess = "0.000000"
beam_min_sigma = "0.020000"
beam_max_sigma = "0.200000"
beam_spot_power = "0.370000"
beam_min_shape = "2.000000"
beam_max_shape = "4.000000"
beam_shape_power = "0.250000"
beam_horiz_filter = "0.000000"
beam_horiz_sigma = "0.545000"
beam_horiz_linear_rgb_weight = "1.000000"
convergence_offset_x_r = "-0.050000"
convergence_offset_x_g = "0.000000"
convergence_offset_x_b = "0.000000"
convergence_offset_y_r = "0.100000"
convergence_offset_y_g = "-0.050000"
convergence_offset_y_b = "0.100000"
mask_type = "0.000000"
mask_sample_mode_desired = "0.000000"
mask_specify_num_triads = "0.000000"
mask_triad_size_desired = "1.000000"
mask_num_triads_desired = "900.000000"
aa_subpixel_r_offset_x_runtime = "-0.333333"
aa_subpixel_r_offset_y_runtime = "0.000000"
aa_cubic_c = "0.500000"
aa_gauss_sigma = "0.500000"
geom_mode_runtime = "0.000000"
geom_radius = "3.000000"
geom_view_dist = "2.000000"
geom_tilt_angle_x = "0.000000"
geom_tilt_angle_y = "0.000000"
geom_aspect_ratio_x = "432.000000"
geom_aspect_ratio_y = "329.000000"
geom_overscan_x = "1.000000"
geom_overscan_y = "1.000000"
border_size = "0.005000"
border_darkness = "0.000000"
border_compress = "2.500000"
interlace_bff = "0.000000"
interlace_1080i = "0.000000"

Just copy this into a text file and name it something like “mdapt-royale-kurozumi.slangp” and put it in your ‘presets’ directory.