From e2036576a26a8e5bfff17c4d7ae2a5ea0e7bd549 Mon Sep 17 00:00:00 2001 From: majorpainthecactus Date: Wed, 29 Dec 2021 20:37:19 +0000 Subject: [PATCH] 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 --- crt/crt-sony-pvm-4k-hdr.slangp | 9 +- crt/shaders/crt-sony-pvm-4k-hdr.slang | 293 ++++++++++++-------------- 2 files changed, 147 insertions(+), 155 deletions(-) diff --git a/crt/crt-sony-pvm-4k-hdr.slangp b/crt/crt-sony-pvm-4k-hdr.slangp index da24770..2e8b948 100644 --- a/crt/crt-sony-pvm-4k-hdr.slangp +++ b/crt/crt-sony-pvm-4k-hdr.slangp @@ -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" \ No newline at end of file diff --git a/crt/shaders/crt-sony-pvm-4k-hdr.slang b/crt/shaders/crt-sony-pvm-4k-hdr.slang index 8d936e3..48809b4 100644 --- a/crt/shaders/crt-sony-pvm-4k-hdr.slang +++ b/crt/shaders/crt-sony-pvm-4k-hdr.slang @@ -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)); + + vec3 Luminance = Ramp3(vec3(Gaussian), (SDRColour * kGuassianMax) + kGuassianMin); - /* TODO: Below needs optimising - just needs a mask array rather than doing if's! Ran out of time */ + vec3 OutputColour; -#if 0 /* 12 pattern - 8K screens */ - float x = ModInteger(floor(InPixels.x), 12.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(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 */ + uint ResolutionPattern = uint(params.ResolutionPattern); -#if 0 /* 9 pattern - 8K screens */ - float x = ModInteger(floor(InPixels.x), 9.0); + switch(ResolutionPattern) + { + 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 ); + + uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize)); + OutputColour = Luminance * HDRColour * Mask[PatternX]; + + 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 ); + + uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize)); + OutputColour = Luminance * HDRColour * Mask[PatternX]; + + 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; + } + } - 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 < 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 */ - -#if 1 /* 6 pattern - 4K screens */ - float x = ModInteger(floor(InPixels.x), 6.0); - - 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 */ - -#if 0 /* 3 pattern - 1080-1440p screens */ - float x = ModInteger(floor(InPixels.x), 3.0); - - 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); - } - 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); }