New CRT shader from Guest + CRT Guest Advanced updates

I take it this revised version will only work in slang?

I really need to figure out how to get GLcore working…

2 Likes

Sorry to hear about the driver issue. Can make a (unofficial) GLSL version, it’s no hassle. :slight_smile: It’s smooth/sharp span is quite big, plus some ‘cubic’ like filtering is added. Feedback is welcome…:smile:

GLSL version:

/*
   CRT - Guest - SM (Scanline Mask) Shader
   
   Copyright (C) 2019-2020 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.5 0.5 3.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 2.0 1.0
#pragma parameter scanline1 "Scanline Shape Center" 8.0 2.0 14.0 0.5
#pragma parameter scanline2 "Scanline Shape Edges" 8.0 4.0 16.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_beam "Overgrown Bright Beam" 0.75 0.0 1.0 0.05
#pragma parameter cubic "'Cubic' filtering" 0.0 0.0 1.0 1.0
#pragma parameter h_sharp "Horizontal sharpness" 4.60 2.0 8.0 0.20
#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.40 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 stype;
uniform COMPAT_PRECISION float scanline1;
uniform COMPAT_PRECISION float scanline2;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float s_beam;
uniform COMPAT_PRECISION float cubic;
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.50     // adjust brightness - dark pixels
#define brightboost2 1.10     // adjust brightness - bright pixels
#define stype        0.00     // scanline type
#define scanline1    8.00     // scanline shape, center
#define scanline2    8.00     // scanline shape, edges
#define beam_min     1.40     // dark area beam min - narrow
#define beam_max     1.10     // bright area beam max - wide
#define s_beam       0.75     // overgrown bright beam
#define cubic        0.00     // cubic filtering
#define h_sharp      4.60     // 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.40     // gamma out
#endif


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

float st1(float x, float scan)
{
	return exp2(-scan*x*x);
}  

vec3 sw1(float x, vec3 color, float scan)
{
	vec3 tmp = mix(vec3((2.75 - 1.75*stype)*beam_min),vec3(beam_max), color);
	tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color + 0.30));
	vec3 ex = vec3(x)*tmp;
	vec3 res = exp2(-scan*ex*ex);
	float mx = max(max(res.r,res.g),res.b);
	return mix(vec3(mx), res, 0.7 + 0.3*stype);
}

vec3 sw2(float x, vec3 color)
{	
	vec3 ex = mix(vec3(2.0*beam_min), vec3(beam_max), color);
	vec3 m = 0.5*ex;
	ex = x*ex; vec3 xx = ex*ex;
	xx = mix(xx, ex*xx, m);
	vec3 res = exp2(-10.0*xx);
	float mx = max(max(res.r,res.g),res.b);
	return mix(vec3(mx), res, 0.60);
}

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;

	if (smart == 1.0)
	{
		float factor = OutputSize.y/InputSize.y;
		float intfactor = floor(factor + 0.5);
		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
	vec2 dx = vec2(SourceSize.z,0.0);
	vec2 dy = vec2(0.0,SourceSize.w);
	vec2 x2 = dx+dx;
	float zero = mix(0.03, exp2(-h_sharp), cubic);
	
	float wl2 = 1.0 + fp.x;	
	float wl1 =       fp.x;
	float wr1 = 1.0 - fp.x;
	float wr2 = 2.0 - fp.x;

	wl2*=wl2; wl2 = exp2(-h_sharp*wl2);	float sl2 = wl2;
	wl1*=wl1; wl1 = exp2(-h_sharp*wl1);	float sl1 = wl1;
	wr1*=wr1; wr1 = exp2(-h_sharp*wr1);	float sr1 = wr1;
	wr2*=wr2; wr2 = exp2(-h_sharp*wr2);	float sr2 = wr2;
	
	wl2 = max(wl2 - zero, mix(0.0,mix(-0.15, -0.01, fp.x),cubic));
	wl1 = max(wl1 - zero, 0.0);
	wr1 = max(wr1 - zero, 0.0);	
	wr2 = max(wr2 - zero, mix(0.0,mix(-0.15, -0.01, 1.-fp.x),cubic));
	
	float wtt =  1.0/(wl2+wl1+wr1+wr2);
	float wts =  1.0/(sl2+sl1+sr1+sr2);

	vec3 l2 = COMPAT_TEXTURE(Texture, pC4 - dx).xyz; l2*=l2;
	vec3 l1 = COMPAT_TEXTURE(Texture, pC4     ).xyz; l1*=l1;
	vec3 r1 = COMPAT_TEXTURE(Texture, pC4 + dx).xyz; r1*=r1;
	vec3 r2 = COMPAT_TEXTURE(Texture, pC4 + x2).xyz; r2*=r2;
	
	vec3 color1 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt;
	if (cubic == 1.0) color1 = clamp(color1, 0.6*min(l1,r1),1.4*max(l1,r1));
	
	l1*=l1*l1; l1*=l1; r1*=r1*r1; r1*=r1; l2*=l2*l2; l2*=l2; r2*=r2*r2; r2*=r2;
	vec3 scolor1 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts;
	scolor1 = pow(scolor1, vec3(1.0/6.0));
	scolor1 = mix(color1, scolor1, 1.0);
	
	pC4+=dy;
	l2 = COMPAT_TEXTURE(Texture, pC4 - dx).xyz; l2*=l2;
	l1 = COMPAT_TEXTURE(Texture, pC4     ).xyz; l1*=l1;
	r1 = COMPAT_TEXTURE(Texture, pC4 + dx).xyz; r1*=r1;
	r2 = COMPAT_TEXTURE(Texture, pC4 + x2).xyz; r2*=r2;
	
	vec3 color2 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt;
	if (cubic == 1.0) color2 = clamp(color2, 0.6*min(l1,r1),1.4*max(l1,r1));

	l1*=l1*l1; l1*=l1; r1*=r1*r1; r1*=r1; l2*=l2*l2; l2*=l2; r2*=r2*r2; r2*=r2;
	vec3 scolor2 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts;
	scolor2 = pow(scolor2, vec3(1.0/6.0));
	scolor2 = mix(color2, scolor2, 1.0);
	
	float f1 = fp.y;
	float f2 = 1.0 - fp.y;
	
	vec3 color;
	float t1 = st(f1);
	float t2 = st(f2);
	float wt = 1.0/(t1+t2);
	
// calculating scanlines

	float scan1 = mix(scanline1, scanline2, f1);
	float scan2 = mix(scanline1, scanline2, f2);
	
	vec3 sctemp = (t1*scolor1 + t2*scolor2)/(t1+t2);
	
	vec3 ref1 = mix(sctemp, scolor1, s_beam);
	vec3 ref2 = mix(sctemp, scolor2, s_beam);	
	
	vec3 w1, w2 = vec3(0.0);

	if (stype < 2.0)
	{
		w1 = sw1(f1, ref1, scan1);
		w2 = sw1(f2, ref2, scan2);
	}
	else
	{
		w1 = sw2(f1, ref1);
		w2 = sw2(f2, ref2);
	}

	color = w1*color1 + w2*color2;
	color = min(color,1.0);	
	
	vec3 ctemp = (t1*color1 + t2*color2)*wt;
		
	vec3 scan3 = vec3(0.0);
	float spos = floor((gl_FragCoord.x * 1.000001)/masksize); float spos1 = 0.0;

	vec3 tmp1 = mix(ctemp, sctemp, 0.75);
	tmp1 = 0.5*(sqrt(tmp1) + tmp1);
	
	color*=mix(brightboost1, brightboost2, max(max(ctemp.r,ctemp.g),ctemp.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);
	
	vec3 color1g = pow(color, vec3(1.0/2.1));

	if (stype != 1.0)
	{
		vec3 color2g = pow(color, vec3(1.0/gamma_out));			
		float mx1 = max(max(color1g.r,color1g.g),color1g.b) + 1e-12;	
		float mx2 = max(max(color2g.r,color2g.g),color2g.b);
		color1g*=mx2/mx1;		
	}
	
	FragColor = vec4(color1g, 1.0);
} 
#endif

Edit: Silly scanline dark bug fixed.

4 Likes

Yes, this looks fantastic! Much wider smooth/sharp range, as you said. It also seems like this basically eliminates the color fringing / chromatic aberration you get when using the mask. Awesome!

Quick question: If I want an exact 50/50 ratio of scanlines to black space, what should the scanline settings be? (I like slightly less black space than a 50/50 ratio, but just wondering what would be a good starting point). I like to take a more exact approach than just eyeballing it. :slight_smile:

2 Likes

A good starting point would be to test some settings first i think. I setup a shader in which relative to black scanline strength can be tested. It get’s funny after 60% ^^.

Here is a test version, hope it fits the problem.

Edit: obsolete test version removed.

1 Like

I think @Nesguy was asking how to get 1:1 (50/50) scanlines (perfect scanlines?) with settings, so he could then adjust his settings from there to get the look he wants (slightly imperfect scanlines).

Correct me if I’m @Nesguy, that’s just how I read what you asked.

2 Likes

First thanks @Syh for a good understanding. I was under the impression that he would like to have a bit more brightness while still having scanlines lol. :rofl:

This on is a bit tough. The best there is is to select scanline type 1 and max out beam min and max values. It’s the only ‘neutral’ type. Other two prefer broader bright and thinner dark scanlines. Ofc. scanline shapes can be used to get stronger 'lines. Edges shape to the max, and center shape to correct the beam. It should give nice results i think. You could also extend the parameter domain in the header of the shader.

like:

#pragma parameter beam_min "Scanline dark" 1.40 0.5 3.0 0.02
#pragma parameter beam_max "Scanline bright" 1.10 0.5 3.0 0.02

Adding this trick by replacing a line in the shader also helps a lot!

//color = w1*color1 + w2*color2;
color = w1*w1*color1 + w2*w2*color2;
3 Likes

Here are some examples using my current “max backlight” settings, following @guest.r’s suggestions. This is without the “color trick.”

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;brightboost1;brightboost2;stype;scanline1;scanline2;beam_min;beam_max;s_beam;cubic;h_sharp;mask;maskmode;maskdark;maskbright;masksize;gamma_out"
WP = "0.000000"
wp_saturation = "1.000000"
smart = "1.000000"
brightboost1 = "1.500000"
brightboost2 = "1.500000"
stype = "0.000000"
scanline1 = "14.000000"
scanline2 = "16.000000"
beam_min = "1.400000"
beam_max = "1.100000"
s_beam = "0.000000"
cubic = "0.000000"
h_sharp = "3.999999"
mask = "0.000000"
maskmode = "0.000000"
maskdark = "1.000000"
maskbright = "0.500000"
masksize = "1.000000"
gamma_out = "2.600000"
4 Likes

For easier and faster tweaking you can use the “Scanline Cutoff” param. Unfortunatelly there isn’t much playroom at 1080p, but the results are much better with higher scaling ratios.

/*
   CRT - Guest - SM (Scanline Mask) Shader
   
   Copyright (C) 2019-2020 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.5 0.5 3.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 2.0 1.0
#pragma parameter scanline1 "Scanline Shape Center" 8.0 2.0 14.0 0.5
#pragma parameter scanline2 "Scanline Shape Edges" 8.0 4.0 16.0 0.5
#pragma parameter beam_min "Scanline dark" 1.40 0.5 3.0 0.02
#pragma parameter beam_max "Scanline bright" 1.10 0.5 3.0 0.02
#pragma parameter cutoff "Scanline Cutoff" 0.00 0.00 0.90 0.01
#pragma parameter s_beam "Overgrown Bright Beam" 0.75 0.0 1.0 0.05
#pragma parameter cubic "'Cubic' filtering" 0.0 0.0 1.0 1.0
#pragma parameter h_sharp "Horizontal sharpness" 4.60 2.0 8.0 0.20
#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.40 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 stype;
uniform COMPAT_PRECISION float scanline1;
uniform COMPAT_PRECISION float scanline2;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float cutoff;
uniform COMPAT_PRECISION float s_beam;
uniform COMPAT_PRECISION float cubic;
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.50     // adjust brightness - dark pixels
#define brightboost2 1.10     // adjust brightness - bright pixels
#define stype        0.00     // scanline type
#define scanline1    8.00     // scanline shape, center
#define scanline2    8.00     // scanline shape, edges
#define beam_min     1.40     // dark area beam min - narrow
#define beam_max     1.10     // bright area beam max - wide
#define cutoff       0.00     // scanline darkening ratio
#define s_beam       0.75     // overgrown bright beam
#define cubic        0.00     // cubic filtering
#define h_sharp      4.60     // 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.40     // gamma out
#endif


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

float st1(float x, float scan)
{
	return exp2(-scan*x*x);
}  

vec3 sw1(float x, vec3 color, float scan)
{
	vec3 tmp = mix(vec3((2.75 - 1.75*stype)*beam_min),vec3(beam_max), color);
	tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color + 0.30));
	vec3 ex = vec3(x)*tmp;
	vec3 res = exp2(-scan*ex*ex);
	float mx = max(max(res.r,res.g),res.b);
	return mix(vec3(mx), res, 0.7 + 0.3*stype);
}

vec3 sw2(float x, vec3 color)
{	
	vec3 ex = mix(vec3(2.0*beam_min), vec3(beam_max), color);
	vec3 m = 0.5*ex;
	ex = x*ex; vec3 xx = ex*ex;
	xx = mix(xx, ex*xx, m);
	vec3 res = exp2(-10.0*xx);
	float mx = max(max(res.r,res.g),res.b);
	return mix(vec3(mx), res, 0.60);
}

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;

	if (smart == 1.0)
	{
		float factor = OutputSize.y/InputSize.y;
		float intfactor = floor(factor + 0.5);
		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
	vec2 dx = vec2(SourceSize.z,0.0);
	vec2 dy = vec2(0.0,SourceSize.w);
	vec2 x2 = dx+dx;
	float zero = mix(0.03, exp2(-h_sharp), cubic);
	
	float wl2 = 1.0 + fp.x;	
	float wl1 =       fp.x;
	float wr1 = 1.0 - fp.x;
	float wr2 = 2.0 - fp.x;

	wl2*=wl2; wl2 = exp2(-h_sharp*wl2);	float sl2 = wl2;
	wl1*=wl1; wl1 = exp2(-h_sharp*wl1);	float sl1 = wl1;
	wr1*=wr1; wr1 = exp2(-h_sharp*wr1);	float sr1 = wr1;
	wr2*=wr2; wr2 = exp2(-h_sharp*wr2);	float sr2 = wr2;
	
	wl2 = max(wl2 - zero, mix(0.0,mix(-0.15, -0.01, fp.x),cubic));
	wl1 = max(wl1 - zero, 0.0);
	wr1 = max(wr1 - zero, 0.0);	
	wr2 = max(wr2 - zero, mix(0.0,mix(-0.15, -0.01, 1.-fp.x),cubic));
	
	float wtt =  1.0/(wl2+wl1+wr1+wr2);
	float wts =  1.0/(sl2+sl1+sr1+sr2);

	vec3 l2 = COMPAT_TEXTURE(Texture, pC4 - dx).xyz; l2*=l2;
	vec3 l1 = COMPAT_TEXTURE(Texture, pC4     ).xyz; l1*=l1;
	vec3 r1 = COMPAT_TEXTURE(Texture, pC4 + dx).xyz; r1*=r1;
	vec3 r2 = COMPAT_TEXTURE(Texture, pC4 + x2).xyz; r2*=r2;
	
	vec3 color1 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt;
	if (cubic == 1.0) color1 = clamp(color1, 0.6*min(l1,r1),1.4*max(l1,r1));
	
	l1*=l1*l1; l1*=l1; r1*=r1*r1; r1*=r1; l2*=l2*l2; l2*=l2; r2*=r2*r2; r2*=r2;
	vec3 scolor1 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts;
	scolor1 = pow(scolor1, vec3(1.0/6.0));
	scolor1 = mix(color1, scolor1, 1.0);
	
	pC4+=dy;
	l2 = COMPAT_TEXTURE(Texture, pC4 - dx).xyz; l2*=l2;
	l1 = COMPAT_TEXTURE(Texture, pC4     ).xyz; l1*=l1;
	r1 = COMPAT_TEXTURE(Texture, pC4 + dx).xyz; r1*=r1;
	r2 = COMPAT_TEXTURE(Texture, pC4 + x2).xyz; r2*=r2;
	
	vec3 color2 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt;
	if (cubic == 1.0) color2 = clamp(color2, 0.6*min(l1,r1),1.4*max(l1,r1));

	l1*=l1*l1; l1*=l1; r1*=r1*r1; r1*=r1; l2*=l2*l2; l2*=l2; r2*=r2*r2; r2*=r2;
	vec3 scolor2 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts;
	scolor2 = pow(scolor2, vec3(1.0/6.0));
	scolor2 = mix(color2, scolor2, 1.0);
	
	float f1 = fp.y;
	float f2 = 1.0 - fp.y;
	
	vec3 color;
	float t1 = st(f1);
	float t2 = st(f2);
	float wt = 1.0/(t1+t2);
	
// calculating scanlines

	float scan1 = mix(scanline1, scanline2, f1);
	float scan2 = mix(scanline1, scanline2, f2);
	
	vec3 sctemp = (t1*scolor1 + t2*scolor2)*wt;
	vec3 ctemp = (t1*color1 + t2*color2)*wt;
	
	vec3 ref1 = mix(sctemp, scolor1, s_beam);
	vec3 ref2 = mix(sctemp, scolor2, s_beam);	
	
	vec3 w1, w2 = vec3(0.0);

	if (stype < 2.0)
	{
		w1 = sw1(f1, ref1, scan1);
		w2 = sw1(f2, ref2, scan2);
	}
	else
	{
		w1 = sw2(f1, ref1);
		w2 = sw2(f2, ref2);
	}
	
	w1 = clamp((w1-cutoff)/(1.0-cutoff), 0.0005, 1.0);
	w2 = clamp((w2-cutoff)/(1.0-cutoff), 0.0005, 1.0);	
	
	color = w1*color1 + w2*color2;
	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 = mix(ctemp, sctemp, 0.75);
	tmp1 = 0.5*(sqrt(tmp1) + tmp1);
	
	color*=mix(brightboost1, brightboost2, max(max(ctemp.r,ctemp.g),ctemp.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);
	
	vec3 color1g = pow(color, vec3(1.0/2.1));

	if (stype != 1.0)
	{
		vec3 color2g = pow(color, vec3(1.0/gamma_out));			
		float mx1 = max(max(color1g.r,color1g.g),color1g.b) + 1e-12;	
		float mx2 = max(max(color2g.r,color2g.g),color2g.b);
		color1g*=mx2/mx1;		
	}
	
	FragColor = vec4(color1g, 1.0);
} 
#endif

Edit: silly beam min bug fixed, please refresh (also above version).

5 Likes

Don’t know if you made any changes regarding the scanlines type1 but now they look good on my setup, using latest glsl version, no more uneven scanlines. Awesome!

1 Like

Yes, this scanline type needed a rework. The look got better too IMO. :slightly_smiling_face:

Mr. @Syh mentioned interlacing with crt-guest-dr-venom, but i’m rather testing some approaches with this shader, because it’s more simple to add. I used interlacing for a nice vertical blur and some anti-aliasing, which is welcome at 2x internal resolution. Currently, higher resolutions aren’t supported, because the horizontal filter would fail at 2D elements. Probably it’s a good idea to use a smoothing shader and a dotmask addon for higher resolutions.

I also added an option to use scanlines at 2x resolution. The 3D elements look better defined, otherwise the 2D should look very alike as at 1x res.

The parameter for the new options is at the bottom in the menu, feedback is welcome so maybe i can improve it somewhat. :wink:

Almost forgot to mention, at 2x resolution, sharpness is 2x stronger, so the parameter for it should be a bit lower.

/*
   CRT - Guest - SM (Scanline Mask) Shader
   
   Copyright (C) 2019-2020 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.4 0.5 3.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 2.0 1.0
#pragma parameter scanline1 "Scanline Shape Center" 8.0 2.0 14.0 0.5
#pragma parameter scanline2 "Scanline Shape Edges" 8.0 4.0 16.0 0.5
#pragma parameter beam_min "Scanline dark" 1.30 0.5 2.5 0.05
#pragma parameter beam_max "Scanline bright" 1.10 0.5 2.5 0.05
#pragma parameter cutoff "Scanline Cutoff" 0.00 0.00 0.90 0.02
#pragma parameter s_beam "Overgrown Bright Beam" 0.75 0.0 1.0 0.05
#pragma parameter cubic "'Cubic' filtering" 0.0 0.0 1.0 0.05
#pragma parameter h_sharp "Horizontal sharpness" 4.60 0.4 8.0 0.20
#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.40 1.0 3.0 0.05
#pragma parameter intr "Internal Resolution / Interlace" 1.0 1.0 3.0 1.0

#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 stype;
uniform COMPAT_PRECISION float scanline1;
uniform COMPAT_PRECISION float scanline2;
uniform COMPAT_PRECISION float beam_min;
uniform COMPAT_PRECISION float beam_max;
uniform COMPAT_PRECISION float cutoff;
uniform COMPAT_PRECISION float s_beam;
uniform COMPAT_PRECISION float cubic;
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;
uniform COMPAT_PRECISION float intr;
#else
#define smart        0.00     // smart Y integer scaling
#define brightboost1 1.50     // adjust brightness - dark pixels
#define brightboost2 1.10     // adjust brightness - bright pixels
#define stype        0.00     // scanline type
#define scanline1    8.00     // scanline shape, center
#define scanline2    8.00     // scanline shape, edges
#define beam_min     1.40     // dark area beam min - narrow
#define beam_max     1.10     // bright area beam max - wide
#define cutoff       0.00     // scanline darkening ratio
#define s_beam       0.75     // overgrown bright beam
#define cubic        0.00     // cubic filtering
#define h_sharp      4.60     // 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.40     // gamma out
#define intr         1.00     // internal resolution
#endif


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

float st1(float x, float scan)
{
	return exp2(-scan*x*x);
}  

vec3 sw1(float x, vec3 color, float scan)
{
	vec3 tmp = mix(vec3((2.75 - 1.75*stype)*beam_min),vec3(beam_max), color);
	tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color + 0.30));
	vec3 ex = vec3(x)*tmp;
	vec3 res = exp2(-scan*ex*ex);
	float mx = max(max(res.r,res.g),res.b);
	return mix(vec3(mx), res, 0.7 + 0.3*stype);
}

vec3 sw2(float x, vec3 color)
{	
	vec3 ex = mix(vec3(2.0*beam_min), vec3(beam_max), color);
	vec3 m = 0.5*ex;
	ex = x*ex; vec3 xx = ex*ex;
	xx = mix(xx, ex*xx, m);
	vec3 res = exp2(-10.0*xx);
	float mx = max(max(res.r,res.g),res.b);
	return mix(vec3(mx), res, 0.60);
}

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;

	if (smart == 1.0)
	{
		float factor = OutputSize.y/InputSize.y;	
		float intfactor = floor(factor + 0.5);	
		float diff = factor/intfactor;
		tex.y = Overscan(tex.y*(SourceSize.y/InputSize.y), diff)*(InputSize.y/SourceSize.y); 
	}
	
	
	float frame_no = floor(mod(float(FrameCount),2.0));	
	vec2 ps = SourceSize.zw *((intr < 2.0) ? vec2(1.0,1.0) : vec2(1.0,2.0));
	
	vec2 OGL2Pos = tex / ps - vec2(0.5,0.5);
	vec2 fp = fract(OGL2Pos);
	
	vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps;	

	vec2 dx = vec2(SourceSize.z,0.0);
	vec2 dy = vec2(0.0,SourceSize.w);
	vec2 x2 = dx+dx;

	if (intr == 2.0) pC4.y = tex.y;
	if (intr == 3.0) { pC4.y = tex.y - 0.4*SourceSize.w; dy*=0.8; }
	
	float zero = mix(0.03, exp2(-h_sharp), cubic);
	
	float wl2 = 1.0 + fp.x;	
	float wl1 =       fp.x;
	float wr1 = 1.0 - fp.x;
	float wr2 = 2.0 - fp.x;

	wl2*=wl2; wl2 = exp2(-h_sharp*wl2);	float sl2 = wl2;
	wl1*=wl1; wl1 = exp2(-h_sharp*wl1);	float sl1 = wl1;
	wr1*=wr1; wr1 = exp2(-h_sharp*wr1);	float sr1 = wr1;
	wr2*=wr2; wr2 = exp2(-h_sharp*wr2);	float sr2 = wr2;
	
	wl2 = max(wl2 - zero, mix(0.0,mix(-0.15, -0.01, fp.x),cubic));
	wl1 = max(wl1 - zero, 0.0);
	wr1 = max(wr1 - zero, 0.0);	
	wr2 = max(wr2 - zero, mix(0.0,mix(-0.15, -0.01, 1.-fp.x),cubic));
	
	float wtt =  1.0/(wl2+wl1+wr1+wr2);
	float wts =  1.0/(sl2+sl1+sr1+sr2);

	vec3 l2 = COMPAT_TEXTURE(Texture, pC4 - dx).xyz; l2*=l2;
	vec3 l1 = COMPAT_TEXTURE(Texture, pC4     ).xyz; l1*=l1;
	vec3 r1 = COMPAT_TEXTURE(Texture, pC4 + dx).xyz; r1*=r1;
	vec3 r2 = COMPAT_TEXTURE(Texture, pC4 + x2).xyz; r2*=r2;
	
	vec3 color1 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt;
	if (cubic == 1.0) color1 = clamp(color1, 0.6*min(l1,r1),1.4*max(l1,r1));
	
	l1*=l1*l1; l1*=l1; r1*=r1*r1; r1*=r1; l2*=l2*l2; l2*=l2; r2*=r2*r2; r2*=r2;
	vec3 scolor1 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts;
	scolor1 = pow(scolor1, vec3(1.0/6.0));
	scolor1 = mix(color1, scolor1, 1.0);
	
	pC4+=dy;
	l2 = COMPAT_TEXTURE(Texture, pC4 - dx).xyz; l2*=l2;
	l1 = COMPAT_TEXTURE(Texture, pC4     ).xyz; l1*=l1;
	r1 = COMPAT_TEXTURE(Texture, pC4 + dx).xyz; r1*=r1;
	r2 = COMPAT_TEXTURE(Texture, pC4 + x2).xyz; r2*=r2;
	
	vec3 color2 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt;
	if (cubic == 1.0) color2 = clamp(color2, 0.6*min(l1,r1),1.4*max(l1,r1));

	l1*=l1*l1; l1*=l1; r1*=r1*r1; r1*=r1; l2*=l2*l2; l2*=l2; r2*=r2*r2; r2*=r2;
	vec3 scolor2 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts;
	scolor2 = pow(scolor2, vec3(1.0/6.0));
	scolor2 = mix(color2, scolor2, 1.0);
	
	float f1 = fp.y;
	float f2 = 1.0 - fp.y;
	
	vec3 color;
	float t1 = st(f1);
	float t2 = st(f2);
	float wt = 1.0/(t1+t2);
	
// calculating scanlines

	float scan1 = mix(scanline1, scanline2, f1);
	float scan2 = mix(scanline1, scanline2, f2);
	
	vec3 sctemp = (t1*scolor1 + t2*scolor2)*wt;
	vec3 ctemp = (t1*color1 + t2*color2)*wt;
	
	vec3 ref1 = mix(sctemp, scolor1, s_beam);
	vec3 ref2 = mix(sctemp, scolor2, s_beam);	
	
	vec3 w1, w2 = vec3(0.0);

	if (stype < 2.0)
	{
		w1 = sw1(f1, ref1, scan1);
		w2 = sw1(f2, ref2, scan2);
	}
	else
	{
		w1 = sw2(f1, ref1);
		w2 = sw2(f2, ref2);
	}
	
	w1 = clamp((w1-cutoff)/(1.0-cutoff), 0.0001, 1.0);
	w2 = clamp((w2-cutoff)/(1.0-cutoff), 0.0001, 1.0);	
	
	color = w1*color1 + w2*color2;
	if (intr == 2.0) { color = color1*(w1+w2); ctemp = color1; sctemp = scolor1; }
	if (intr == 3.0) { color = mix(color1, color2, frame_no);  ctemp = color; mix(scolor1, scolor2, frame_no); }	
	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 = mix(ctemp, sctemp, 0.75);
	tmp1 = 0.5*(sqrt(tmp1) + tmp1);
	
	color*=mix(brightboost1, brightboost2, max(max(ctemp.r,ctemp.g),ctemp.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);
	
	vec3 color1g = pow(color, vec3(1.0/2.1));

	if (stype != 1.0 && intr < 3.0)
	{
		vec3 color2g = pow(color, vec3(1.0/gamma_out));			
		float mx1 = max(max(color1g.r,color1g.g),color1g.b) + 1e-12;	
		float mx2 = max(max(color2g.r,color2g.g),color2g.b);
		color1g*=mx2/mx1;		
	}
	
	FragColor = vec4(color1g, 1.0);
} 
#endif

Edit: much nicer interlacing

5 Likes

This makes me both happy and sad.

1 Like

@guest.r Soooo I re-read you post, and you said;

“Mr. @Syh mentioned interlacing with crt-guest-dr-venom, but i’m rather testing some approaches with this shader, because it’s more simple to add.”

Does this mean we may possibly get interlacing or w/e with crt-guest-dr-venom eventually after you’ve tested and found a method you like for this?

1 Like

@Nesguy very nice screenie! I also like the ‘cubic’ filtering effect, it’s very fast also, couple of tweaks in addition to the standard filter.

@Syh: yes, it’s in the works. But before i’d like to have some feedback on the interlacing implementation. Personaly, i like it as it is, a bit softer with some temporal smoothing. Crt-guest-dr-venom will be retweaked for some higher resolution content and updated if you guys concur.

1 Like

@guest.r

Is it possible to add a third option to the “smart integer Y scaling”?

0 = off
1 = scales to largest size where all of the image is still visible
2 = overscans the image

For example, on a 1080p display, if the output size is 240 pixels high, you’d get 1200 pixels high with option 2, which would result in some overscan while still displaying the entire “CRT safe area” (just like on an actual CRT!).

All of the examples I’ve posted are 5x vertical scale with some overscan.

3 Likes

I like the version of smart integer scaling found in crt-guest-dr-venom, @guest.r . Can you please consider including it in crt-guest-sm? FYI I use slang.

1 Like

Been thinking about it also, but it’s a per-frame situation, not ony per-game one.

At output resolution, hundreds of pixels would need to be checked for a decent solution and the results could be strange, stretching and shrinking the image in different situations.

For more even scanlines my vertical downsampler could be useful, among some solutions i checked this was the most useful one. :sweat_smile:

@c9f5fdda06

Heya there, i might add the both smart scaling options. I avoided it because i wanted an optimized shader, but why not.

5 Likes

Noticed that the Scanline Shape Center and Edges parameters don’t seem to do anything for scanline type 2. I assume it’s meant like that?

1 Like

Yeah, default settings are different for type 2 and the desired shape is hard-coded into the scanline function. Scanline beam altering depending on pixel brightness still work trough parameters though. It would mean no great deal to “fix” this though…

I like the shape of type 2. If the extra configurability (Shape center and edges) can be applied without losing any of the currect characteristics than that would be nice, but otherwise I’m fine with it. Don’t fix it if it aint broken :slight_smile:

1 Like

@guest.r

Scanline type 0, by far the most accurate type for the aperture grille masks, looks perfect with brighter content. However, now I’m playing Resident Evil 2 and it looks a bit too dark. What’s the best way to get brighter highlights while maintaining the overall scanline shape and variation? Then again, RE2 is a very dark game and the display I’m using has pretty mediocre picture quality to begin with, so it could just come down to that.

EDIT: nvm, it’s just the crappy display I’m using :stuck_out_tongue:

1 Like