New CRT shader from Guest + CRT Guest Advanced updates

Like I said, I haven’t pushed all of the updates to the slang repo because it showed the artifacts on my machine. I didn’t get them on GLSL, so I assumed it was safe to push, but it seems some people are getting them there, too.

3 Likes

Edit: think I’m getting pretty close. Mask strength 95% is the highest I’ve been able to get it without weird TN panel inversion artifacts. Increased gamma out and saturation and played with the scanline settings a bit.

Zoom in to 100%. Must be viewed on a 1080p display. Needs at least an LED-lit LCD with the backlight at 100% to look acceptably bright.

shaders = "2"
shader0 = "shaders_glsl/crt/shaders/guest/d65-d50.glsl"
filter_linear0 = "false"
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_glsl/crt/shaders/guest/crt-guest-sm.glsl"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = ""
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
parameters = "WP;wp_saturation;smart;brightboost;scanline;beam_min;beam_max;s_gamma;h_sharp;mask;maskdark;maskbright;masksize;gamma_out"
WP = "0.000000"
wp_saturation = "1.100000"
smart = "1.000000"
brightboost = "1.400000"
scanline = "7.000000"
beam_min = "1.400001"
beam_max = "1.200000"
s_gamma = "2.400001"
h_sharp = "1.500000"
mask = "0.000000"
maskdark = "0.950000"
maskbright = "0.950000"
masksize = "1.000000"
gamma_out = "2.400000"

edit2: new settings, new pics

2 Likes

Hey these are some nice screenies and settings. Perhaps “maskbright” could be lower, like 0.7-0.8, for every day use. :smiley:

2 Likes

Can’t stop showing off this shader! Mask strength at 80/100 light/dark and a few other tweaks.

Usual terms and viewing conditions apply.

shaders = "2"
shader0 = "shaders_glsl/crt/shaders/guest/d65-d50.glsl"
filter_linear0 = "false"
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_glsl/crt/shaders/guest/crt-guest-sm.glsl"
wrap_mode1 = "clamp_to_border"
mipmap_input1 = "false"
alias1 = ""
float_framebuffer1 = "false"
srgb_framebuffer1 = "false"
parameters = "WP;wp_saturation;smart;brightboost;scanline;beam_min;beam_max;s_gamma;h_sharp;mask;maskdark;maskbright;masksize;gamma_out"
WP = "0.000000"
wp_saturation = "1.000000"
smart = "1.000000"
brightboost = "1.400000"
scanline = "10.000000"
beam_min = "1.400001"
beam_max = "1.100000"
s_gamma = "2.400001"
h_sharp = "1.500000"
mask = "0.000000"
maskdark = "1.000000"
maskbright = "0.800000"
masksize = "1.000000"
gamma_out = "2.200000"
4 Likes

my composite tweaks

crt-guest-sm-comp.glslp https://pastebin.com/0L0QgcMg

my settings https://pastebin.com/CN8xFKR6

3 Likes

Thanks for the very constructive feedback on the shader.

Nevertheless, some issues were still bothering me. First of all non-integer scaling scanlines were looking odd in some games (for example 224px on 1080p), especially with brighter colors. I spent much time adjusting the scanline function because it could look nicer. Finally i tweaked and adjusted the brightboost application and surprisingly, some situations got better. In short terms, non-integer scanlines look much nicer now (on brighter image parts). :partying_face:

I’ll drop the newest version here:

crt-guest-sm.glsl

/*
   CRT - Guest - SM (Scanline Mask) Shader
   
   Copyright (C) 2019 guest(r) - [email protected]

   Big thanks to Nesguy from the Libretro forums for the masks and other ideas.
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   
*/

/*   README - MASKS GUIDE

To obtain the best results with masks 0, 1, 3, 4: 
must leave “mask size” at 1 and the display must be set to its native resolution to result in evenly spaced “active” LCD subpixels.

Mask 0: Uses a magenta and green pattern for even spacing of the LCD subpixels.

Mask 1: Intended for displays that have RBG subpixels (as opposed to the more common RGB). 
Uses a yellow/blue pattern for even spacing of the LCD subpixels.

Mask 2: Common red/green/blue pattern.

Mask 3: This is useful for 4K displays, where masks 0 and 1 can look too fine. 
Uses a red/yellow/cyan/blue pattern to result in even spacing of the LCD subpixels.

Mask 4: Intended for displays that have the less common RBG subpixel pattern. 
This is useful for 4K displays, where masks 0 and 1 can look too fine. 
Uses a red/magenta/cyan/green pattern for even spacing of the LCD subpixels.

*/


// Parameter lines go here:
#pragma parameter smart "Smart Y Integer Scaling" 0.0 0.0 1.0 1.0
#pragma parameter brightboost1 "Bright boost dark colors" 1.40 0.5 2.0 0.05
#pragma parameter brightboost2 "Bright boost bright colors" 1.10 0.5 2.0 0.05
#pragma parameter scanline "Scanline shape" 8.0 4.0 14.0 0.5
#pragma parameter beam_min "Scanline dark" 1.40 0.5 2.0 0.05
#pragma parameter beam_max "Scanline bright" 1.10 0.5 2.0 0.05
#pragma parameter s_gamma "Scanline gamma" 2.4 1.5 3.0 0.05
#pragma parameter h_sharp "Horizontal sharpness" 2.0 1.0 5.0 0.05
#pragma parameter mask "CRT Mask (3&4 are 4k masks)" 0.0 0.0 4.0 1.0
#pragma parameter maskmode "CRT Mask Mode: Classic, Fine, Coarse" 0.0 0.0 2.0 1.0
#pragma parameter maskdark "CRT Mask Strength Dark Pixels" 1.0 0.0 1.5 0.05
#pragma parameter maskbright "CRT Mask Strength Bright Pixels" 0.20 -0.5 1.0 0.05
#pragma parameter masksize "CRT Mask Size" 1.0 1.0 2.0 1.0
#pragma parameter gamma_out "Gamma Out" 2.30 1.0 3.0 0.05

#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 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;

vec4 _oPosition1; 
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;

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

#elif defined(FRAGMENT)

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

#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

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;

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

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

#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float smart;
uniform COMPAT_PRECISION float brightboost1;
uniform COMPAT_PRECISION float brightboost2;
uniform COMPAT_PRECISION float scanline;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float s_gamma;
uniform COMPAT_PRECISION float h_sharp;
uniform COMPAT_PRECISION float mask;
uniform COMPAT_PRECISION float maskmode;
uniform COMPAT_PRECISION float maskdark;
uniform COMPAT_PRECISION float maskbright;
uniform COMPAT_PRECISION float masksize;
uniform COMPAT_PRECISION float gamma_out;
#else
#define smart        0.00     // smart Y integer scaling
#define brightboost1 1.40     // adjust brightness - dark pixels
#define brightboost2 1.10     // adjust brightness - bright pixels
#define scanline     8.00     // scanline param, vertical sharpness
#define beam_min     1.40     // dark area beam min - narrow
#define beam_max     1.10     // bright area beam max - wide
#define s_gamma      2.40     // scanline gamma
#define h_sharp      2.00     // pixel sharpness
#define mask         0.00     // crt mask type
#define maskmode     0.00     // crt mask mode
#define maskdark     1.00     // crt mask strength dark pixels
#define maskbright   0.20     // crt mask strength bright pixels
#define masksize     1.00     // crt mask size
#define gamma_out    2.30     // gamma out
#endif

float st(float x)
{
	return exp2(-10.0*x*x);
}  

vec3 sw(float x, vec3 color)
{
	vec3 tmp = mix(vec3(2.75*beam_min),vec3(beam_max), color);
	tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color + 0.25));
	vec3 ex = vec3(x)*tmp;
	return exp2(-scanline*ex*ex)/(0.60 + 0.40*color);
}

float Overscan(float pos, float dy){
  pos=pos*2.0-1.0;    
  pos*=dy;
  return pos*0.5+0.5;
}

void main()
{
	vec2 tex = TEX0.xy * 1.000001;

	if (smart == 1.0)
	{
		float factor = OutputSize.y/InputSize.y;
		float intfactor = round(factor);
		float diff = factor/intfactor;
		tex.y = Overscan(tex.y*(SourceSize.y/InputSize.y), diff)*(InputSize.y/SourceSize.y); 
	}
	
	vec2 OGL2Pos = tex * SourceSize.xy - vec2(0.5);
	vec2 fp = fract(OGL2Pos);

	vec2 pC4 = (floor(OGL2Pos) + vec2(0.5)) * SourceSize.zw;	
	
	// Reading the texels
	vec3 ul = COMPAT_TEXTURE(Texture, pC4                         ).xyz; ul*=ul;
	vec3 ur = COMPAT_TEXTURE(Texture, pC4 + vec2(SourceSize.z,0.0)).xyz; ur*=ur;
	vec3 dl = COMPAT_TEXTURE(Texture, pC4 + vec2(0.0,SourceSize.w)).xyz; dl*=dl;
	vec3 dr = COMPAT_TEXTURE(Texture, pC4 + SourceSize.zw         ).xyz; dr*=dr;
	
	float lx = fp.x;        lx = pow(lx, h_sharp);
	float rx = 1.0 - fp.x;  rx = pow(rx, h_sharp);
	
	float w = 1.0/(lx+rx);
	
	vec3 color1 = w*(ur*lx + ul*rx);
	vec3 color2 = w*(dr*lx + dl*rx);


	ul*=ul*ul; ul*=ul;
	ur*=ur*ur; ur*=ur;
	dl*=dl*dl; dl*=dl;
	dr*=dr*dr; dr*=dr;	
	
	vec3 scolor1 = w*(ur*lx + ul*rx); scolor1 = pow(scolor1, vec3(s_gamma*(1.0/12.0)));
	vec3 scolor2 = w*(dr*lx + dl*rx); scolor2 = pow(scolor2, vec3(s_gamma*(1.0/12.0)));	
	
// calculating scanlines
	
	float f = fp.y;

	float t1 = st(f);
	float t2 = st(1.0-f);
	
	vec3 color = color1*t1 + color2*t2;
	vec3 scolor = scolor1*t1 + scolor2*t2;
	
	vec3 ctemp = color / (t1 + t2);
	vec3 sctemp = scolor / (t1 + t2);
	
	vec3 cref1 = mix(scolor1, sctemp, 0.35);
	vec3 cref2 = mix(scolor2, sctemp, 0.35);
	
	vec3 w1 = sw(f,cref1);
	vec3 w2 = sw(1.0-f,cref2);
	
	color = color1*w1 + color2*w2;
	color = min(color,1.0);
	
	color = mix(color, normalize(ctemp + 1e-8)*length(color), 2.0*abs(f-0.5));	
	
	vec3 scan3 = vec3(0.0);
	float spos = floor((gl_FragCoord.x * 1.000001)/masksize); float spos1 = 0.0;
	vec3 tmp1 = pow(sctemp, vec3(1.5/s_gamma));

	color*=mix(brightboost1, brightboost2, pow(max(max(sctemp.r,sctemp.g),sctemp.b), 1.25));
	color = min(color,1.0);	
	
	float mboost = 1.25;
	
	if (mask == 0.0)
	{
		spos1 = fract(spos*0.5);
		if      (spos1 < 0.5)  scan3.rb = color.rb;
		else                   scan3.g  = color.g;	
	}
	else
	if (mask == 1.0)
	{
		spos1 = fract(spos*0.5);
		if      (spos1 < 0.5)  scan3.rg = color.rg;
		else                   scan3.b  = color.b;
	}
	else
	if (mask == 2.0)
	{
		mboost = 1.0;
		spos1 = fract(spos/3.0);
		if      (spos1 < 0.333)  scan3.r = color.r;
		else if (spos1 < 0.666)  scan3.g = color.g;
		else                     scan3.b = color.b;
	}
	else
	if (mask == 3.0)
	{
		spos1 = fract(spos*0.25);
		if      (spos1 < 0.25)  scan3.r = color.r;
		else if (spos1 < 0.50)  scan3.rg = color.rg;
		else if (spos1 < 0.75)  scan3.gb = color.gb;	
		else                    scan3.b  = color.b;	
	}
	else	
	{
		spos1 = fract(spos*0.25);
		if      (spos1 < 0.25)  scan3.r = color.r;
		else if (spos1 < 0.50)  scan3.rb = color.rb;
		else if (spos1 < 0.75)  scan3.gb = color.gb;
		else                    scan3.g =  color.g;
	}
	
	vec3 lerpmask = tmp1;
	if (maskmode == 1.0) lerpmask = vec3(max(max(tmp1.r,tmp1.g),tmp1.b)); else
	if (maskmode == 2.0) lerpmask = sctemp*(w1+w2);	
	
	color = max(mix( mix(color, mboost*scan3, maskdark), mix(color, scan3, maskbright), lerpmask), 0.0);
	
	color = pow(color, vec3(1.0/gamma_out));	
    FragColor = vec4(color, 1.0);
} 
#endif

Edit: This shader works well with two brightboost parameters. One for dark colors (should be greater) and one for bright colors. The brightness reduction on darker pixels is bigger due to thinner scanlines and in general stronger mask application. Fixed in the shader update.

Edit2: There are more ways to distribute a mask over the image. The default mode was to use a RGB color, which works pretty well. But there are other ways and they are now included in the shader. Second (fine) mode uses pixel ‘brightness’ and the last (coarse) uses scanline color. The speed impact is very small and the looks are nice but different.

5 Likes

Could you guys do me a favor? I implemented a brand “new” scanline type into the shader and would like some feedback how they go against the classic version. Personally i like them very much. I also fixed saturation with both types. The speed using scanlines 0.0 is about the same as before, new scanlines are tad slower, but not much.

crt-guest-sm.glsl:

/*
   CRT - Guest - SM (Scanline Mask) Shader
   
   Copyright (C) 2019 guest(r) - [email protected]

   Big thanks to Nesguy from the Libretro forums for the masks and other ideas.
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   
*/

/*   README - MASKS GUIDE

To obtain the best results with masks 0, 1, 3, 4: 
must leave “mask size” at 1 and the display must be set to its native resolution to result in evenly spaced “active” LCD subpixels.

Mask 0: Uses a magenta and green pattern for even spacing of the LCD subpixels.

Mask 1: Intended for displays that have RBG subpixels (as opposed to the more common RGB). 
Uses a yellow/blue pattern for even spacing of the LCD subpixels.

Mask 2: Common red/green/blue pattern.

Mask 3: This is useful for 4K displays, where masks 0 and 1 can look too fine. 
Uses a red/yellow/cyan/blue pattern to result in even spacing of the LCD subpixels.

Mask 4: Intended for displays that have the less common RBG subpixel pattern. 
This is useful for 4K displays, where masks 0 and 1 can look too fine. 
Uses a red/magenta/cyan/green pattern for even spacing of the LCD subpixels.

*/


// Parameter lines go here:
#pragma parameter smart "Smart Y Integer Scaling" 0.0 0.0 1.0 1.0
#pragma parameter brightboost1 "Bright boost dark colors" 1.40 0.5 2.0 0.05
#pragma parameter brightboost2 "Bright boost bright colors" 1.10 0.5 2.0 0.05
#pragma parameter stype "Scanline Type" 0.0 0.0 1.0 1.0
#pragma parameter scanline "Scanline0 intensity" 8.0 4.0 14.0 0.5
#pragma parameter beam_min "Scanline dark" 1.40 0.5 2.0 0.02
#pragma parameter beam_max "Scanline bright" 1.10 0.5 2.0 0.02
#pragma parameter s_gamma "Scanline gamma" 2.4 1.5 3.0 0.05
#pragma parameter h_sharp "Horizontal sharpness" 2.0 1.0 5.0 0.05
#pragma parameter mask "CRT Mask (3&4 are 4k masks)" 0.0 0.0 4.0 1.0
#pragma parameter maskmode "CRT Mask Mode: Classic, Fine, Coarse" 0.0 0.0 2.0 1.0
#pragma parameter maskdark "CRT Mask Strength Dark Pixels" 1.0 0.0 1.5 0.05
#pragma parameter maskbright "CRT Mask Strength Bright Pixels" 0.20 -0.5 1.0 0.05
#pragma parameter masksize "CRT Mask Size" 1.0 1.0 2.0 1.0
#pragma parameter gamma_out "Gamma Out" 2.30 1.0 3.0 0.05

#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 COLOR;
COMPAT_ATTRIBUTE vec4 TexCoord;
COMPAT_VARYING vec4 COL0;
COMPAT_VARYING vec4 TEX0;

vec4 _oPosition1; 
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;

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

#elif defined(FRAGMENT)

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

#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

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;

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

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

#ifdef PARAMETER_UNIFORM
// All parameter floats need to have COMPAT_PRECISION in front of them
uniform COMPAT_PRECISION float smart;
uniform COMPAT_PRECISION float brightboost1;
uniform COMPAT_PRECISION float brightboost2;
uniform COMPAT_PRECISION float scanline;
uniform COMPAT_PRECISION float stype;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float s_gamma;
uniform COMPAT_PRECISION float h_sharp;
uniform COMPAT_PRECISION float mask;
uniform COMPAT_PRECISION float maskmode;
uniform COMPAT_PRECISION float maskdark;
uniform COMPAT_PRECISION float maskbright;
uniform COMPAT_PRECISION float masksize;
uniform COMPAT_PRECISION float gamma_out;
#else
#define smart        0.00     // smart Y integer scaling
#define brightboost1 1.40     // adjust brightness - dark pixels
#define brightboost2 1.10     // adjust brightness - bright pixels
#define scanline     8.00     // scanline param, vertical sharpness
#define stype        1.00     // scanline type
#define beam_min     1.40     // dark area beam min - narrow
#define beam_max     1.10     // bright area beam max - wide
#define s_gamma      2.40     // scanline gamma
#define h_sharp      2.00     // pixel sharpness
#define mask         0.00     // crt mask type
#define maskmode     0.00     // crt mask mode
#define maskdark     1.00     // crt mask strength dark pixels
#define maskbright   0.20     // crt mask strength bright pixels
#define masksize     1.00     // crt mask size
#define gamma_out    2.30     // gamma out
#endif


float bpixels = 0.33*InputSize.y/OutputSize.y;

float st(float x)
{
	return exp2(-10.0*x*x);
}  


vec3 sw0(float x, vec3 color)
{
	color = mix(color, vec3(max(max(color.r,color.g),color.b)), mix(0.6, 0.1, x + 0.25*(beam_min - 1.0)));
	vec3 tmp = mix(vec3(2.75*beam_min),vec3(beam_max), color);
	tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color + 0.25));
	vec3 ex = vec3(x)*tmp;	
	return exp2(-scanline*ex*ex)/(0.7 + 0.3*color);
}

vec3 sw1(float x, vec3 color)
{	
	float mx = max(max(color.r,color.g),color.b);
	color = mix(color, vec3(mx), mix(0.8, 0.3, x + 0.30*(beam_min - 1.0)));
	vec3 ex = mix(vec3(beam_min), vec3(beam_max), color) - 1.0;
	vec3 start = 0.20  - 0.40*ex;
	vec3 end   = 1.50 - 2.20*ex;
	vec3 x1 = smoothstep(start, end, 2.0*vec3(x));
	return (1.0-x1)*mix(1.0, 1.4, (1.0-mx)*max(beam_min - 1.0,0.0));
}

float Overscan(float pos, float dy){
  pos=pos*2.0-1.0;    
  pos*=dy;
  return pos*0.5+0.5;
}

void main()
{
	vec2 tex = TEX0.xy * 1.000001;

	if (smart == 1.0)
	{
		float factor = OutputSize.y/InputSize.y;
		float intfactor = round(factor);
		float diff = factor/intfactor;
		tex.y = Overscan(tex.y*(SourceSize.y/InputSize.y), diff)*(InputSize.y/SourceSize.y); 
	}
	
	vec2 OGL2Pos = tex * SourceSize.xy - vec2(0.5);
	vec2 fp = fract(OGL2Pos);

	vec2 pC4 = (floor(OGL2Pos) + vec2(0.5)) * SourceSize.zw;	
	
	// Reading the texels
	vec3 ul = COMPAT_TEXTURE(Texture, pC4                         ).xyz; ul*=ul;
	vec3 ur = COMPAT_TEXTURE(Texture, pC4 + vec2(SourceSize.z,0.0)).xyz; ur*=ur;
	vec3 dl = COMPAT_TEXTURE(Texture, pC4 + vec2(0.0,SourceSize.w)).xyz; dl*=dl;
	vec3 dr = COMPAT_TEXTURE(Texture, pC4 + SourceSize.zw         ).xyz; dr*=dr;
	
	float lx = fp.x;        lx = pow(lx, h_sharp);
	float rx = 1.0 - fp.x;  rx = pow(rx, h_sharp);
	
	float w = 1.0/(lx+rx);
	float f = fp.y;

	vec3 color;
	float t1 = st(f);
	float t2 = st(1.0-f);

	if (stype == 1.0)
	{
	
		vec3 colorl = sw1(f,ul)*ul + sw1(1.0-f,dl)*dl;
		vec3 colorr = sw1(f,ur)*ur + sw1(1.0-f,dr)*dr;
		color = w*(colorr*lx + colorl*rx);
		color = min(color, 1.0);	
	}

	
	vec3 color1 = w*(ur*lx + ul*rx);
	vec3 color2 = w*(dr*lx + dl*rx);
	
	ul*=ul*ul; ul*=ul;
	ur*=ur*ur; ur*=ur;
	dl*=dl*dl; dl*=dl;
	dr*=dr*dr; dr*=dr;	
	
	vec3 scolor1 = w*(ur*lx + ul*rx); scolor1 = pow(scolor1, vec3(s_gamma*(1.0/12.0)));
	vec3 scolor2 = w*(dr*lx + dl*rx); scolor2 = pow(scolor2, vec3(s_gamma*(1.0/12.0)));

// calculating scanlines
	
	vec3 color0 = color1*t1 + color2*t2;
	vec3 scolor = scolor1*t1 + scolor2*t2;
	
	vec3 ctemp = color0 / (t1 + t2);
	vec3 sctemp = scolor / (t1 + t2);
	
	vec3 cref1 = mix(scolor1, sctemp, 0.35);
	vec3 cref2 = mix(scolor2, sctemp, 0.35);
	
	if (stype == 0.0)
	{
		vec3 w1 = sw0(f,cref1);
		vec3 w2 = sw0(1.0-f,cref2);
		color = color1*w1 + color2*w2;
		color = min(color,1.0);
	}
	
	vec3 scan3 = vec3(0.0);
	float spos = floor((gl_FragCoord.x * 1.000001)/masksize); float spos1 = 0.0;
	vec3 tmp1 = pow(sctemp, vec3(1.5/s_gamma));

	color*=mix(brightboost1, brightboost2, max(max(sctemp.r,sctemp.g),sctemp.b));
	color = min(color,1.0);	
	
	float mboost = 1.25;
	
	if (mask == 0.0)
	{
		spos1 = fract(spos*0.5);
		if      (spos1 < 0.5)  scan3.rb = color.rb;
		else                   scan3.g  = color.g;	
	}
	else
	if (mask == 1.0)
	{
		spos1 = fract(spos*0.5);
		if      (spos1 < 0.5)  scan3.rg = color.rg;
		else                   scan3.b  = color.b;
	}
	else
	if (mask == 2.0)
	{
		mboost = 1.0;
		spos1 = fract(spos/3.0);
		if      (spos1 < 0.333)  scan3.r = color.r;
		else if (spos1 < 0.666)  scan3.g = color.g;
		else                     scan3.b = color.b;
	}
	else
	if (mask == 3.0)
	{
		spos1 = fract(spos*0.25);
		if      (spos1 < 0.25)  scan3.r = color.r;
		else if (spos1 < 0.50)  scan3.rg = color.rg;
		else if (spos1 < 0.75)  scan3.gb = color.gb;	
		else                    scan3.b  = color.b;	
	}
	else	
	{
		spos1 = fract(spos*0.25);
		if      (spos1 < 0.25)  scan3.r = color.r;
		else if (spos1 < 0.50)  scan3.rb = color.rb;
		else if (spos1 < 0.75)  scan3.gb = color.gb;
		else                    scan3.g =  color.g;
	}
	
	vec3 lerpmask = tmp1;
	if (maskmode == 1.0) lerpmask = vec3(max(max(tmp1.r,tmp1.g),tmp1.b)); else
	if (maskmode == 2.0) lerpmask = color;
	
	color = max(mix( mix(color, mboost*scan3, maskdark), mix(color, scan3, maskbright), lerpmask), 0.0);
	
	color = pow(color, vec3(1.0/gamma_out));	
	FragColor = vec4(color, 1.0);
} 
#endif

Edit1: fixed some issues. Edit2: should be complete now.

4 Likes

edit: nvm, I got my screenshots reversed. I see less contrast, lighter scan-lines, more definition of the mask in white. I like it.

1 Like

Thanks for testing it. I guess the shader is finished now. :yum: ‘Smoothstep’ scanlines are quite unintuitive to setup regarding the extra parameters, so more polishing was needed, but i’m pleased with the result. Basically nothing changed with the original shader, only an option was added, so no worries. :upside_down_face:

I really like how the horizontal filtering works with the new scanlines, since it adds some interesting haze on dark/bright pixel situations.

2 Likes

With the mask removed and 4K scale, the scanline differences are still subtle but it’s easier to see what’s going on (stype = 1 on top, stype = 0 below):

The new method has less “roundness” on both axes (that is, they look more square on the corners and there’s very little fade/gradient on the edges of the scanlines themselves), but the bright areas bulge out really nicely on the vertical axis and the image looks altogether a little bit sharper.

3 Likes

This shader looks great! I eagerly await the updated slang version.

1 Like

Looks good but scanlinetype1 results in uneven scanlines (no integer scale). All good using type0

1 Like

@hunterk thanks for testing it in 4k. With the “scanline 1” setup i had higher resolutions in mind and it should also do with (smart) integer scaling / thicker dark scanlines at 1080p.

This image shows “blurry” horizontal filtering which looks quite nice, relatively better than the default setup. I think it gives the shader a different character.

Hey no worries. Glad you like the default setup. :grinning: I also prefer “type0” in some situations.

1 Like

Hi, do you manage to load this shader on android shield tv? I update the shaders throw the buildbot and attempt to load it on shader menu, but it fail to load. The two others guest crt shader in crt directory load fine. Any idea ?

I haven’t gone through and checked it against GLES pickiness yet. I’ll try to get to that tonight.

2 Likes

You’ve the gotten crt guest dr venom glsl shader to work for you on a Shield? I get a black screen and can only hear the audio everytime I try, only the fast version loads up properly on mines. They all work in slang though.

hi, the crt guest dr venom and fast glsl shader work for me on shield tv, i’m up to date in etroarch and shaders. only this new shader gest sm doesn’t load unfortunaly.

That’s strange. I updated to the latest nightly last night and updated my shaders and I still get a black screen when I load up that shader.

Some systems, like N64, needs “stock” in the shader.

Try this:

crt-guest-dr-venom.glslp

shaders = 12

shader0 = ../stock.glsl
filter_linear0 = false
scale_type_x0 = source
scale_type_y0 = absolute
scale_y0 = 240

shader1 = shaders/guest/lut/lut.glsl
filter_linear1 = false
scale_type1 = source
scale1 = 1.0

textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3"
SamplerLUT1 = shaders/guest/lut/sony_trinitron1.png
SamplerLUT1_linear = true 
SamplerLUT2 = shaders/guest/lut/sony_trinitron2.png
SamplerLUT2_linear = true 
SamplerLUT3 = shaders/guest/lut/other1.png
SamplerLUT3_linear = true 

shader2 = shaders/guest/color-profiles.glsl
filter_linear2 = false
scale_type2 = source
scale2 = 1.0

shader3 = shaders/guest/d65-d50.glsl
filter_linear3 = false
scale_type3 = source
scale3 = 1.0

shader4 = ../stock.glsl
filter_linear4 = false
scale_type4 = source
scale4 = 1.0

shader5 = ../stock.glsl
filter_linear5 = false
scale_type5 = source
scale5 = 1.0

shader6 = ../stock.glsl
filter_linear6 = false
scale_type6 = source
scale6 = 1.0

shader7 = shaders/guest/linearize.glsl
filter_linear7 = false
scale_type7 = source
scale7 = 1.0
float_framebuffer7 = true

shader8 = shaders/guest/blur_horiz.glsl
filter_linear8 = false
scale_type8 = source
scale8 = 1.0
float_framebuffer8 = true

shader9 = shaders/guest/blur_vert.glsl
filter_linear9 = false
scale_type9 = source
scale9 = 1.0
float_framebuffer9 = true

shader10 = shaders/guest/linearize_scanlines.glsl
filter_linear10 = true
scale_type10 = source
scale10 = 1.0
float_framebuffer10 = true

shader11 = shaders/guest/crt-guest-dr-venom.glsl
filter_linear11 = true
scale_type11 = viewport
scale_x11 = 1.0
scale_y11 = 1.0
2 Likes

That definitely did the trick, thank you. Would this trick also work with crt royale kurozumi glsl?

2 Likes