Some notes on comparing Trinitron CRT-LCD

Here is some notes after examining my Trinitron closely side by side with my laptop screen (no shader) on 240p suite SNES9x. Both running Retroarch, TV runs on RGB Scart. Maybe help someone in development of accurate shaders.

TV: Gray is warmer, LCD Gray is colder, a tiny bit greenish

TV: Blue is blue, LCD Blue is a bit greenish

240p PLUGE: TV, Dark bars are a bit darker, Gray bars are 30% brighter. LCD: White is not full white!

240p COLOR BARS: TV: Yellow is “fire yellow”, it’s warmer. LCD: Yellow is cold, a bit greenish. There is visible convergence on CRT. 1 pixel red has shifted to left.

TV: Pixel shape is rounded! LCD pixel is block square. Caused by red and green bars shifting up and down and some “ghost pixels” left and right that shape that roundness. Examined 2 cm from screen lol.

Clipboard03

TV: Visible NOISE at dark colors.

Colors after “C” Letter on COLOR BARS merge on TV. Not distinguishable. LCD: all visible.

TV: Red is full red (blood red). As red as it gets. Extremely red :stuck_out_tongue: LCD: Red is a bit brownish, probably has some green in there.

TV: Green is colder, way more than LCD! Definitely shifts to blue a bit.

TV: Colors are saturated 10% more

TV: White is warmer, cyan is more bluish (has less green).

SCANLINES: 240p GRAY RAMP, TV: barely visible at 1m distance (that’s a low TVL CRT to be honest) on luminance less than 50%. 2 highest bars and 2 lowest bars merge, not distinguishable.

5 Likes

I’m a little curious, which model is that Trinitron?

1 Like

It’s Sony KV-M1400D.

2 Likes

A lame attempt to create something with these notes in mind. Shift colors a bit to warm, create “ghost” pixels left and right. Save and apply as Linear

#version 450

layout(push_constant) uniform Push
{
	vec4 SourceSize;
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float blur, MASK, beaml, beamh, scanl, scanh,INGAMMA,OUTGAMMA,SAT, factor;

} params;

#pragma parameter blur "Blur" 0.6 0.0 1.0 0.01
#define blur params.blur
#pragma parameter factor "Scanlines profile" 1.0 0.0 1.0 0.2
#define factor params.factor
#pragma parameter beamh "Beam Dark" 2.0 2.0 15.0 1.0
#define beamh params.beamh
#pragma parameter beaml "Beam Bright" 4.0 2.0 15.0 1.0
#define beaml params.beaml
#pragma parameter scanl "Scanline Dark" 1.85 0.3 2.5 0.05
#define scanl params.scanl
#pragma parameter scanh "Scanline Bright" 1.45 0.3 2.5 0.05
#define scanh params.scanh
#pragma parameter MASK "Mask Brightness" 0.6 0.0 1.0 0.05
#define MASK params.MASK
#pragma parameter INGAMMA "Gamma In" 2.2 1.0 4.0 0.05
#define INGAMMA params.INGAMMA
#pragma parameter OUTGAMMA "Gamma Out" 2.2 1.0 4.0 0.05
#define OUTGAMMA params.OUTGAMMA
#pragma parameter SAT "Saturation" 1.1 0.0 2.0 0.05
#define SAT params.SAT 

#define iTime float(params.FrameCount) / 2.0
#define SourceSize params.SourceSize
#define OutputSize params.OutputSize
#define OriginalSize params.OriginalSize

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 Source;

vec3 mask( float x)
{
	float m = fract(x*0.3333);
	if (m<0.3333) return vec3(MASK,MASK,1.1);
	else if (m<0.6666) return vec3(MASK,0.7,MASK);
	else return vec3(1.15,MASK,MASK);

}

float scan(float y, float l)
{
	float beam = mix(beaml,beamh,l);
	float scan = mix(scanl,scanh,l);
	return exp(-beam*pow(y,3.0*((l*factor+0.2)))*scan);

}

void main()
{	
	vec2 pixel = vec2(0.25/SourceSize.x,0.0);
	vec2 pixely = vec2(0.0,0.125/SourceSize.y);


/// SHARP-BILINEAR  Author: rsn8887
 	vec2 texel = vTexCoord * SourceSize.xy;
   vec2 scale = max(floor(OutputSize.xy / SourceSize.xy), vec2(1.0, 1.0));
   vec2 texel_floored = floor(texel);
   vec2 s = fract(texel);
   vec2 region_range = 0.5 - 0.5 / scale;

   // Figure out where in the texel to sample to get correct pre-scaled bilinear.
   // Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.

   vec2 center_dist = s - 0.5;
   vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;

   vec2 mod_texel = texel_floored + f;
   vec2 uv=mod_texel / SourceSize.xy;

/// "GHOST" PIXELS LEFT/RIGHT CALCULATION
   vec3 col   = texture(Source, uv).rgb;
   vec3 colr  = texture(Source, uv-pixel+pixely).rgb;
   vec3 coll  = texture(Source, uv+pixel).rgb;
   vec3 cold  = texture(Source, uv-pixely).rgb;

	vec3 res = vec3 (col.r*(1.0-blur) + coll.r*blur,
						  col.g*(1.0-blur)                              + cold.g*blur,
						  col.b*(1.0-blur)               + colr.b*blur);

	vec3 lumweight = vec3(max(max(res.r,res.b),res.g)); 
	float lum = dot(vec3(0.22,0.7,0.08),res);
	res = pow(res,vec3(INGAMMA));
	res *= scan(fract(texel.y)-0.5,lumweight.x);
	res *= mask(vTexCoord.x*OutputSize.x);	

	res = pow(res,vec3(1.0/OUTGAMMA));
	res = mix(vec3(lum),res,SAT);

   FragColor = vec4(res,1.0);
}

1 Like

Improved a bit. Worth a try i think.

#version 450

layout(push_constant) uniform Push
{
	vec4 SourceSize;
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float blur, MASK, beaml, beamh, scanl, scanh,INGAMMA,OUTGAMMA,SAT, factor, WP;

} params;

#pragma parameter blur "Pixel Softness" 0.8 0.0 1.0 0.05
#define blur params.blur
#pragma parameter factor "Scanlines profile" 1.0 0.5 1.0 0.1
#define factor params.factor
#pragma parameter beamh "Beam Dark" 2.0 2.0 15.0 1.0
#define beamh params.beamh
#pragma parameter beaml "Beam Bright" 2.0 2.0 15.0 1.0
#define beaml params.beaml
#pragma parameter scanl "Scanline Dark" 1.85 0.3 2.5 0.05
#define scanl params.scanl
#pragma parameter scanh "Scanline Bright" 1.45 0.3 2.5 0.05
#define scanh params.scanh
#pragma parameter MASK "Mask Brightness" 0.6 0.0 1.0 0.05
#define MASK params.MASK
#pragma parameter INGAMMA "Gamma In" 2.2 1.0 4.0 0.05
#define INGAMMA params.INGAMMA
#pragma parameter OUTGAMMA "Gamma Out" 2.2 1.0 4.0 0.05
#define OUTGAMMA params.OUTGAMMA
#pragma parameter SAT "Saturation" 1.1 0.0 2.0 0.05
#define SAT params.SAT 
#pragma parameter WP "Color Temp. Shift" 0.1 -0.25 0.25 0.01
#define WP params.WP
#define iTime float(params.FrameCount) / 2.0
#define SourceSize params.SourceSize
#define OutputSize params.OutputSize
#define OriginalSize params.OriginalSize

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 Source;

    vec3 mask( float x)
    {
    	float m = fract(x*0.3333);
    	if (m<0.3333) return vec3(MASK,MASK,1.0);
    	else if (m<0.6666) return vec3(MASK,1.0,MASK);
    	else return vec3(1.0,MASK,MASK);

    }

    float scan(float y, float l)
    {
    	float beam = mix(beaml,beamh,l);
    	float scan = mix(scanl,scanh,l);
    	return exp(-beam*pow(y,2.0*((l*factor+0.2)))*scan);

    }

    void main()
    {	
    	

    /// SHARP-BILINEAR  Author: rsn8887
     	vec2 texel = vTexCoord * SourceSize.xy;
     	vec2 texel2 = vTexCoord * OutputSize.xy;
       vec2 scale = max(floor(OutputSize.xy / SourceSize.xy), vec2(1.0, 1.0));
       vec2 pixel = vec2(0.5/OutputSize.x,0.0);
    	vec2 pixely = vec2(0.0,0.5/OutputSize.y);

       vec2 texel_floored = floor(texel);
       vec2 texel_floored2 = floor(texel2);
       vec2 s = fract(texel);
       vec2 s2 = fract(texel2);
       vec2 region_range = 0.5 - 0.5 / scale;

       // Figure out where in the texel to sample to get correct pre-scaled bilinear.
       // Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.

       vec2 center_dist = s - 0.5;
       vec2 center_dist2 = s2 - 0.5;
       vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
       vec2 f2 = (center_dist2 - clamp(center_dist2, -region_range, region_range))/scale  + 0.5;

       vec2 mod_texel = texel_floored + f;
       vec2 mod_texel2 = texel_floored2 + f2;
       vec2 uv=mod_texel / SourceSize.xy;
       vec2 uv2=mod_texel2 / OutputSize.xy;

    /// "GHOST" PIXELS LEFT/RIGHT CALCULATION
       vec3 col   = texture(Source, uv).rgb;
       vec3 colr  = texture(Source, uv2-pixel).rgb;
       vec3 coll  = texture(Source, uv2+pixel).rgb;
       vec3 cold  = texture(Source, uv2-pixely).rgb;

    	vec3 res = vec3 (col.r*(1.0-blur) + coll.r*blur,
    						  col.g*(1.0-blur)                              + cold.g*blur,
    						  col.b*(1.0-blur)               + colr.b*blur);

    	vec3 lumweight = vec3(max(max(res.r,res.b),res.g)); 
    	float lum = dot(vec3(0.22,0.7,0.08),res);

    	res = pow(res,vec3(INGAMMA));

    	res *= scan(fract(texel.y)-0.5,lumweight.x);
    	res *= mask(vTexCoord.x*OutputSize.x);	

    	res = pow(res,vec3(1.0/OUTGAMMA));
    	
    	res = mix(vec3(lum),res,SAT);
    	res *= mix(1.0,1.5,lum);
    	res *= vec3(1.0+WP,1.0-WP/2.0,1.0-WP/2.0);
       FragColor = vec4(res,1.0);
    }

ZOOMED IN 16x

PLAIN-NO FILTER

Check the smoothness now on SMW

3 Likes

That looks quite nice (and very fast)!

It looks like you have some accentuation of the black lines, which happens from adding textures in gamma space. You may try those res/col* additions in linear gamma instead to tighten the lines up a bit.

2 Likes

Did a deeper examination on this. I think it’s caused because the formula creates very dark scanlines at places and very bright at others.

return exp(-beam*pow(y,2.0*((l*factor+0.2)))*scan);

l (luminance) could be 0.1 at places and 1.0 next to 0.1. so we get y^(2.0 * 0.1) and y^(2.0 * 1.0). That creates huge difference, while

return exp2(-beam*pow(y,scan));

as used in Guest.r-Dr.Venom creates a much better result without that effect. Moving those res* in linear gamma too erased the problem entirely (still existed if not change formula).

Tried to do my own trick but seems @guest.r was ahead of me lol.

1 Like

Updated to use Guest.R-Dr.Venom scanlines

#version 450

layout(push_constant) uniform Push
{
	vec4 SourceSize;
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float blur, MASK, beaml, beamh, scanl, scanh,INGAMMA,OUTGAMMA,SAT, factor, WP;

} params;

#pragma parameter blur "Pixel Softness" 0.8 0.0 1.0 0.05
#define blur params.blur
#pragma parameter factor "Scanlines profile" 1.0 0.4 1.0 0.2
#define factor params.factor
#pragma parameter beamh "Beam Dark" 2.0 2.0 15.0 1.0
#define beamh params.beamh
#pragma parameter beaml "Beam Bright" 4.0 2.0 15.0 1.0
#define beaml params.beaml
#pragma parameter scanl "Scanline Dark" 1.7 0.3 2.5 0.05
#define scanl params.scanl
#pragma parameter scanh "Scanline Bright" 2.1 0.3 2.5 0.05
#define scanh params.scanh
#pragma parameter MASK "Mask Brightness" 0.6 0.0 1.0 0.05
#define MASK params.MASK
#pragma parameter INGAMMA "Gamma In" 2.2 1.0 4.0 0.05
#define INGAMMA params.INGAMMA
#pragma parameter OUTGAMMA "Gamma Out" 2.2 1.0 4.0 0.05
#define OUTGAMMA params.OUTGAMMA
#pragma parameter SAT "Saturation" 1.1 0.0 2.0 0.05
#define SAT params.SAT 
#pragma parameter WP "Color Temp. Shift" 0.1 -0.25 0.25 0.01
#define WP params.WP
#define iTime float(params.FrameCount) / 2.0
#define SourceSize params.SourceSize
#define OutputSize params.OutputSize
#define OriginalSize params.OriginalSize

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 Source;

vec3 mask( float x)
{
	float m = fract(x*0.3333);
	if (m<0.3333) return vec3(MASK,MASK,1.0);
	else if (m<0.6666) return vec3(MASK,1.0,MASK);
	else return vec3(1.0,MASK,MASK);

}

// GUEST.R-DR.VENOM SCANLINES
float scan(float y, float l)
{
	float beam = mix(beaml,beamh,l);
	float scan = mix(scanl,scanh,l);

	//the less y is (= the more is raised to), the less pronounced scanlines we get.
	return exp2(-beam*pow(y,scan*factor));

}

void main()
{	
	

/// SHARP-BILINEAR  Author: rsn8887
 	vec2 texel = vTexCoord * SourceSize.xy;
 	vec2 texel2 = vTexCoord * OutputSize.xy;
   vec2 scale = max(floor(OutputSize.xy / SourceSize.xy), vec2(1.0, 1.0));
   vec2 pixel = vec2(0.5/OutputSize.x,0.0);
	vec2 pixely = vec2(0.0,0.5/OutputSize.y);

   vec2 texel_floored = floor(texel);
   vec2 texel_floored2 = floor(texel2);
   vec2 s = fract(texel);
   vec2 s2 = fract(texel2);
   vec2 region_range = 0.5 - 0.5 / scale;

   // Figure out where in the texel to sample to get correct pre-scaled bilinear.
   // Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.

   vec2 center_dist = s - 0.5;
   vec2 center_dist2 = s2 - 0.5;
   vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
   vec2 f2 = (center_dist2 - clamp(center_dist2, -region_range, region_range))/scale  + 0.5;

   vec2 mod_texel = texel_floored + f;
   vec2 mod_texel2 = texel_floored2 + f2;
   vec2 uv=mod_texel / SourceSize.xy;
   vec2 uv2=mod_texel2 / OutputSize.xy;

/// "GHOST" PIXELS LEFT/RIGHT CALCULATION
   vec3 col   = texture(Source, uv).rgb;
   vec3 colr  = texture(Source, uv2-pixel).rgb;
   vec3 coll  = texture(Source, uv2+pixel).rgb;
   vec3 cold  = texture(Source, uv2-pixely).rgb;

	vec3 res = vec3 (col.r*(1.0-blur) + coll.r*blur,
						  col.g*(1.0-blur)                              + cold.g*blur,
						  col.b*(1.0-blur)               + colr.b*blur);

	vec3 lumweight = vec3(max(max(res.r,res.b),res.g)); 
	float lum = dot(vec3(0.22,0.7,0.08),res);

	res = pow(res,vec3(INGAMMA));

	res *= scan(fract(texel.y)-0.5,lumweight.x);
	res *= mask(vTexCoord.x*OutputSize.x);	

	res *= mix(1.0,1.5,lum);
	res *= vec3(1.0+WP,1.0-WP/2.0,1.0-WP/2.0);
	res = pow(res,vec3(1.0/OUTGAMMA));
	
	res = mix(vec3(lum),res,SAT);
   FragColor = vec4(res,1.0);
}

3 Likes

Added

  • noise,
  • “soft pixel” size,
  • 2 masks CGWG & BGR,
  • mask size,
  • color shifting,
  • brightness fixes for Sega and Amiga.

#version 450

layout(push_constant) uniform Push
{
	vec4 SourceSize;
	vec4 OriginalSize;
	vec4 OutputSize;
	uint FrameCount;
	float blur, MASK, beaml, beamh, scanl, scanh,INGAMMA,OUTGAMMA,SAT, WPR,WPG,
	      WPB, SIZE, BOOST, MSK_SIZE,Shadowmask, noise, SEGA;

} params;

#pragma parameter SIZE "Soft Pixel Size" 0.5 0.125 4.0 0.125
#define SIZE params.SIZE
#pragma parameter blur "Pixel Softness" 0.7 0.0 1.0 0.05
#define blur params.blur
#pragma parameter beamh "Beam Low" 6.0 2.0 15.0 1.0
#define beamh params.beamh
#pragma parameter beaml "Beam High" 8.0 2.0 15.0 1.0
#define beaml params.beaml
#pragma parameter scanl "Scanline Dark" 1.35 0.3 3.0 0.05
#define scanl params.scanl
#pragma parameter scanh "Scanline Bright" 1.05 0.3 3.0 0.05
#define scanh params.scanh
#pragma parameter Shadowmask "Mask Type" 0.0 -1.0 2.0 1.0
#define Shadowmask params.Shadowmask
#pragma parameter MASK "Mask Brightness" 0.7 0.0 1.0 0.05
#define MASK params.MASK
#pragma parameter MSK_SIZE "Mask Size" 1.0 1.0 2.0 1.0 
#define MSK_SIZE params.MSK_SIZE
#pragma parameter INGAMMA "  Gamma In" 2.4 1.0 4.0 0.05
#define INGAMMA params.INGAMMA
#pragma parameter OUTGAMMA "  Gamma Out" 2.25 1.0 4.0 0.05
#define OUTGAMMA params.OUTGAMMA
#pragma parameter SAT "  Saturation" 1.1 0.0 2.0 0.05
#define SAT params.SAT 
#pragma parameter WPR "  Shift to Red" 0.1 -0.25 0.25 0.01
#define WPR params.WPR
#pragma parameter WPG "  Shift to Green" -0.1 -0.25 0.25 0.01
#define WPG params.WPG
#pragma parameter WPB "  Shift to Blue" 0.1 -0.25 0.25 0.01
#define WPB params.WPB
#pragma parameter BOOST "  Bright Boost" 1.3 1.0 2.0 0.05
#define BOOST params.BOOST
#pragma parameter noise "  Add Noise" 0.2 0.0 1.0 0.01
#define noise params.noise
#pragma parameter SEGA "  Brightness Fix 1.0:Sega, 2:Amiga/ST" 0.0 0.0 2.0 1.0
#define SEGA params.SEGA

#define iTimer (float(params.FrameCount) / 60.0)
#define SourceSize params.SourceSize
#define OutputSize params.OutputSize
#define OriginalSize params.OriginalSize

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 Source;

vec3 mask( float x)
{
	float m = floor(x/MSK_SIZE);

	if (Shadowmask == 0.0)
	{
	m = fract(m*0.5);
	if (m<0.5) return vec3(MASK); else return vec3(1.0);		
	}

	else if (Shadowmask == 1.0)
	{
	m = fract(m*0.3333);
	if (m<0.3333) return vec3(MASK,MASK,1.0);
	else if (m<0.6666) return vec3(MASK,1.0,MASK);
	else return vec3(1.0,MASK,MASK);
   }
   else return vec3(1.0);
}

// GUEST.R-DR.VENOM SCANLINES
float scan(float y, float l)
{
	float beam = mix(beaml,beamh,y);
	float scan = mix(scanl,scanh,l);
   
   //the less y is (= the more is raised to), the less pronounced scanlines we get.
	float ex = y*scan;
	return exp2(-beam*ex*ex);

}

float snow(vec2 pos)
{
    return fract(sin(iTimer * dot(pos.xy ,vec2(13,78.233))) * 43758.5453);
}


void main()
{	
	
	vec2 pos = vTexCoord.xy;
/// SHARP-BILINEAR  Author: rsn8887
 	vec2 texel = pos * SourceSize.xy;
 	vec2 texel2 = pos * OutputSize.xy;
   vec2 scale = max(floor(OutputSize.xy / SourceSize.xy), vec2(1.0, 1.0));
   vec2 pixel = vec2(SIZE/OutputSize.x,0.0);
	vec2 pixely = vec2(0.0,SIZE/OutputSize.y);

   vec2 texel_floored = floor(texel);
   vec2 texel_floored2 = floor(texel2);
   vec2 s = fract(texel);
   vec2 s2 = fract(texel2);
   vec2 region_range = 0.5 - 0.5 / scale;

   // Figure out where in the texel to sample to get correct pre-scaled bilinear.
   // Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.

   vec2 center_dist = s - 0.5;
   vec2 center_dist2 = s2 - 0.5;
   vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * scale + 0.5;
   vec2 f2 = (center_dist2 - clamp(center_dist2, -region_range, region_range))/scale  + 0.5;

   vec2 mod_texel = texel_floored + f;
   vec2 mod_texel2 = texel_floored2 + f2;
   vec2 uv=mod_texel / SourceSize.xy;
   vec2 uv2=mod_texel2 / OutputSize.xy;

/// "GHOST" PIXELS LEFT/RIGHT CALCULATION
   vec3 col   = texture(Source, uv).rgb;
   vec3 colr  = texture(Source, uv2-pixel).rgb;
   vec3 coll  = texture(Source, uv2+pixel).rgb;
   vec3 cold  = texture(Source, uv2-pixely).rgb;

	vec3 res = vec3 (col.r*(1.0-blur) + coll.r*blur,
						  col.g*(1.0-blur)                              + cold.g*blur,
						  col.b*(1.0-blur)               + colr.b*blur);

	vec3 lumweight = vec3(max(max(res.r,res.b),res.g)); 
	float lum = dot(vec3(0.22,0.7,0.08),res);

	res = pow(res,vec3(INGAMMA));

	res = res*scan(fract(texel.y+0.5),lum)+res*scan(1.0-fract(texel.y+0.5),lum) ;
	res *= mask(pos.x*OutputSize.x);	

	res *= mix(1.0,BOOST,lum);
	res *= vec3(1.0+WPR,     1.0-WPR/2.0, 1.0-WPR/2.0);
	res *= vec3(1.0-WPG/2.0, 1.0+WPG,     1.0-WPG/2.0);
	res *= vec3(1.0-WPB/2.0, 1.0-WPB/2.0, 1.0+WPB);
	res *= 1.0 + snow(uv * 2.0) * (1.0-lumweight) *noise;

	res = pow(res,vec3(1.0/OUTGAMMA));
	if (SEGA == 1.0) res *= 1.0625; else if (SEGA == 2.0) res *=2.0; else res;
	res = mix(vec3(lum),res,SAT);
   FragColor = vec4(res,1.0);
}

Untouchables, another Amiga game with dark ST palette fixed

1 Like

For anyone interested this work has been integrated to “crt-sines” slang shader.

4 Likes