Please show off what crt shaders can do!

Awesome, I’m excited to try this! I can’t get the shader to load though. Do I need to add an “endif” somewhere?

Shouldn’t have to, no, but it is in slang format (should have mentioned that), and it’s currently not compatible with GLSL at all since it uses newer features than are available there. I just re-pasted the code, though, in case something was wrong with the first paste.

2 Likes

okay, looks like I need to play around with my drivers. Any suggestions for getting the best performance with slang shaders?

edit: can you use hard gpu sync with whatever drivers slang is compatible with?

Yeah, if you switch to glcore, it should be exactly the same as gl, just with slang shaders.

EDIT: before you go too far, though, here it is in regular GLSL format (I already had an idea of what I needed to do to make it work there):

#version 130
#pragma parameter phosphor_layout "Phosphor Layout" 1.0 1.0 3.0 1.0
#pragma parameter mask_strength "Mask Strength" 1.0 0.0 1.0 0.01
#pragma parameter scanmix "Scanline Strength" 1.0 0.0 1.0 0.01

#if defined(VERTEX)

#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying 
#define COMPAT_ATTRIBUTE attribute 
#define COMPAT_TEXTURE texture2D
#endif

#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

COMPAT_ATTRIBUTE vec4 VertexCoord;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 TEX0;

uniform mat4 MVPMatrix;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;

// vertex compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)

void main()
{
    gl_Position = MVPMatrix * VertexCoord;
    TEX0.xy = TexCoord.xy;
}

#elif defined(FRAGMENT)

#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif

#if __VERSION__ >= 130
#define COMPAT_VARYING in
#define COMPAT_TEXTURE texture
out COMPAT_PRECISION vec4 FragColor;
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif

uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
uniform sampler2D Texture;
COMPAT_VARYING vec4 TEX0;

// fragment compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy

#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)

#ifdef PARAMETER_UNIFORM
uniform COMPAT_PRECISION float phosphor_layout, mask_strength, scanmix;
#else
#define phosphor_layout 1.0
#define mask_strength 0.3
#define scanmix 0.3
#endif

vec3 beam(vec3 px, vec2 coord){
   vec3 pixel = pow(px, vec3(2.2));
   vec3 black = vec3(0.,0.,0.);
  
   vec3 beam_profile_1[9] = vec3[](black, black, pixel * 0.12, pixel * 0.5, pixel, pixel * 0.5, pixel * 0.12, black, black);
   vec3 beam_profile_2[9] = vec3[](black, pixel * 0.12, pixel * 0.5, pixel, pixel, pixel, pixel * 0.5, pixel * 0.12, black);
   vec3 beam_profile_3[9] = vec3[](black, pixel * 0.25, pixel * 0.5, pixel, pixel, pixel, pixel * 0.5, pixel * 0.25, black);
   vec3 beam_profile_4[9] = vec3[](pixel * 0.12, pixel * 0.5, pixel, pixel, pixel, pixel, pixel, pixel * 0.5, pixel * 0.12);
   
   int j = int(floor(mod(coord.y * 9.0, 8.9999)));
   int k = int(floor(max(max(px.r, px.g), px.b) * 3.9999));
   
   vec3 array_bool = (k == 3) ? beam_profile_4[j] : (k == 2) ? beam_profile_3[j] : (k == 1) ? beam_profile_2[j] : beam_profile_1[j];
   
   return pow(array_bool, vec3(1./2.2));
}

vec3 mask_weights(vec2 coord, float mask_strength){
   vec3 weights = vec3(0.,0.,0.);
   vec3 green = vec3(1.-mask_strength, 1.0, 1.-mask_strength);
   vec3 magenta = vec3(1.0,1.-mask_strength,1.0);
   vec3 black = vec3(1.-mask_strength,1.-mask_strength,1.-mask_strength);

   // classic aperture
   vec3 aperture_weights = mix(magenta, green, floor(mod(coord.x, 2.0)));

   // 2x2 shadow mask
   vec3 inverse_aperture = mix(green, magenta, floor(mod(coord.x, 2.0)));
   vec3 shadow_weights   = mix(aperture_weights, inverse_aperture, floor(mod(coord.y, 2.0)));

   // slot mask
   // Can't do 2D arrays until version 430, so do this stupid thing instead
   // first lay out the horizontal pixels in arrays
   vec3 slotmask_x1[6] = vec3[](magenta,green,black,magenta,green,black);
   vec3 slotmask_x2[6] = vec3[](magenta,green,black,black,black,black);
   vec3 slotmask_x3[6] = vec3[](magenta,green,black,magenta,green,black);
   vec3 slotmask_x4[6] = vec3[](black,black,black,magenta,green,black);

   // find the horizontal index
   int slot_index_x = int(floor(mod(coord.x, 6.0)));
   int j = slot_index_x; // use a single letter for variable to make comparison easier to read later

   // find the vertical index
   int slot_index_y = int(floor(mod(coord.y, 4.0)));

   // do a big, dumb comparison in place of a 2D array
   vec3 slot_weights = (slot_index_y == 1) ? slotmask_x1[j] : (slot_index_y == 2) ? slotmask_x2[j] : (slot_index_y == 3) ? slotmask_x3[j] : slotmask_x4[j];
   
   if(phosphor_layout == 1.) weights = aperture_weights;
   else if(phosphor_layout == 2.) weights = shadow_weights;
   else weights = slot_weights;
   
   return weights;
}

vec2 quilez(vec2 coord){
	vec2 p = coord.xy;

	p = p * SourceSize.xy + vec2(0.5, 0.5);

	vec2 i = floor(p);
	vec2 f = p - i;
	f = f * f * f * (f * (f * 6.0 - vec2(15.0, 15.0)) + vec2(10.0, 10.0));
	p = i + f;

   p = (p - vec2(0.5, 0.5)) * SourceSize.zw;
   
   return p;
}

void main()
{
   vec3 mask_weights = mask_weights(gl_FragCoord.xy, mask_strength);
	vec3 screen = COMPAT_TEXTURE(Source, quilez(vTexCoord) * 1.0001).rgb;
   vec3 scanlines = beam(screen.rgb, vTexCoord.xy * SourceSize.xy);
   FragColor = vec4(mix(screen, scanlines, scanmix) * mask_weights, 1.0);
} 
#endif
5 Likes

lol :joy: You really are the best. This is fantastic!

1 Like

@hunterk

This is a great proof of concept! I think the aperture grille and dotmask look good even at 4x scale, and I think you can get away with 8x for the slotmask if you’re going for a low TVL look.

A few additions would make this the best shader ever (I know, I’m a greedy bastard lol):

  1. whatever automatic color/brightness/gamma correction CRT-aperture is doing. Looks like mask strength is tied up in a single function with a lot of the other variables…? How is CRT Aperture able to preserve brightness and color so well when using mask/scanlines…?
  2. adjustable sharpness, needs just the slightest blur. Again, I think CRT-aperture is doing a good job here. The lowest setting is still sharper than bilinear filter.
  3. gamma options
  4. adjustable beam width (looks like it’s possible to edit the shader pretty easily for this, though)
  5. additional mask types, as per cgwg’s recommendations. Both of these, like the magenta/green patterns, result in even spacing of the LCD subpixels, but they both need 4K to look good.

-aperture grille #2 Red, Yellow, Cyan, Green

-aperture grille #3 Red, Green, Blue, Black

  1. “switch mask colors” option for those with RBG subpixels. This would swap magenta/green in the magenta/green patterns with yellow/blue
  2. adjustment for mask size, so you can raise/lower the dot pitch/TVL as desired.
1 Like

Guys i’m so proud of this preset :grinning: colors are so vivid with good white intensity and a mask, it’s in slang but can work in glsl by changing all extensions in preset (slang -> glsl)

shaders = "2"
shader0 = "shaders_slang/crt/shaders/crt-aperture.slang"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = ""
float_framebuffer0 = "false"
srgb_framebuffer0 = "false"
scale_type_x0 = "source"
scale_x0 = "9.000000"
scale_type_y0 = "source"
scale_y0 = "9.000000"
shader1 = "shaders_slang/reshade/shaders/blendoverlay/blendoverlay.slang"
filter_linear1 = "true"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = ""
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
parameters = "SHARPNESS_IMAGE;SHARPNESS_EDGES;GLOW_WIDTH;GLOW_HEIGHT;GLOW_HALATION;GLOW_DIFFUSION;MASK_COLORS;MASK_STRENGTH;MASK_SIZE;SCANLINE_SIZE_MIN;SCANLINE_SIZE_MAX;SCANLINE_SHAPE;SCANLINE_OFFSET;GAMMA_INPUT;GAMMA_OUTPUT;BRIGHTNESS;OverlayMix;LUTWidth;LUTHeight"
SHARPNESS_IMAGE = "1.000000"
SHARPNESS_EDGES = "3.000000"
GLOW_WIDTH = "0.650000"
GLOW_HEIGHT = "0.650000"
GLOW_HALATION = "0.350000"
GLOW_DIFFUSION = "0.000000"
MASK_COLORS = "2.000000"
MASK_STRENGTH = "0.000000"
MASK_SIZE = "1.000000"
SCANLINE_SIZE_MIN = "0.500000"
SCANLINE_SIZE_MAX = "1.500000"
SCANLINE_SHAPE = "1.000000"
SCANLINE_OFFSET = "0.000000"
GAMMA_INPUT = "2.500000"
GAMMA_OUTPUT = "2.200000"
BRIGHTNESS = "2.000000"
OverlayMix = "1.000000"
LUTWidth = "4.000000"
LUTHeight = "1.000000"
textures = "overlay"
overlay = "shaders_slang/reshade/shaders/blendoverlay/delta_1_4x1_rgb.png"
overlay_wrap_mode = "clamp_to_border"
overlay_mipmap = "false"

Edit: High quality screenshots

1.https://pasteboard.co/IKPgRQV.png

2.https://pasteboard.co/IKPh5ez.png

3.https://pasteboard.co/IKPhpe0.png

4.https://pasteboard.co/IKPhFWl.png

5.https://pasteboard.co/IKPi0AC.png

6.https://pasteboard.co/IKPiaSt.png

7.https://pasteboard.co/IKPipfq.png

8.https://pasteboard.co/IKPiAQB.png

9.https://pasteboard.co/IKPiNRa.png

10.https://pasteboard.co/IKPj4ri.png

11.https://pasteboard.co/IKPjiGH.png

6 Likes

looks awesome, but the scanlines appear to be inconsistent; are you using integer scale? There’s only so much you can do if you’re not using integer scale. Looks great aside from that, very BVM / Royale-like.

1 Like

Thanks , yes i’m using integer scaling but everything is compressed ,mask missing ,uneven scanlines bad scaling haha ,i’m gonna reupload everything on pasteboard with no loss. It’s like day and night haha

Edit: screenshots added

2 Likes

That’s what the issue with mask was, lol. I was looking at the shader chain and saw the blendoverlay but didn’t see the mask and was confused :joy:.

1 Like

Yeah that’s why i always test shader when someone post a preset,it never looks like the screenshot :sweat_smile:

Edit: even worst when seen on lower resolution to the native screenshot,libretro forums compress when file size is huge.

1 Like

Yeahhhh… I’m going to just start posting my images on an image hosting site instead of using the forums method of uploading images (seems to destroy the quality :joy:).

1 Like

I’m curious can you try my preset ,then post a screenshot on libretro,then on pasteboard. i want to see how it looks on my screen with lower resolution,i assume you have 1080p screen. :man_mechanic:

1 Like

That’s not a problem, but it’ll probably be tomorrow before I post them. I’m currently on mobile and using my tv (which is my computer monitor…) to watch wrestling, lol.

But for sure I’ll do that tomorrow, and yeah 1080p.

1 Like

Sure when you have time :muscle:

1 Like

Sorry ALL the wrestling is on tonight XD.

I may get it uploaded tonight, lol.

EDIT: Most likely it will only be a few images, probably a Super Mario World shot, Little Sampson, and Marvel vs Capcom, maybe one of the CPS3 Street Fighter games.

1 Like

can you post some shots with the following changes?

GLOW_WIDTH = "0.50000"
GLOW_HEIGHT = "0.50000"
GLOW_HALATION = "0.00000"
GAMMA_INPUT = "2.400000"

Also some shots with the dotmask (magenta/green checkerboard) @ same resolution and slotmask at 10x? whatever LUTWidth/Height is appropriate.

Trying to figure out what’s going on with my own preset.

Edit: also those scanlines still look uneven to me, but that could actually be TN panel inversion artifacts from the display I’m using; hard to tell. If you’re using integer scale you have to set aspect ratio to “custom” and set a custom res for it to work right.

1 Like

Current CRT-Aperture settings; should look good on any HDR display when using black frame insertion @ 120 Hz and maxing out the backlight, assuming the display doesn’t have automatic brightness limiting. It will also look good on any non-HDR display when not using BFI and increasing the backlight.

If the display is at least 500 nits then these settings should result in at least 100 nits. If you’re starting with 600 nits you can push the mask strength as high as 65% while still maintaining adequate brightness.

If you’re using the slotmask, OTOH, you can push mask strength to 25% if you start with 500 nits, or 40% if you’re starting with 600 nits.

I just wish I had all the mask options in CRT-Aperture :stuck_out_tongue:

alias0 = ""
BRIGHTNESS = "1.000000"
filter_linear0 = "false"
float_framebuffer0 = "false"
GAMMA_INPUT = "2.400000"
GAMMA_OUTPUT = "2.200000"
GLOW_DIFFUSION = "0.000000"
GLOW_HALATION = "0.000000"
GLOW_HEIGHT = "0.500000"
GLOW_WIDTH = "0.500000"
MASK_COLORS = "2.000000"
MASK_SIZE = "1.000000"
MASK_STRENGTH = "0.400000"
mipmap_input0 = "false"
parameters = "SHARPNESS_IMAGE;SHARPNESS_EDGES;GLOW_WIDTH;GLOW_HEIGHT;GLOW_HALATION;GLOW_DIFFUSION;MASK_COLORS;MASK_STRENGTH;MASK_SIZE;SCANLINE_SIZE_MIN;SCANLINE_SIZE_MAX;SCANLINE_SHAPE;SCANLINE_OFFSET;GAMMA_INPUT;GAMMA_OUTPUT;BRIGHTNESS"
SCANLINE_OFFSET = "1.000000"
SCANLINE_SHAPE = "1.000000"
SCANLINE_SIZE_MAX = "1.500000"
SCANLINE_SIZE_MIN = "0.500000"
shader0 = "C:\Program Files\RetroArch\shaders\shaders_glsl\crt\shaders\crt-aperture.glsl"
shaders = "1"
SHARPNESS_EDGES = "3.000000"
SHARPNESS_IMAGE = "1.000000"
srgb_framebuffer0 = "false"
wrap_mode0 = "clamp_to_border"
1 Like

I don’t know the problem, all i can i say it’s everything is even for me. you can try to use dsr (dynamic super resolution) to simulate 4k with nvidia gpu if you have one ,still don’t know if it’s gonna look like me.

Here’s a screen on 800% zoomed (maybe you need to zoom if the grid it’s not scaled correctly on pasteboard) https://pasteboard.co/IKQ0wpx.png

Sure i can do that,let me a couple minutes to take screenshots with your settings

Edit: Screenshots with settings you asked ( by this order -> 9x delta_1_4x1_rgb,9x m/g checkerboard,10x slotmask) 2 and 3 doesn’t have the same checkerboard pattern with each scanlines.

1.https://pasteboard.co/IKQb2F0.png

2.https://pasteboard.co/IKQd8Bd.png

3.https://pasteboard.co/IKQdEG4.png

2 Likes

Wowww… I’m really not a fan of the m/g mask (option 2). Personally I enjoyed 1 the most and 3 seemed nice.

Take this with a grain of salt though as I’m on mobile.

1 Like