v1.0 of Sony PVM 4K HDR shader that tries to emulate a Sony PVM 20L4 CRT. Again requires a HDR capable screen typically of at least 700 nits (but may need less for OLEDs) This version adds support for different resolution patterns see shader for more details

This commit is contained in:
majorpainthecactus 2021-12-29 20:37:19 +00:00
parent 46cbd81131
commit e2036576a2
2 changed files with 147 additions and 155 deletions

View file

@ -1,11 +1,13 @@
/*
A group of shaders that tries to emulate a sony PVM type aperture grille screen but with full brightness.
A group of shaders that tries to emulate a Sony PVM type aperture grille screen but with full brightness.
The novel thing about this group of shaders is that it transforms the image output by the 'console/arcade/computer' into HDR space first i.e brightens it first and then applies
an aperture grille afterwards which is kind of what a CRT would actually do - its kind of a kin to the electron beam (but nothing like it lol).
My HDR 700 monitors does seem to get reasonably close to the brightness of my PVM's - its not quite there but its close.
Currently geared towards a Sony PVM 20L4.
To use:
Please Enable HDR in RetroArch,
@ -75,4 +77,7 @@ mipmap_input3 = "false"
alias3 = ""
float_framebuffer3 = "false"
srgb_framebuffer3 = "false"
ScanlineWidth = "1.000000"
ScanlineWidth = "0.600000"
Contrast = "4.500000"
PaperWhiteNits = "500.000000"

View file

@ -27,15 +27,16 @@ layout(push_constant) uniform Push
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount;
float ScanlineWidth;
float ScreenWidth;
float ScreenHeight;
float ScanlineWidth;
float ScreenWidth;
float ScreenHeight;
float ResolutionPattern;
} params;
/* #pragma parameter ScanlineWidth "Scanline Width" 4.0 0.0 20.0 1.0 */
#pragma parameter ScanlineWidth "Scanline Width" 0.5 0.0 1.0 0.01
#pragma parameter ScreenWidth "Screen Width" 3840.0 0.0 7680.0 1.0
#pragma parameter ScreenHeight "Screen Height" 2160.0 0.0 4320.0 1.0
#pragma parameter ScanlineWidth "Scanline Width" 0.5 0.0 1.0 0.01
#pragma parameter ScreenWidth "Screen Width" 3840.0 0.0 7680.0 1.0
#pragma parameter ScreenHeight "Screen Height" 2160.0 0.0 4320.0 1.0
#pragma parameter ResolutionPattern "Resolution Pattern" 1.0 0.0 8.0 1.0
layout(std140, set = 0, binding = 0) uniform UBO
{
@ -46,35 +47,15 @@ layout(std140, set = 0, binding = 0) uniform UBO
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out float Scale;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
vec2 ScreenSize = max(params.OutputSize.xy, vec2(params.ScreenWidth, params.ScreenHeight));
if((params.SourceSize.x > ScreenSize.x) || (params.SourceSize.y > ScreenSize.y))
{
Scale = 1.0;
}
else
{
float ScaleFactor = 2.0;
while(((params.SourceSize.x * ScaleFactor) <= ScreenSize.x) && ((params.SourceSize.y * ScaleFactor) <= ScreenSize.y))
{
ScaleFactor += 1.0;
}
Scale = ScaleFactor - 1.0;
}
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in float Scale;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
layout(set = 0, binding = 3) uniform sampler2D StockPass;
@ -89,165 +70,171 @@ float ModInteger(float a, float b)
#define kEuler 2.718281828459
#define kMax 1.0
#define kGuassianMin 1.1
#define kGuassianMax 3.0
#define kGuassianMin vec3(1.1)
#define kGuassianMax vec3(3.0)
#define Sharpness 2.5
#define kRed vec3(1.0, 0.0, 0.0)
#define kGreen vec3(0.0, 1.0, 0.0)
#define kBlue vec3(0.0, 0.0, 1.0)
#define kMagenta vec3(1.0, 0.0, 1.0)
#define kYellow vec3(1.0, 1.0, 0.0)
#define kCyan vec3(0.0, 1.0, 1.0)
#define kBlack vec3(0.0, 0.0, 0.0)
#define kWhite vec3(1.0, 1.0, 1.0)
float Ramp(const float gaussian, float colour)
{
return clamp(gaussian * colour, 0.0, 1.0);
}
vec3 Ramp3(const vec3 gaussian, vec3 colour)
{
return clamp(gaussian * colour, vec3(0.0), vec3(1.0));
}
void main()
{
float ScanlineSize = params.OutputSize.y / params.SourceSize.y;
/* vec2 InPixels = (vTexCoord * params.SourceSize.xy) * vec2(Scale); */
const vec2 InPixels = (vTexCoord * params.OutputSize.xy);
const float ScanlinePosition = (floor(vTexCoord.y * params.SourceSize.y) * ScanlineSize) + (ScanlineSize * 0.5);
float ScanlineDistance = ScanlinePosition - (floor(InPixels.y) + 0.5);
/*
if(ModInteger(floor(InPixels.y), Scale) < params.ScanlineWidth)
{
FragColor = vec4(texture(Source, vTexCoord).xyz, 1.0);
}
else
{
FragColor = vec4(0.0,0.0,0.0,1.0);
}
*/
ScanlineDistance /= ScanlineSize * params.ScanlineWidth;
ScanlineDistance = clamp(abs(ScanlineDistance * 2.0), 0.0, 1.0);
/* Gaussian distribution */
const float gaussian = pow(kEuler, -0.5 * pow(ScanlineDistance/0.3, 2.0));
//const float guassianf = gaussian * clamp(ScanlineSize / 2, 1.2, 5.0);
//const float guassian_clamp = clamp(guassianf, 0.0, 1.0);
const float Gaussian = pow(kEuler, -0.5 * pow(ScanlineDistance/0.3, 2.0)); /* Gaussian distribution */
float Ratio = (vTexCoord.x * params.SourceSize.x) - (floor(vTexCoord.x * params.SourceSize.x));
Ratio = clamp(((Ratio - 0.5) * Sharpness) + 0.5, 0.0f, 1.0);
const vec2 SourceTexCoord0 = vec2(vTexCoord.x, ScanlinePosition / params.OutputSize.y);
vec3 hdr_colour0 = texture(Source, SourceTexCoord0).xyz;
vec3 sdr_colour0 = texture(StockPass, SourceTexCoord0).xyz;
vec3 HDRColour0 = texture(Source, SourceTexCoord0).xyz;
vec3 SDRColour0 = texture(StockPass, SourceTexCoord0).xyz;
const vec2 SourceTexCoord1 = vec2(vTexCoord.x + (1.0 / params.SourceSize.x), ScanlinePosition / params.OutputSize.y);
vec3 hdr_colour1 = texture(Source, SourceTexCoord1).xyz;
vec3 sdr_colour1 = texture(StockPass, SourceTexCoord1).xyz;
const vec3 HDRColour1 = texture(Source, SourceTexCoord1).xyz;
const vec3 SDRColour1 = texture(StockPass, SourceTexCoord1).xyz;
vec3 hdr_colour = mix(hdr_colour0, hdr_colour1, vec3(Ratio));
vec3 sdr_colour = mix(sdr_colour0, sdr_colour1, vec3(Ratio));
const vec3 HDRColour = mix(HDRColour0, HDRColour1, vec3(Ratio));
const vec3 SDRColour = mix(SDRColour0, SDRColour1, vec3(Ratio));
/* TODO: Below needs optimising - just needs a mask array rather than doing if's! Ran out of time */
vec3 Luminance = Ramp3(vec3(Gaussian), (SDRColour * kGuassianMax) + kGuassianMin);
#if 0 /* 12 pattern - 8K screens */
float x = ModInteger(floor(InPixels.x), 12.0);
vec3 OutputColour;
if(x < 3.0)
{
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin);
FragColor = vec4(red * hdr_colour.x, 0.0, 0.0, 1.0);
}
else if(x < 4.0)
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0); /* black */
}
else if(x < 7.0)
{
float green = Ramp(gaussian, (sdr_colour.y * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, green * hdr_colour.y, 0.0, 1.0);
}
else if(x < 8.0)
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0); /* black */
}
else if(x < 11.0)
{
float blue = Ramp(gaussian, (sdr_colour.z * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, 0.0, blue * hdr_colour.z, 1.0);
}
else /* if(x < 12.0) */
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0); /* black */
}
#endif /* 0 */
/* Various resolution patterns - remember your LCD 4K screen will likely be 16:9 whereas
the CRT TV will likely be 4:3 and so higher TVL values will be required on your 16:9 screen
to get an equivalent TVL seen on a 4:3 CRT TV.
Pattern 1's 960TVL at 16:9 is about right for a 800TVL at 4:3.
*/
#if 0 /* 9 pattern - 8K screens */
float x = ModInteger(floor(InPixels.x), 9.0);
uint ResolutionPattern = uint(params.ResolutionPattern);
if(x < 2.0)
switch(ResolutionPattern)
{
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin);
FragColor = vec4(red * hdr_colour.x, 0.0, 0.0, 1.0);
}
else if(x < 3.0)
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0); /* black */
}
else if(x < 5.0)
{
float green = Ramp(gaussian, (sdr_colour.y * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, green * hdr_colour.y, 0.0, 1.0);
}
else if(x < 6.0)
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0); /* black */
}
else if(x < 8.0)
{
float blue = Ramp(gaussian, (sdr_colour.z * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, 0.0, blue * hdr_colour.z, 1.0);
}
else /* if(x < 9.0) */
{
FragColor = vec4(0.0, 0.0, 0.0, 1.0); /* black */
}
#endif /* 0 */
case 0: /* 2 pattern - 1440TVL (16:9) horiz resolution (too high?) on a 4K screen */
{
const uint PatternSize = 2;
const vec3 Mask[2] = vec3[]( kYellow, kCyan );
#if 1 /* 6 pattern - 4K screens */
float x = ModInteger(floor(InPixels.x), 6.0);
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
if(x < 2.0)
{
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin);
FragColor = vec4(red * hdr_colour.x, 0.0, 0.0, 1.0);
}
else if(x < 4.0)
{
float green = Ramp(gaussian, (sdr_colour.y * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, green * hdr_colour.y, 0.0, 1.0);
}
else /* if(x < 6.0) */
{
float blue = Ramp(gaussian, (sdr_colour.z * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, 0.0, blue * hdr_colour.z, 1.0);
}
#endif /* 1 */
break;
}
case 1: /* 3 pattern - 960TVL (16:9) horiz resolution on a 4K screen */
{
const uint PatternSize = 3;
const vec3 Mask[3] = vec3[]( kRed, kGreen, kBlue );
#if 0 /* 3 pattern - 1080-1440p screens */
float x = ModInteger(floor(InPixels.x), 3.0);
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
if(x < 1.0)
{
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin);
FragColor = vec4(red * hdr_colour.x, 0.0, 0.0, 1.0);
break;
}
case 2: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
{
const uint PatternSize = 4;
const vec3 Mask[4] = vec3[]( kRed, kYellow, kCyan, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
case 3: /* 5 BRG pattern - 576TVL (16:9) horiz resolution on a 4K screen */
{
const uint PatternSize = 5;
const vec3 Mask[5] = vec3[]( kRed, kMagenta, kBlue, kGreen, kGreen );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
case 4: /* 5 pattern - 576TVL (16:9) horiz resolution on a 4K screen */
{
const uint PatternSize = 5;
const vec3 Mask[5] = vec3[]( kRed, kYellow, kGreen, kCyan, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
case 5: /* 6 pattern - 480TVL (16:9) horiz resolution on a 4K screen */
{
const uint PatternSize = 6;
const vec3 Mask[6] = vec3[]( kRed, kRed, kGreen, kGreen, kBlue, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
case 6: /* 7 pattern - 410TVL (16:9) horiz resolution on a 4K screen */
{
const uint PatternSize = 7;
const vec3 Mask[7] = vec3[]( kRed, kRed, kYellow, kGreen, kCyan, kBlue, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
case 7: /* 9 pattern - 640TVL (16:9) horiz resolution on a *8K* screen */
{
const uint PatternSize = 9;
const vec3 Mask[9] = vec3[]( kBlack, kRed, kRed, kBlack, kGreen, kGreen, kBlack, kBlue, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
case 8: /* 12 pattern - 480TVL (16:9) horiz resolution on a *8K* screen */
{
const uint PatternSize = 12;
const vec3 Mask[12] = vec3[]( kBlack, kRed, kRed, kRed, kBlack, kGreen, kGreen, kGreen, kBlack, kBlue, kBlue, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
}
default:
{
OutputColour = vec3(0.0);
break;
}
}
else if(x < 2.0)
{
float green = Ramp(gaussian, (sdr_colour.y * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, green * hdr_colour.y, 0.0, 1.0);
}
else /* if(x < 3.0) */
{
float blue = Ramp(gaussian, (sdr_colour.z * kGuassianMax) + kGuassianMin);
FragColor = vec4(0.0, 0.0, blue * hdr_colour.z, 1.0);
}
#endif /* 0 */
FragColor = vec4(OutputColour, 1.0);
}