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 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). 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. 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: To use:
Please Enable HDR in RetroArch, Please Enable HDR in RetroArch,
@ -75,4 +77,7 @@ mipmap_input3 = "false"
alias3 = "" alias3 = ""
float_framebuffer3 = "false" float_framebuffer3 = "false"
srgb_framebuffer3 = "false" srgb_framebuffer3 = "false"
ScanlineWidth = "1.000000"
ScanlineWidth = "0.600000"
Contrast = "4.500000"
PaperWhiteNits = "500.000000"

View file

@ -30,12 +30,13 @@ layout(push_constant) uniform Push
float ScanlineWidth; float ScanlineWidth;
float ScreenWidth; float ScreenWidth;
float ScreenHeight; float ScreenHeight;
float ResolutionPattern;
} params; } 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 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 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 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 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 = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord; layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord; layout(location = 0) out vec2 vTexCoord;
layout(location = 1) out float Scale;
void main() void main()
{ {
gl_Position = global.MVP * Position; gl_Position = global.MVP * Position;
vTexCoord = TexCoord; 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 #pragma stage fragment
layout(location = 0) in vec2 vTexCoord; layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in float Scale;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source; layout(set = 0, binding = 2) uniform sampler2D Source;
layout(set = 0, binding = 3) uniform sampler2D StockPass; layout(set = 0, binding = 3) uniform sampler2D StockPass;
@ -89,165 +70,171 @@ float ModInteger(float a, float b)
#define kEuler 2.718281828459 #define kEuler 2.718281828459
#define kMax 1.0 #define kMax 1.0
#define kGuassianMin 1.1 #define kGuassianMin vec3(1.1)
#define kGuassianMax 3.0 #define kGuassianMax vec3(3.0)
#define Sharpness 2.5 #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) float Ramp(const float gaussian, float colour)
{ {
return clamp(gaussian * colour, 0.0, 1.0); 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() void main()
{ {
float ScanlineSize = params.OutputSize.y / params.SourceSize.y; float ScanlineSize = params.OutputSize.y / params.SourceSize.y;
/* vec2 InPixels = (vTexCoord * params.SourceSize.xy) * vec2(Scale); */
const vec2 InPixels = (vTexCoord * params.OutputSize.xy); const vec2 InPixels = (vTexCoord * params.OutputSize.xy);
const float ScanlinePosition = (floor(vTexCoord.y * params.SourceSize.y) * ScanlineSize) + (ScanlineSize * 0.5); const float ScanlinePosition = (floor(vTexCoord.y * params.SourceSize.y) * ScanlineSize) + (ScanlineSize * 0.5);
float ScanlineDistance = ScanlinePosition - (floor(InPixels.y) + 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 /= ScanlineSize * params.ScanlineWidth;
ScanlineDistance = clamp(abs(ScanlineDistance * 2.0), 0.0, 1.0); 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)); /* 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);
float Ratio = (vTexCoord.x * params.SourceSize.x) - (floor(vTexCoord.x * params.SourceSize.x)); 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); Ratio = clamp(((Ratio - 0.5) * Sharpness) + 0.5, 0.0f, 1.0);
const vec2 SourceTexCoord0 = vec2(vTexCoord.x, ScanlinePosition / params.OutputSize.y); const vec2 SourceTexCoord0 = vec2(vTexCoord.x, ScanlinePosition / params.OutputSize.y);
vec3 hdr_colour0 = texture(Source, SourceTexCoord0).xyz; vec3 HDRColour0 = texture(Source, SourceTexCoord0).xyz;
vec3 sdr_colour0 = texture(StockPass, SourceTexCoord0).xyz; vec3 SDRColour0 = texture(StockPass, SourceTexCoord0).xyz;
const vec2 SourceTexCoord1 = vec2(vTexCoord.x + (1.0 / params.SourceSize.x), ScanlinePosition / params.OutputSize.y); const vec2 SourceTexCoord1 = vec2(vTexCoord.x + (1.0 / params.SourceSize.x), ScanlinePosition / params.OutputSize.y);
vec3 hdr_colour1 = texture(Source, SourceTexCoord1).xyz; const vec3 HDRColour1 = texture(Source, SourceTexCoord1).xyz;
vec3 sdr_colour1 = texture(StockPass, SourceTexCoord1).xyz; const vec3 SDRColour1 = texture(StockPass, SourceTexCoord1).xyz;
vec3 hdr_colour = mix(hdr_colour0, hdr_colour1, vec3(Ratio)); const vec3 HDRColour = mix(HDRColour0, HDRColour1, vec3(Ratio));
vec3 sdr_colour = mix(sdr_colour0, sdr_colour1, 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 */ vec3 OutputColour;
float x = ModInteger(floor(InPixels.x), 12.0);
if(x < 3.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
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin); to get an equivalent TVL seen on a 4:3 CRT TV.
FragColor = vec4(red * hdr_colour.x, 0.0, 0.0, 1.0); Pattern 1's 960TVL at 16:9 is about right for a 800TVL at 4:3.
} */
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 */
#if 0 /* 9 pattern - 8K screens */ uint ResolutionPattern = uint(params.ResolutionPattern);
float x = ModInteger(floor(InPixels.x), 9.0);
if(x < 2.0) switch(ResolutionPattern)
{ {
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin); case 0: /* 2 pattern - 1440TVL (16:9) horiz resolution (too high?) on a 4K screen */
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 */ const uint PatternSize = 2;
} const vec3 Mask[2] = vec3[]( kYellow, kCyan );
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 */
#if 1 /* 6 pattern - 4K screens */ uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
float x = ModInteger(floor(InPixels.x), 6.0); OutputColour = Luminance * HDRColour * Mask[PatternX];
if(x < 2.0) break;
{
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) case 1: /* 3 pattern - 960TVL (16:9) horiz resolution on a 4K screen */
{ {
float green = Ramp(gaussian, (sdr_colour.y * kGuassianMax) + kGuassianMin); const uint PatternSize = 3;
FragColor = vec4(0.0, green * hdr_colour.y, 0.0, 1.0); const vec3 Mask[3] = vec3[]( kRed, kGreen, kBlue );
}
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 */
#if 0 /* 3 pattern - 1080-1440p screens */ uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
float x = ModInteger(floor(InPixels.x), 3.0); OutputColour = Luminance * HDRColour * Mask[PatternX];
if(x < 1.0) break;
{
float red = Ramp(gaussian, (sdr_colour.x * kGuassianMax) + kGuassianMin);
FragColor = vec4(red * hdr_colour.x, 0.0, 0.0, 1.0);
} }
else if(x < 2.0) case 2: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
{ {
float green = Ramp(gaussian, (sdr_colour.y * kGuassianMax) + kGuassianMin); const uint PatternSize = 4;
FragColor = vec4(0.0, green * hdr_colour.y, 0.0, 1.0); const vec3 Mask[4] = vec3[]( kRed, kYellow, kCyan, kBlue );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
} }
else /* if(x < 3.0) */ case 3: /* 5 BRG pattern - 576TVL (16:9) horiz resolution on a 4K screen */
{ {
float blue = Ramp(gaussian, (sdr_colour.z * kGuassianMax) + kGuassianMin); const uint PatternSize = 5;
FragColor = vec4(0.0, 0.0, blue * hdr_colour.z, 1.0); const vec3 Mask[5] = vec3[]( kRed, kMagenta, kBlue, kGreen, kGreen );
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
OutputColour = Luminance * HDRColour * Mask[PatternX];
break;
} }
#endif /* 0 */ 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;
}
}
FragColor = vec4(OutputColour, 1.0);
} }