New CRT shader from Guest + CRT Guest Advanced updates

Some bloom was added. But it’s rather a fast setup. Brightboost would also do. Bloom, in general, has mostly two components in linear space. First is plain brightness increase, the other is spatial. Very high blur increases the second.

1 Like

GDV Shader update.

Notable changes:

  • now a prominent metric is used to measure the color differences for the ‘smart edge’ effect
  • automatic switching between aliased and anti-aliased modes at horizontal sharpness 2.8->2.6 with the mentioned effect.
  • new menu parameter organization -> main reason to switch to new version :grin:
  • LUT 3 colors patched in the dark spectrum, need some feedback though (@Tatsuya79).

Download link:

https://mega.nz/file/49BAGTiR#nZi1v7OVh7v80PQcQpOiKid52_OynEyoX9sjH5NXQuo

Feedback, as always, is welcome. :upside_down_face:

12 Likes

The problem is still there on LUT 3 with that blue color.

Nice new presentation!

3 Likes

Thanks. :grinning:

Could you try this pre-shaders-afterglow.slang file ? It does a quirky transplantation. I like LUT3, but it has broken low’s and pre-processing manages to find more inconsistencies. Maybe i’ll just ask here if someone has a better suggestions for LUT 3 (size 32, gamma friendly, no 'bad sectors :wink:).

#version 450
 

layout(push_constant) uniform Push
{
   vec4 SourceSize;
   vec4 OriginalSize;
   vec4 OutputSize;
   uint FrameCount;
   float TNTC;
   float CP, CS;
   float WP;
   float wp_saturation;
   float AS, sat;
} params;

#pragma parameter AS "          Afterglow Strength" 0.07 0.0 0.50 0.01
#define AS params.AS

#pragma parameter sat "          Afterglow saturation" 0.10 0.0 1.0 0.01
#define sat params.sat

#pragma parameter bogus_color "[ COLOR TWEAKS ]:" 0.0 0.0 1.0 1.0

#pragma parameter TNTC "          LUT Colors" 0.0 0.0 3.0 1.0
#define TNTC params.TNTC

#pragma parameter CP "          CRT Color Profile" 0.0 -1.0 5.0 1.0 
#pragma parameter CS "          Color Space: sRGB, DCI, Adobe, Rec.2020" 0.0 0.0 3.0 1.0 

#define CP params.CP
#define CS params.CS
 
#pragma parameter WP "          Color Temperature %" 0.0 -100.0 100.0 5.0 
#pragma parameter wp_saturation "          Saturation Adjustment" 1.0 0.0 2.0 0.05 

#define WP params.WP
#define wp_saturation params.wp_saturation

layout(std140, set = 0, binding = 0) uniform UBO
{
   mat4 MVP;
} global;

#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;

void main()
{
   gl_Position = global.MVP * Position;
   vTexCoord = TexCoord;
}

#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D StockPass;
layout(set = 0, binding = 3) uniform sampler2D AfterglowPass;
layout(set = 0, binding = 4) uniform sampler2D SamplerLUT1;
layout(set = 0, binding = 5) uniform sampler2D SamplerLUT2;
layout(set = 0, binding = 6) uniform sampler2D SamplerLUT3;
layout(set = 0, binding = 7) uniform sampler2D OriginalHistory0;

#define LUT_Size 32.0
#define COMPAT_TEXTURE(c,d) texture(c,d)


// Color profile matrices

const mat3 Profile0 = 
mat3(
 0.412391,  0.212639,  0.019331,
 0.357584,  0.715169,  0.119195,
 0.180481,  0.072192,  0.950532
);

const mat3 Profile1 = 
mat3(
 0.430554,  0.222004,  0.020182,
 0.341550,  0.706655,  0.129553,
 0.178352,  0.071341,  0.939322
);

const mat3 Profile2 = 
mat3(
 0.396686,  0.210299,  0.006131,
 0.372504,  0.713766,  0.115356,
 0.181266,  0.075936,  0.967571
);

const mat3 Profile3 = 
mat3(
 0.393521,  0.212376,  0.018739,
 0.365258,  0.701060,  0.111934,
 0.191677,  0.086564,  0.958385
);

const mat3 Profile4 = 
mat3(
 0.392258,  0.209410,  0.016061,
 0.351135,  0.725680,  0.093636,
 0.166603,  0.064910,  0.850324
);

const mat3 Profile5 = 
mat3(
 0.377923,  0.195679,  0.010514,
 0.317366,  0.722319,  0.097826,
 0.207738,  0.082002,  1.076960
);

const mat3 ToSRGB = 
mat3(
 3.240970, -0.969244,  0.055630,
-1.537383,  1.875968, -0.203977,
-0.498611,  0.041555,  1.056972
);

const mat3 ToDCI = 
mat3(
 2.725394,  -0.795168,   0.041242,
-1.018003,   1.689732,  -0.087639,
-0.440163,   0.022647,   1.100929
);

const mat3 ToAdobe = 
mat3(
 2.041588, -0.969244,  0.013444,
-0.565007,  1.875968, -0.11836,
-0.344731,  0.041555,  1.015175
);

const mat3 ToREC = 
mat3(
 1.716651, -0.666684,  0.017640,
-0.355671,  1.616481, -0.042771,
-0.253366,  0.015769,  0.942103
); 

// Color temperature matrices

const mat3 D65_to_D55 = mat3 (
           0.4850339153,  0.2500956126,  0.0227359648,
           0.3488957224,  0.6977914447,  0.1162985741,
           0.1302823568,  0.0521129427,  0.6861537456);


const mat3 D65_to_D93 = mat3 (
           0.3683017655,  0.1899055978,  0.0172641453,
           0.3555467892,  0.7110935785,  0.1185155964,
           0.2475020592,  0.0990008237,  1.3035108450);

// This shouldn't be necessary but it seems some undefined values can
// creep in and each GPU vendor handles that differently. This keeps
// all values within a safe range
vec4 mixfix(vec4 a, vec4 b, float c)
{
	return (a.z < 1.0) ? mix(a, b, c) : a;
}


void main()
{
   vec4 imgColor = COMPAT_TEXTURE(StockPass, vTexCoord.xy);
   vec4 imgColop = COMPAT_TEXTURE(OriginalHistory0, vTexCoord.xy);   
   vec4 aftglow = COMPAT_TEXTURE(AfterglowPass, vTexCoord.xy);
   
   float w = 1.0-aftglow.w;

   float l = length(aftglow.rgb);
   aftglow.rgb = AS*w*normalize(pow(aftglow.rgb + 0.01, vec3(sat)))*l;
   
   vec3 color = imgColor.rgb;
   
   if (int(TNTC) == 3 && (imgColor.r < 11.0/255.0 || imgColor.g < 11.0/255.0 || imgColor.b < 11.0/255.0)) imgColor.rgb = imgColop.rgb;
   
   if (int(TNTC) == 0)
   {
      color.rgb = imgColor.rgb;
   }
   else
   {
      float red = ( imgColor.r * (LUT_Size - 1.0) + 0.499999 ) / (LUT_Size * LUT_Size);
      float green = ( imgColor.g * (LUT_Size - 1.0) + 0.499999 ) / LUT_Size;
      float blue1 = (floor( imgColor.b  * (LUT_Size - 1.0) ) / LUT_Size) + red;
      float blue2 = (ceil( imgColor.b  * (LUT_Size - 1.0) ) / LUT_Size) + red;
      float mixer = clamp(max((imgColor.b - blue1) / (blue2 - blue1), 0.0), 0.0, 32.0);
      vec4 color1, color2, res;
      if (int(TNTC) == 1)
      {
         color1 = COMPAT_TEXTURE( SamplerLUT1, vec2( blue1, green ));
         color2 = COMPAT_TEXTURE( SamplerLUT1, vec2( blue2, green ));
         res = mixfix(color1, color2, mixer);
         float mx = max(res.r,max(res.g,res.b));
         float l = mix(length(imgColor.rgb), length(res.rgb), max(mx-0.5,0.0));
         res.rgb = mix(imgColor.rgb, res.rgb, clamp(25.0*(mx-0.02),0.0,1.0));
         res.rgb = normalize(res.rgb+1e-9)*l;
      }
      else if (int(TNTC) == 2)
      {
         color1 = COMPAT_TEXTURE( SamplerLUT2, vec2( blue1, green ));
         color2 = COMPAT_TEXTURE( SamplerLUT2, vec2( blue2, green ));
         res = mixfix(color1, color2, mixer);
         float l = mix(length(imgColor.rgb), length(res.rgb), 0.4);
         res.rgb = normalize(res.rgb + 1e-9)*l;
      }	
      else if (int(TNTC) == 3)
      {
         color1 = COMPAT_TEXTURE( SamplerLUT3, vec2( blue1, green ));
         color2 = COMPAT_TEXTURE( SamplerLUT3, vec2( blue2, green ));
         res = mixfix(color1, color2, mixer);
         res.rgb = pow(res.rgb, vec3(1.0/1.20));
         
         if ( max(max(imgColor.r, imgColor.g),imgColor.b) < 11.0/255.0) res.rgb = imgColor.rgb;
		 
         float l = length(imgColor.rgb);
         res.rgb = normalize(res.rgb + 1e-9)*l;
      }	
      
      color = mix(imgColor.rgb, res.rgb, min(TNTC,1.0));
   }

	vec3 c = clamp(color, 0.0, 1.0);
	
	float p;
	mat3 m_out;
	
	if (CS == 0.0) { p = 2.4; m_out =  ToSRGB; } else
	if (CS == 1.0) { p = 2.6; m_out =  ToDCI;  } else
	if (CS == 2.0) { p = 2.2; m_out =  ToAdobe;} else
	if (CS == 3.0) { p = 2.4; m_out =  ToREC;  }
	
	color = pow(c, vec3(p));
	
	mat3 m_in = Profile0;

	if (CP == 0.0) { m_in = Profile0; } else	
	if (CP == 1.0) { m_in = Profile1; } else
	if (CP == 2.0) { m_in = Profile2; } else
	if (CP == 3.0) { m_in = Profile3; } else
	if (CP == 4.0) { m_in = Profile4; } else
	if (CP == 5.0) { m_in = Profile5; }
	
	color = m_in*color;
	color = m_out*color;

	color = clamp(color, 0.0, 1.0);

	color = pow(color, vec3(1.0/p));	
	
	if (CP == -1.0) color = c;
	
	vec3 scolor1 = normalize(pow(color + 0.000000001, vec3(wp_saturation)))*length(color);
	float luma = dot(color, vec3(0.2126, 0.7152, 0.0722));
	vec3 scolor2 = mix(vec3(luma), color, wp_saturation);
	color = (wp_saturation > 1.0) ? scolor1 : scolor2;
   
	p = 2.2;
	
	color = pow(color, vec3(p)); 
 
	color = clamp(color, 0.0, 1.0); 
	
	vec3 warmer = D65_to_D55*color;
	warmer = ToSRGB*warmer;
	
	vec3 cooler = D65_to_D93*color;
	cooler = ToSRGB*cooler;
	
	float m = abs(WP)/100.0;
	
	vec3 comp = (WP < 0.0) ? cooler : warmer;
	
	color = mix(color, comp, m);
	color = pow(max(color, 0.0), vec3(1.0/p));
	
	color = color + aftglow.rgb;
	
	FragColor = vec4(color, 1.0); 
}

No change for that bug. :confused:

It’s not one of the darkest blue.

1 Like

@Tatsuya79 @guest.r

Not sure if this is the same thing, I got similar artifacts on LUT3 and posted my “fix” in this post:

I haven’t got a clue about coding or what that value does in the lut file, but messing about and changing the value from 1.20 to 1.0 removed the artifacts!

By the way, this worked on GLSL and SLANG versions and it was the original GDV shader, not sure if the lut file has changed in recent times.

2 Likes

That doesn’t help here.

But I just found that setting FBNeo to 16bits instead of 32bits color (in the core options) gets rid of the problem.

3 Likes

Thanks for the shader, even without a mask it’s fun to play. I like the glow :sunrise_over_mountains:

Bonus with bloom and mask:

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

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

Deconvergence 1.0:

  1. https://pasteboard.co/JDfeEaG.png
3 Likes

New GDV settings for the very latest version. Looks fantastic and it’s much easier to use now. Really good stuff, thanks again! @guest.r

The scanlines now look uneven at odd integer scales when using a mask pattern that is x pixels tall, with x>1 and not a factor of y, with y being the vertical scale integer. AFAIK, this is inevitable with improved mask definition. This is another thing that is made worse by additional interpolation; doesn’t look as bad in-game. @guest.r What is your preferred method for solving this?

shaders = "8"
shader0 = "shaders_slang/misc/grade.slang"
filter_linear0 = "false"
wrap_mode0 = "clamp_to_border"
mipmap_input0 = "false"
alias0 = "StockPass"
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/guest/crt-gdv-new/afterglow0.slang"
filter_linear1 = "false"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = "AfterglowPass"
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/guest/crt-gdv-new/pre-shaders-afterglow.slang"
filter_linear2 = "false"
wrap_mode2 = "clamp_to_border"
mipmap_input2 = "false"
alias2 = "PrePass"
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/guest/crt-gdv-new/avg-lum.slang"
filter_linear3 = "true"
wrap_mode3 = "clamp_to_border"
mipmap_input3 = "true"
alias3 = "AvgLumPass"
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/guest/crt-gdv-new/linearize.slang"
filter_linear4 = "true"
wrap_mode4 = "clamp_to_border"
mipmap_input4 = "false"
alias4 = "LinearizePass"
float_framebuffer4 = "true"
srgb_framebuffer4 = "false"
scale_type_x4 = "source"
scale_x4 = "1.000000"
scale_type_y4 = "source"
scale_y4 = "1.000000"
shader5 = "shaders_slang/crt/shaders/guest/crt-gdv-new/blur_horiz2.slang"
filter_linear5 = "true"
wrap_mode5 = "clamp_to_border"
mipmap_input5 = "false"
alias5 = ""
float_framebuffer5 = "false"
srgb_framebuffer5 = "false"
scale_type_x5 = "absolute"
scale_x5 = "800"
scale_type_y5 = "source"
scale_y5 = "1.000000"
shader6 = "shaders_slang/crt/shaders/guest/crt-gdv-new/blur_vert2.slang"
filter_linear6 = "true"
wrap_mode6 = "clamp_to_border"
mipmap_input6 = "false"
alias6 = "GlowPass"
float_framebuffer6 = "false"
srgb_framebuffer6 = "false"
scale_type_x6 = "absolute"
scale_x6 = "800"
scale_type_y6 = "absolute"
scale_y6 = "600"
shader7 = "shaders_slang/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang"
filter_linear7 = "true"
wrap_mode7 = "clamp_to_border"
mipmap_input7 = "false"
alias7 = ""
float_framebuffer7 = "false"
srgb_framebuffer7 = "false"
scale_type_x7 = "viewport"
scale_x7 = "1.000000"
scale_type_y7 = "viewport"
scale_y7 = "1.000000"
parameters = "g_gamma_in;g_signal_type;g_gamma_type;g_crtgamut;g_space_out;g_hue_degrees;g_I_SHIFT;g_Q_SHIFT;g_I_MUL;g_Q_MUL;g_lum_fix;g_vignette;g_vstr;g_vpower;g_lum;g_cntrst;g_mid;wp_temperature;g_sat;g_vibr;g_satr;g_satg;g_satb;g_lift;blr;blg;blb;wlr;wlg;wlb;rg;rb;gr;gb;br;bg;LUT_Size1;LUT1_toggle;LUT_Size2;LUT2_toggle;bogus_afterglow;PR;PG;PB;AS;sat;bogus_color;TNTC;CP;CS;WP;wp_saturation;lsmooth;GAMMA_INPUT;bogus_glow;SIZEH;GLOW_FALLOFF_H;SIZEV;GLOW_FALLOFF_V;bogus_brightness;glow;bloom;gamma_c;brightboost;brightboost1;bogus_scanline;gsl;scanline1;scanline2;beam_min;beam_max;beam_size;vertmask;scans;spike;bogus_filtering;h_sharp;s_sharp;smart_ei;ei_limit;sth;bogus_screen;TATE;IOS;OS;BLOOM;csize;bsize;warpX;warpY;bogus_masks;shadowMask;masksize;maskDark;maskLight;CGWG;mcut;mask_gamma;slotmask;slotwidth;double_slot;slotms;mclip;bogus_interlacing;inter;interm;gamma_out"
g_gamma_in = "2.400000"
g_signal_type = "1.000000"
g_gamma_type = "1.000000"
g_crtgamut = "2.000000"
g_space_out = "0.000000"
g_hue_degrees = "0.000000"
g_I_SHIFT = "0.000000"
g_Q_SHIFT = "0.000000"
g_I_MUL = "1.000000"
g_Q_MUL = "1.000000"
g_lum_fix = "0.000000"
g_vignette = "0.000000"
g_vstr = "40.000000"
g_vpower = "0.200000"
g_lum = "0.000000"
g_cntrst = "0.000000"
g_mid = "0.500000"
wp_temperature = "8005.000000"
g_sat = "0.000000"
g_vibr = "0.000000"
g_satr = "0.000000"
g_satg = "0.000000"
g_satb = "0.000000"
g_lift = "0.000000"
blr = "0.000000"
blg = "0.000000"
blb = "0.000000"
wlr = "1.000000"
wlg = "1.000000"
wlb = "1.000000"
rg = "0.000000"
rb = "0.000000"
gr = "0.000000"
gb = "0.000000"
br = "0.000000"
bg = "0.000000"
LUT_Size1 = "16.000000"
LUT1_toggle = "0.000000"
LUT_Size2 = "64.000000"
LUT2_toggle = "0.000000"
bogus_afterglow = "0.000000"
PR = "0.140000"
PG = "0.140000"
PB = "0.140000"
AS = "0.070000"
sat = "0.100000"
bogus_color = "0.000000"
TNTC = "0.000000"
CP = "0.000000"
CS = "0.000000"
WP = "0.000000"
wp_saturation = "1.000000"
lsmooth = "0.750000"
GAMMA_INPUT = "2.400000"
bogus_glow = "0.000000"
SIZEH = "4.000000"
GLOW_FALLOFF_H = "0.300000"
SIZEV = "4.000000"
GLOW_FALLOFF_V = "0.300000"
bogus_brightness = "0.000000"
glow = "0.000000"
bloom = "0.000000"
gamma_c = "1.000000"
brightboost = "2.499999"
brightboost1 = "2.500001"
bogus_scanline = "0.000000"
gsl = "0.000000"
scanline1 = "10.000000"
scanline2 = "10.000000"
beam_min = "1.500000"
beam_max = "1.000000"
beam_size = "1.000000"
vertmask = "0.000000"
scans = "1.000000"
spike = "1.000000"
bogus_filtering = "0.000000"
h_sharp = "3.000001"
s_sharp = "1.000000"
smart_ei = "0.000000"
ei_limit = "2.000000"
sth = "0.200000"
bogus_screen = "0.000000"
TATE = "0.000000"
IOS = "0.000000"
OS = "1.000000"
BLOOM = "0.000000"
csize = "0.000000"
bsize = "600.000000"
warpX = "0.000000"
warpY = "0.000000"
bogus_masks = "0.000000"
shadowMask = "8.000000"
masksize = "1.000000"
maskDark = "0.500000"
maskLight = "1.500000"
CGWG = "0.500000"
mcut = "1.150000"
mask_gamma = "2.400000"
slotmask = "0.500000"
slotwidth = "2.000000"
double_slot = "1.000000"
slotms = "1.000000"
mclip = "0.000000"
bogus_interlacing = "0.000000"
inter = "350.000000"
interm = "1.000000"
gamma_out = "2.200000"
textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3"
SamplerLUT1 = "shaders_slang/crt/shaders/guest/lut/sony_trinitron1.png"
SamplerLUT1_linear = "true"
SamplerLUT1_wrap_mode = "clamp_to_border"
SamplerLUT1_mipmap = "false"
SamplerLUT2 = "shaders_slang/crt/shaders/guest/lut/sony_trinitron2.png"
SamplerLUT2_linear = "true"
SamplerLUT2_wrap_mode = "clamp_to_border"
SamplerLUT2_mipmap = "false"
SamplerLUT3 = "shaders_slang/crt/shaders/guest/lut/other1.png"
SamplerLUT3_linear = "true"
SamplerLUT3_wrap_mode = "clamp_to_border"
SamplerLUT3_mipmap = "false"
2 Likes

My preferred method for this are mild scanlines and non-integer scaling. Slotmask tends to look distracting when it doesn’t have enough brighter output pixels to develop - with brighter colors.

But this is a hard question, since masks always use flat coordinates and using curvature with masks per scanline adaptations would break the appearance totally apart, also with very mild scanlines/interlacing modes the contacts would look very glitchy.

Was testing some interesting stuff like scanline-strength-based slotmask, which is y-dependant . for non integer scaling. I wouldn’t change the lottes masks though, since they work very well with mild scanlines and higher resolutions.

Which i think leaves slotmask as the candidate for possible improvements. The sweetspot for slotmask is IMO 1440p with normal slotmask size and non-integer scaling. I could try some stuff with interpolating the slotmask, would look worse at even y-scaling, maybe better with odd scaling.

I mild fix might be scanline strength based slotmask strength.

Thanks, i think the version 2 got better consistency in many aspects, with less strange side-effects. I’m also glad it’s very 4k ‘compatible’ since i have only tested it on 1080p so far and had to guess a bit beyond. :grinning:

3 Likes

Think I’m done tweaking for a while. Getting the glow spread to look half-decent isn’t easy, but then my LCD blacks are not exactly black. 0.15 bloom really helped get a bit of brightness back to the highs without drowning pixel out. I certainly wouldn’t push the setting any more though. Bright pixel at 1.50 too.

For 1080p, I think it looks good overall.

1 Like

Yeah I can see now that this is one case where integer scaling has some disadvantages. Non-integer scaling helps mitigate it somewhat but can look bad when scrolling. Like you said, hard question, probably no perfect answer. I guess the best solution is to just use an even integer scale with the slotmask, but then you might have to live with letterboxing.

1 Like

Here’s a link to my mask 8 preset, any future tweakings may be found there. Looks better than any of my previous presets, I think.

As an added bonus, the curvature-related moire isn’t bad at all with these settings, I’ve noticed.

Additional interpolation makes this look worse; the display’s pixel grid makes it look better when viewed fullscreen in RA.

Note: this is for RGB subpixels only!

1 Like

Would you mind sharing your preset for this? I think it looks great!

Cont…

2 Likes

Those are some nice scanlines, what are your settings?

I like the curvature while using the tv overlay, but should I disable curvature while use mask? or maybe should I disable mask while use curvature?

What “scanline type” is better to “mask 1”

1 Like

I lost the settings I had in those shots, but it was something perhaps a bit thicker (shape low/high) than here:

And using scanlines type 2, the last darkest one.

1 Like

Now I’m really liking what the gdv slotmask looks like by itself. The versatility of this shader is amazing.

2 Likes

Not necessary, since masks are basically a flat overlay, only calculated. You should only avoid settings which don’t look appealing and might happen with curvature. These mostly happen if you use stronger scanlines and stronger masks together, in general.

Type 0 out of the box, other types with tweaking.

1 Like