Please show off what crt shaders can do!

Just want to explain some later changes to crt-guest-dr-venom, more precisely scanline spike mitigation. It’s already working with the GLSL version and i’ll see what can be done with the slang one.

I think it looks much better now with scanline type 1&2.

Old behaviour can be achieved with setting the “Scanline Spike Removal” parameter to low values.

Cheers!

9 Likes

Could you explain the update a little? Mainly because viewing the image via mobile the only real difference I noticed is that the spike mitigation image was (softer?) at least that’s what I was seeing, at least mainly around (higher contrast?) areas I guess, I noticed it really well on black and white areas, wasn’t super noticeable in the face image but in the space invaders it was really visible in my opinion. You could also see it (slightly?) with the red lines, it had some interesting effects on the multi-color thing in the space invaders area, and its fairly noticeable in the text.

1 Like

Gaussian scanlines have some issues with stronger scanlines, for example “bright scanline spikes” happen when the horizontal sharpness is at preferred medium values.

Once you see it in action/PC screen it get’s quite obvious.

It was on my TODO/FIX list quite some time, it was annoying for me especially with games that use black backgrounds.

In general it should work out very well, since the solution works with different horizontal sharpness levels, maybe some substractive sharpness (like the default 0.05) should be used to avoid some situations.

Ofc., if it’s not so likeable, old behaviour can be used. :grin:

4 Likes

Honestly it’s pretty noticeable on mobile except that face lol. Haven’t tested on PC yet but I do enjoy the new look, kind of seems like another sharpness setting imo. It’s cool that it’s adjustable though.

Does it have any impact on performance or is the difference basically non-existent (basically just as fast, lol)?

It’s cool that the shader is still getting updates, do you have any other things on your to-do list(I understand that you’re busy and they wouldn’t be done in the immediate future)?

1 Like

The performance hit is a couple of %, nothing to worry about. There is still the fast version, which won’t get such updates.

About my further plans, maybe some day HDR support will be added, but the ways to use this technology properly are still quite unclear and maybe i’ll need some help/inspiration too.

Otherwise there is still room for a mask or a cool scanline function, but thats rather an exceptional turn of events and will be implemented only if turns out very cool and benefiting.

2 Likes
1 Like

Here’s an actual macro shot of a CRT. I think you’ll agree that it looks very similar to the shot I posted above, although that shot kind of resembles a cross between the aperture grille and slotmask examples in the link below, due to the lower TVL of the slotmask.

You can’t get results this accurate without the magenta/green masks; blue and red will turn into magenta when you try to take a macro shot (blue won’t be strong enough).

Here are some more macro shots for reference:

2 Likes

Hey @BlockABoots I haven’t posted the new version of the bezel reflection yet, but it’s coming this weekend :slight_smile:

4 Likes

This would be a breakthrough for CRT emulation. As of right now, no currently available display is bright enough to do full-strength masks and scanlines AND black frame insertion, so you’re always compromising on one thing or another. If we can fake SDR content as HDR content then we’d have all the brightness we need for all the CRT effects. No one seems to know how to make this work, though. Maybe a bounty is in order…

If you want to maintain CRT-like brightness, you need a minimum of 800 nits if you want BFI @ 120 Hz and full strength mask and scanlines- that would leave you with about 100 nits. If you want to do the slotmask, you need to start with 1600 nits(!), so that might still be out of reach for even the brightest displays currently available. You’d have to reduce the slotmask strength while using BFI and scanlines.

2 Likes

Fragment program for pre-bloom processing

uniform float Exposure;
 uniform sampler2D SrcColor;
 uniform sampler2D SrcHDR;
 
 varying vec2 texCoord;
 
 const vec4 gloomStart = vec4(0.95,0.95,0.95,0.95);
 
 float sqr(float x) { return x*x; }
 vec4 sqr(vec4 x) { return x*x; }
 
 vec4 expand_Hdr(vec4 color)
 {
    return color*(sqr(color.a*2.0)+1.0);
 }
  
 void main(void)
 {
    vec4 color  = texture2D(SrcColor,texCoord);
    gl_FragColor = expand_Hdr(color*Exposure)-gloomStart;
 }

Fragment program for final composition stage

uniform float Exposure;
 uniform sampler2D SrcColor;
 uniform sampler2D SrcHDR1;
 uniform sampler2D SrcHDR2;
 uniform sampler2D SrcHDR3;
 uniform sampler2D SrcHDR4;
 uniform sampler2D Measure;
 
 uniform vec4 MipMix;
 
 float gloomIntensity=1.0;
 
 varying vec2 texCoord;
 
 float sqr(float x) { return x*x; }
 vec4 sqr(vec4 x) { return x*x; }
 
 vec4 expand_Hdr(vec4 color)
 {
    return color*(sqr(color.a*2.0)+1.0);
 }
 
 void main(void)
 {
    vec4 color  = texture2D(SrcColor,texCoord);
    vec4 gloom  = mat4(
       texture2D(SrcHDR1,texCoord),
       texture2D(SrcHDR2,texCoord),
       texture2D(SrcHDR3,texCoord),
       texture2D(SrcHDR4,texCoord) ) * MipMix;
    gl_FragColor = (expand_Hdr(color*Exposure)+gloom*16.0*gloomIntensity)*Exposure;
 }

Vertex/Fragment program for rendered objects

Vertex program

uniform vec3 LightDir;
 uniform vec4 vViewPosition;
 uniform mat4 matViewProjection;
 
 varying vec2 texCoord;
 varying vec3 normal;
 varying vec3 lightDirInTangent;
 varying vec3 viewDirInTangent;
 
 attribute vec3 rm_Tangent;
 attribute vec3 rm_Binormal;
 
 void main(void)
 {
    texCoord = gl_MultiTexCoord0.xy;
    
    mat3 tangentMat = mat3(rm_Tangent,
                           rm_Binormal,
                           gl_Normal);
    lightDirInTangent = normalize(LightDir) * tangentMat;
    viewDirInTangent  = normalize(vViewPosition-gl_Position).xyz * tangentMat;
    
    gl_Position = ftransform();
 }

Fragment program

uniform sampler2D BumpMap;
 uniform sampler2D ObjectMap;
 uniform sampler2D SpecMap;
 
 uniform float Shininess;
 uniform float SpecularIntensity;
 
 varying vec2 texCoord;
 varying vec3 lightDirInTangent;
 varying vec3 viewDirInTangent;
 
 void main(void)
 {
    vec3  n_lightDirInTangent = -normalize(lightDirInTangent);
    vec3  n_viewDirInTangent = normalize(viewDirInTangent);
    vec3  bump     = normalize(texture2D(BumpMap,texCoord).xyz * 2.0 - 1.0);
    float lighting = dot(bump,n_lightDirInTangent);
    float blighting= n_lightDirInTangent.z;
    float specular = dot(-reflect(n_lightDirInTangent,bump),n_viewDirInTangent);
    
    vec4 texColor = texture2D(ObjectMap,texCoord);
    vec4 specColor = texture2D(SpecMap, texCoord); 
    vec4 color = texColor * lighting 
               + float(lighting>0.0)*float(blighting>0.0) 
                 * SpecularIntensity*pow(specular,Shininess)*specColor;
    gl_FragColor = vec4(color.xyz*0.5,0.414);
 }

Fragment programs for first separable convolution stage

Horizontal

uniform sampler2D Src;
 
 varying vec2 texCoord;
 
 const float texDimension = 512.0;
 const float texScaler =  1.0/texDimension;
 const float texOffset = -0.5/texDimension;
 
 void main(void)
 {
    vec4 color = vec4(0.0,0.0,0.0,0.0);
    
    const float gauss0 = 1.0/32.0;
    const float gauss1 = 5.0/32.0;
    const float gauss2 =15.0/32.0;
    const float gauss3 =22.0/32.0;
    const float gauss4 =15.0/32.0;
    const float gauss5 = 5.0/32.0;
    const float gauss6 = 1.0/32.0;   
    
    vec4 gaussFilter[7];
    gaussFilter[0]  = vec4( -3.0*texScaler , 0.0, 0.0, gauss0); 
    gaussFilter[1]  = vec4( -2.0*texScaler , 0.0, 0.0, gauss1); 
    gaussFilter[2]  = vec4( -1.0*texScaler , 0.0, 0.0, gauss2); 
    gaussFilter[3]  = vec4(  0.0*texScaler , 0.0, 0.0, gauss3);
    gaussFilter[4]  = vec4( +1.0*texScaler , 0.0, 0.0, gauss4);
    gaussFilter[5]  = vec4( +2.0*texScaler , 0.0, 0.0, gauss5);
    gaussFilter[6]  = vec4( +3.0*texScaler , 0.0, 0.0, gauss6);
   
    int i;
    for (i=0;i<7;i++)
       color += texture2D(Src, texCoord + gaussFilter[i].xy) * gaussFilter[i].w;
    
    gl_FragColor = color*0.5;
 }

Vertical

uniform sampler2D Src; 
 
 varying vec2 texCoord;
 
 const float texDimension = 512.0;
 const float texScaler =  1.0/texDimension;
 const float texOffset = -0.5/texDimension;
 
 void main(void)
 {
    vec4 color = vec4(0.0,0.0,0.0,0.0);
    
    const float gauss0 = 1.0/32.0;
    const float gauss1 = 5.0/32.0;
    const float gauss2 =15.0/32.0;
    const float gauss3 =22.0/32.0;
    const float gauss4 =15.0/32.0;
    const float gauss5 = 5.0/32.0;
    const float gauss6 = 1.0/32.0;   
   
    vec4 gaussFilter[7];
    gaussFilter[0]  = vec4( -3.0*texScaler , 0.0, 0.0, gauss0).yxzw;
    gaussFilter[1]  = vec4( -2.0*texScaler , 0.0, 0.0, gauss1).yxzw;
    gaussFilter[2]  = vec4( -1.0*texScaler , 0.0, 0.0, gauss2).yxzw;
    gaussFilter[3]  = vec4(  0.0*texScaler , 0.0, 0.0, gauss3).yxzw;
    gaussFilter[4]  = vec4( +1.0*texScaler , 0.0, 0.0, gauss4).yxzw;
    gaussFilter[5]  = vec4( +2.0*texScaler , 0.0, 0.0, gauss5).yxzw;
    gaussFilter[6]  = vec4( +3.0*texScaler , 0.0, 0.0, gauss6).yxzw;
   
    for (int i=0;i<7;i++)
       color += texture2D(Src, texCoord + gaussFilter[i].xy) * gaussFilter[i].w;
    
    gl_FragColor = color*0.5;
 }

Convolution matrix for second separable convolution stage

const float gauss0 = 1.0/32.0;
   const float gauss1 = 5.0/32.0;
   const float gauss2 =15.0/32.0;
   const float gauss3 =22.0/32.0;
   const float gauss4 =15.0/32.0;
   const float gauss5 = 5.0/32.0;
   const float gauss6 = 1.0/32.0;

Convolution matrix for third separable convolution stage

const float gauss0 = 1.0/32.0;
   const float gauss1 = 5.0/32.0;
   const float gauss2 =15.0/32.0;
   const float gauss3 =22.0/32.0;
   const float gauss4 =15.0/32.0;
   const float gauss5 = 5.0/32.0;
   const float gauss6 = 1.0/32.0;

Convolution matrix for fourth separable convolution stage

const float gauss0 = 1.0/18.0;
   const float gauss1 = 2.0/18.0;
   const float gauss2 = 4.0/18.0;
   const float gauss3 = 4.0/18.0;
   const float gauss4 = 4.0/18.0;
   const float gauss5 = 2.0/18.0;
   const float gauss6 = 1.0/18.0;

Should help some coders here (i hope).

1 Like

all completely over my head but looks like you found some great resources there!

The reflection shader is the only reason i’m checking this thread every day, lol.

2 Likes

I mainly check it for that, along with to see if hunterk or guest.r is doing something new.

On that note the scanline spike mitigation is pretty nice, and I’m also looking forward to the red (smear, blur thing?) by hunterk.

I do check it for specific users shader chains.

8 posts were split to a new topic: New CRT shader from Guest

Could you please share your shader preset?

I would like to see your preset as well. Those scanlines look neat.

Which one you refer to?

Those from Sonic 1 and Earthworm Jim. I presume they use the same preset.

Ah, yes. It’s the preset i’m using for Sega Genesis/Mega Drive. It’s used in combination with the S-Video filter in the core options of GenesisPlusGX.

shaders = "4"
shader0 = "../../shaders_glsl/misc/image-adjustment.glsl"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = "BloomPass"
float_framebuffer0 = "false"
srgb_framebuffer0 = "true"
shader1 = "../../shaders_glsl/crt/shaders/gtu-v050/pass1.glsl"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = ""
float_framebuffer1 = "true"
srgb_framebuffer1 = "false"
scale_type_x1 = "source"
scale_x1 = "1.000000"
scale_type_y1 = "source"
scale_y1 = "1.000000"
shader2 = "../../shaders_glsl/crt/shaders/gtu-v050/pass2.glsl"
filter_linear2 = "false"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = ""
float_framebuffer2 = "true"
srgb_framebuffer2 = "false"
scale_type_x2 = "viewport"
scale_x2 = "1.000000"
scale_type_y2 = "source"
scale_y2 = "1.000000"
shader3 = "../../shaders_glsl/crt/shaders/crt-geom.glsl"
filter_linear3 = "false"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "false"
alias3 = ""
float_framebuffer3 = "false"
srgb_framebuffer3 = "false"
scale_type_x3 = "viewport"
scale_x3 = "1.000000"
scale_type_y3 = "viewport"
scale_y3 = "1.000000"
parameters = "ia_target_gamma;ia_monitor_gamma;ia_overscan_percent_x;ia_overscan_percent_y;ia_saturation;ia_contrast;ia_luminance;ia_black_level;ia_bright_boost;ia_R;ia_G;ia_B;ia_ZOOM;ia_XPOS;ia_YPOS;ia_TOPMASK;ia_BOTMASK;ia_LMASK;ia_RMASK;ia_GRAIN_STR;ia_SHARPEN;ia_FLIP_HORZ;ia_FLIP_VERT;compositeConnection;signalResolution;signalResolutionI;signalResolutionQ;CRTgamma;monitorgamma;d;CURVATURE;R;cornersize;cornersmooth;x_tilt;y_tilt;overscan_x;overscan_y;DOTMASK;SHARPER;scanline_weight;lum"
ia_target_gamma = "2.200000"
ia_monitor_gamma = "2.200000"
ia_overscan_percent_x = "0.000000"
ia_overscan_percent_y = "0.000000"
ia_saturation = "1.000000"
ia_contrast = "1.000000"
ia_luminance = "1.100000"
ia_black_level = "0.010000"
ia_bright_boost = "0.050000"
ia_R = "1.000000"
ia_G = "1.000000"
ia_B = "1.000000"
ia_ZOOM = "1.000000"
ia_XPOS = "0.000000"
ia_YPOS = "0.000000"
ia_TOPMASK = "0.000000"
ia_BOTMASK = "0.000000"
ia_LMASK = "0.000000"
ia_RMASK = "0.000000"
ia_GRAIN_STR = "0.000000"
ia_SHARPEN = "0.000000"
ia_FLIP_HORZ = "0.000000"
ia_FLIP_VERT = "0.000000"
compositeConnection = "0.000000"
signalResolution = "256.000000"
signalResolutionI = "83.000000"
signalResolutionQ = "25.000000"
CRTgamma = "2.400000"
monitorgamma = "2.300000"
d = "1.600000"
CURVATURE = "1.000000"
R = "2.400000"
cornersize = "0.025000"
cornersmooth = "1000.000000"
x_tilt = "0.000000"
y_tilt = "0.000000"
overscan_x = "100.000000"
overscan_y = "100.000000"
DOTMASK = "0.300000"
SHARPER = "1.000000"
scanline_weight = "0.350000"
lum = "0.000000"

First two passes from gtuv50 shader and fakelottes as third pass. Signal Resolution set to 160 for blend the dithering.

PSX game Legacy Of Kain: Soul Reaver.

2 Likes