From 35c2d7064e7a7a9bec76048ed753ea74fe8c1ba0 Mon Sep 17 00:00:00 2001 From: hunterk Date: Sat, 16 Jan 2021 09:31:32 -0600 Subject: [PATCH] add crt-hyllian and crt-guest updates (#166) * add TATE mode to mame_hlsl * add crt-guest-dr-venom2 shaders and presets * get rid of weird character in filename * add updated crt-hyllian shaders and presets * update guest's deconvergence to latest * more gdv updates * forgot one --- blurs/smart-blur.slang | 11 +- crt/crt-guest-dr-venom2.slangp | 85 ++ crt/crt-hyllian-3d.slangp | 2 +- crt/crt-hyllian-curvature.slangp | 4 + crt/crt-hyllian-glow.slangp | 109 ++- crt/crt-hyllian-multipass.slangp | 4 +- crt/crt-hyllian-sinc-glow.slangp | 77 ++ crt/crt-hyllian-sinc.slangp | 4 + crt/crt-hyllian.slangp | 2 +- crt/shaders/crt-hyllian.slang | 185 ----- crt/shaders/guest/afterglow.slang | 16 +- .../guest/crt-gdv-new/afterglow0.slang | 76 ++ .../guest/crt-gdv-new/avg-lum-ntsc.slang | 88 ++ crt/shaders/guest/crt-gdv-new/avg-lum.slang | 112 +++ .../guest/crt-gdv-new/bloom_horizontal.slang | 99 +++ .../guest/crt-gdv-new/bloom_vertical.slang | 102 +++ .../crt-guest-dr-venom2-hires.slang | 729 +++++++++++++++++ .../crt-guest-dr-venom2-ntsc.slang | 729 +++++++++++++++++ .../crt-gdv-new/crt-guest-dr-venom2.slang | 756 ++++++++++++++++++ .../guest/crt-gdv-new/deconvergence.slang | 123 +++ .../crt-gdv-new/gaussian_horizontal.slang | 101 +++ .../guest/crt-gdv-new/gaussian_vertical.slang | 100 +++ .../guest/crt-gdv-new/linearize-ntsc.slang | 163 ++++ crt/shaders/guest/crt-gdv-new/linearize.slang | 133 +++ .../crt-gdv-new/pre-shaders-afterglow.slang | 316 ++++++++ .../guest/fast/smoothing - kopija.slang | 84 ++ crt/shaders/guest/fast/smoothing.slang | 6 +- crt/shaders/guest/linearize.slang | 7 +- crt/shaders/guest/lut/custom_lut.png | Bin 0 -> 78916 bytes .../{ => hyllian}/crt-hyllian-3d.slang | 0 .../hyllian/crt-hyllian-curvature.slang | 260 ++++++ .../crt-hyllian-glow/crt-hyllian.slang | 0 .../crt-hyllian-glow/resolve2.slang | 0 .../crt-hyllian-pass0.slang | 0 .../crt-hyllian-pass1.slang | 0 crt/shaders/hyllian/crt-hyllian-sinc.slang | 197 +++++ crt/shaders/hyllian/crt-hyllian.slang | 214 +++++ .../mame_hlsl/shaders/mame_parameters.inc | 5 +- crt/shaders/mame_hlsl/shaders/mame_post.slang | 3 +- .../mame_hlsl/shaders/mame_scanline.slang | 4 +- dithering/shaders/sgenpt-mix.slang | 112 ++- presets/crt-guest-dr-venom2-hires.slangp | 64 ++ presets/crt-guest-dr-venom2-ntsc.slangp | 81 ++ presets/crt-hyllian-curvature-ntsc.slangp | 100 +++ .../crt-hyllian-sinc-smartblur-sgenpt.slangp | 63 ++ presets/crt-hyllian-smartblur-sgenpt.slangp | 63 ++ 46 files changed, 5117 insertions(+), 272 deletions(-) create mode 100644 crt/crt-guest-dr-venom2.slangp create mode 100644 crt/crt-hyllian-curvature.slangp create mode 100644 crt/crt-hyllian-sinc-glow.slangp create mode 100644 crt/crt-hyllian-sinc.slangp delete mode 100644 crt/shaders/crt-hyllian.slang create mode 100644 crt/shaders/guest/crt-gdv-new/afterglow0.slang create mode 100644 crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang create mode 100644 crt/shaders/guest/crt-gdv-new/avg-lum.slang create mode 100644 crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang create mode 100644 crt/shaders/guest/crt-gdv-new/bloom_vertical.slang create mode 100644 crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang create mode 100644 crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang create mode 100644 crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang create mode 100644 crt/shaders/guest/crt-gdv-new/deconvergence.slang create mode 100644 crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang create mode 100644 crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang create mode 100644 crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang create mode 100644 crt/shaders/guest/crt-gdv-new/linearize.slang create mode 100644 crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang create mode 100644 crt/shaders/guest/fast/smoothing - kopija.slang create mode 100644 crt/shaders/guest/lut/custom_lut.png rename crt/shaders/{ => hyllian}/crt-hyllian-3d.slang (100%) create mode 100644 crt/shaders/hyllian/crt-hyllian-curvature.slang rename crt/shaders/{ => hyllian}/crt-hyllian-glow/crt-hyllian.slang (100%) rename crt/shaders/{ => hyllian}/crt-hyllian-glow/resolve2.slang (100%) rename crt/shaders/{ => hyllian}/crt-hyllian-multipass/crt-hyllian-pass0.slang (100%) rename crt/shaders/{ => hyllian}/crt-hyllian-multipass/crt-hyllian-pass1.slang (100%) create mode 100644 crt/shaders/hyllian/crt-hyllian-sinc.slang create mode 100644 crt/shaders/hyllian/crt-hyllian.slang create mode 100644 presets/crt-guest-dr-venom2-hires.slangp create mode 100644 presets/crt-guest-dr-venom2-ntsc.slangp create mode 100644 presets/crt-hyllian-curvature-ntsc.slangp create mode 100644 presets/crt-hyllian-sinc-smartblur-sgenpt.slangp create mode 100644 presets/crt-hyllian-smartblur-sgenpt.slangp diff --git a/blurs/smart-blur.slang b/blurs/smart-blur.slang index c67aa46..0b048c0 100644 --- a/blurs/smart-blur.slang +++ b/blurs/smart-blur.slang @@ -3,7 +3,8 @@ /* Hyllian Smart-Blur Shader - Copyright (C) 2011-2016 Hyllian - sergiogdb@gmail.com + Copyright (C) 2011-2020 Hyllian - sergiogdb@gmail.com + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -27,11 +28,13 @@ layout(push_constant) uniform Push vec4 OriginalSize; vec4 OutputSize; uint FrameCount; + float SB_BLUR_LEVEL; float SB_RED_THRESHOLD; float SB_GREEN_THRESHOLD; float SB_BLUE_THRESHOLD; } params; +#pragma parameter SB_BLUR_LEVEL "Smart Blur Level" 0.66 0.00 1.00 0.02 #pragma parameter SB_RED_THRESHOLD "Smart Blur Red Threshold" 0.2 0.0 0.6 0.01 #pragma parameter SB_GREEN_THRESHOLD "Smart Blur Green Threshold" 0.2 0.0 0.6 0.01 #pragma parameter SB_BLUE_THRESHOLD "Smart Blur Blue Threshold" 0.2 0.0 0.6 0.01 @@ -99,9 +102,9 @@ void main() if (eq(E,F) && eq(E,H) && eq(E,I) && eq(E,B) && eq(E,C) && eq(E,A) && eq(E,D) && eq(E,G)) { - sum = (E+A+C+D+F+G+I+B+H)/9.0; - E = sum; + sum = (A+C+D+F+G+I+B+H)/8.0; + E = mix(E, sum, params.SB_BLUR_LEVEL); } FragColor = vec4(E, 1.0); -} \ No newline at end of file +} diff --git a/crt/crt-guest-dr-venom2.slangp b/crt/crt-guest-dr-venom2.slangp new file mode 100644 index 0000000..cc82eaa --- /dev/null +++ b/crt/crt-guest-dr-venom2.slangp @@ -0,0 +1,85 @@ +shaders = 11 + +shader0 = ../stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 +alias0 = StockPass + +shader1 = shaders/guest/crt-gdv-new/afterglow0.slang +filter_linear1 = false +scale_type1 = source +scale1 = 1.0 +alias1 = AfterglowPass + +shader2 = shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang +filter_linear2 = false +scale_type2 = source +scale2 = 1.0 +alias2 = PrePass + +textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4" +SamplerLUT1 = shaders/guest/lut/sony_trinitron1.png +SamplerLUT1_linear = true +SamplerLUT2 = shaders/guest/lut/sony_trinitron2.png +SamplerLUT2_linear = true +SamplerLUT3 = shaders/guest/lut/other1.png +SamplerLUT3_linear = true +SamplerLUT4 = shaders/guest/lut/custom_lut.png +SamplerLUT4_linear = true + +shader3 = shaders/guest/crt-gdv-new/avg-lum.slang +filter_linear3 = true +scale_type3 = source +scale3 = 1.0 +mipmap_input3 = true +alias3 = AvgLumPass + +shader4 = shaders/guest/crt-gdv-new/linearize.slang +filter_linear4 = true +scale_type4 = source +scale4 = 1.0 +alias4 = LinearizePass +float_framebuffer4 = true # comment this line for max precision + +shader5 = shaders/guest/crt-gdv-new/gaussian_horizontal.slang +filter_linear5 = true +scale_type_x5 = viewport +scale_x5 = 0.5 +scale_type_y5 = source +scale_y5 = 1.0 + +shader6 = shaders/guest/crt-gdv-new/gaussian_vertical.slang +filter_linear6 = true +scale_type_x6 = viewport +scale_x6 = 0.5 +scale_type_y6 = viewport +scale_y6 = 0.5 +alias6 = GlowPass + +shader7 = shaders/guest/crt-gdv-new/bloom_horizontal.slang +filter_linear7 = true +scale_type_x7 = source +scale_x7 = 1.0 +scale_type_y7 = source +scale_y7 = 1.0 + +shader8 = shaders/guest/crt-gdv-new/bloom_vertical.slang +filter_linear8 = true +scale_type_x8 = source +scale_x8 = 1.0 +scale_type_y8 = source +scale_y8 = 1.0 +alias8 = BloomPass + +shader9 = shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang +filter_linear9 = true +scale_type9 = viewport +scale_x9 = 1.0 +scale_y9 = 1.0 + +shader10 = shaders/guest/crt-gdv-new/deconvergence.slang +filter_linear10 = true +scale_type10 = viewport +scale_x10 = 1.0 +scale_y10 = 1.0 diff --git a/crt/crt-hyllian-3d.slangp b/crt/crt-hyllian-3d.slangp index 0b877d9..b13bc59 100644 --- a/crt/crt-hyllian-3d.slangp +++ b/crt/crt-hyllian-3d.slangp @@ -1,4 +1,4 @@ shaders = 1 -shader0 = shaders/crt-hyllian-3d.slang +shader0 = shaders/hyllian/crt-hyllian-3d.slang filter_linear0 = true diff --git a/crt/crt-hyllian-curvature.slangp b/crt/crt-hyllian-curvature.slangp new file mode 100644 index 0000000..5530cde --- /dev/null +++ b/crt/crt-hyllian-curvature.slangp @@ -0,0 +1,4 @@ +shaders = 1 + +shader0 = shaders/hyllian/crt-hyllian-curvature.slang +filter_linear0 = false diff --git a/crt/crt-hyllian-glow.slangp b/crt/crt-hyllian-glow.slangp index 0b44c78..78106db 100644 --- a/crt/crt-hyllian-glow.slangp +++ b/crt/crt-hyllian-glow.slangp @@ -1,30 +1,79 @@ -shaders = 6 - -shader0 = shaders/glow/linearize.slang -filter_linear0 = false -srgb_framebuffer0 = true - -shader1 = shaders/crt-hyllian-glow/crt-hyllian.slang -filter_linear1 = false -scale_type1 = viewport -scale1 = 1.0 -srgb_framebuffer1 = true -alias1 = CRT_PASS - -shader2 = shaders/glow/threshold.slang -filter_linear2 = false -srgb_framebuffer2 = true - -shader3 = shaders/glow/blur_horiz.slang -mipmap_input3 = true -filter_linear3 = true -scale_type3 = source -scale3 = 0.25 -srgb_framebuffer3 = true - -shader4 = shaders/glow/blur_vert.slang -filter_linear4 = true -srgb_framebuffer4 = true - -shader5 = shaders/crt-hyllian-glow/resolve2.slang -filter_linear5 = true \ No newline at end of file +shaders = "6" +shader0 = "shaders/glow/linearize.slang" +filter_linear0 = "false" +wrap_mode0 = "clamp_to_border" +mipmap_input0 = "false" +alias0 = "" +float_framebuffer0 = "false" +srgb_framebuffer0 = "true" +shader1 = "shaders/hyllian/crt-hyllian.slang" +filter_linear1 = "false" +wrap_mode1 = "clamp_to_border" +mipmap_input1 = "false" +alias1 = "CRTPass" +float_framebuffer1 = "false" +srgb_framebuffer1 = "true" +scale_type_x1 = "viewport" +scale_x1 = "1.000000" +scale_type_y1 = "viewport" +scale_y1 = "1.000000" +shader2 = "shaders/glow/threshold.slang" +filter_linear2 = "false" +wrap_mode2 = "clamp_to_border" +mipmap_input2 = "false" +alias2 = "" +float_framebuffer2 = "false" +srgb_framebuffer2 = "true" +shader3 = "shaders/glow/blur_horiz.slang" +filter_linear3 = "true" +wrap_mode3 = "clamp_to_border" +mipmap_input3 = "true" +alias3 = "" +float_framebuffer3 = "false" +srgb_framebuffer3 = "true" +scale_type_x3 = "viewport" +scale_x3 = "0.200000" +scale_type_y3 = "viewport" +scale_y3 = "0.200000" +shader4 = "shaders/glow/blur_vert.slang" +filter_linear4 = "true" +wrap_mode4 = "clamp_to_border" +mipmap_input4 = "false" +alias4 = "" +float_framebuffer4 = "false" +srgb_framebuffer4 = "true" +shader5 = "shaders/glow/resolve.slang" +filter_linear5 = "true" +wrap_mode5 = "clamp_to_border" +mipmap_input5 = "false" +alias5 = "" +float_framebuffer5 = "false" +srgb_framebuffer5 = "false" +parameters = "INPUT_GAMMA;BEAM_PROFILE;HFILTER_PROFILE;BEAM_MIN_WIDTH;BEAM_MAX_WIDTH;SCANLINES_STRENGTH;COLOR_BOOST;HFILTER_SHARPNESS;PHOSPHOR_LAYOUT;MASK_INTENSITY;CRT_ANTI_RINGING;InputGamma;OutputGamma;VSCANLINES;GLOW_WHITEPOINT;GLOW_ROLLOFF;BLOOM_STRENGTH;OUTPUT_GAMMA;CURVATURE;warpX;warpY;cornersize;cornersmooth;noise_amt;shadowMask;maskDark;maskLight" +INPUT_GAMMA = "2.400000" +BEAM_PROFILE = "0.000000" +HFILTER_PROFILE = "0.000000" +BEAM_MIN_WIDTH = "0.860000" +BEAM_MAX_WIDTH = "1.000000" +SCANLINES_STRENGTH = "0.580000" +COLOR_BOOST = "1.250000" +HFILTER_SHARPNESS = "1.000000" +PHOSPHOR_LAYOUT = "4.000000" +MASK_INTENSITY = "0.500000" +CRT_ANTI_RINGING = "1.000000" +InputGamma = "1.000000" +OutputGamma = "1.000000" +VSCANLINES = "0.000000" +GLOW_WHITEPOINT = "0.500000" +GLOW_ROLLOFF = "1.200000" +BLOOM_STRENGTH = "0.100000" +OUTPUT_GAMMA = "2.200000" +CURVATURE = "0.000000" +warpX = "0.031000" +warpY = "0.031000" +cornersize = "0.030000" +cornersmooth = "1000.000000" +noise_amt = "1.000000" +shadowMask = "0.000000" +maskDark = "0.500000" +maskLight = "1.500000" diff --git a/crt/crt-hyllian-multipass.slangp b/crt/crt-hyllian-multipass.slangp index 0f81106..aad902d 100644 --- a/crt/crt-hyllian-multipass.slangp +++ b/crt/crt-hyllian-multipass.slangp @@ -1,6 +1,6 @@ shaders = 2 -shader0 = shaders/crt-hyllian-multipass/crt-hyllian-pass0.slang +shader0 = shaders/hyllian/crt-hyllian-multipass/crt-hyllian-pass0.slang filter_linear0 = false srgb_framebuffer0 = true scale_type_x0 = viewport @@ -8,7 +8,7 @@ scale_type_y0 = source scale_x0 = 1.0 scale_y0 = 1.0 -shader1 = shaders/crt-hyllian-multipass/crt-hyllian-pass1.slang +shader1 = shaders/hyllian/crt-hyllian-multipass/crt-hyllian-pass1.slang filter_linear1 = false # Uncomment these lines for a sharper variation diff --git a/crt/crt-hyllian-sinc-glow.slangp b/crt/crt-hyllian-sinc-glow.slangp new file mode 100644 index 0000000..7e113cc --- /dev/null +++ b/crt/crt-hyllian-sinc-glow.slangp @@ -0,0 +1,77 @@ +shaders = "6" +shader0 = "shaders/glow/linearize.slang" +filter_linear0 = "false" +wrap_mode0 = "clamp_to_border" +mipmap_input0 = "false" +alias0 = "" +float_framebuffer0 = "false" +srgb_framebuffer0 = "true" +shader1 = "shaders/hyllian/crt-hyllian-sinc.slang" +filter_linear1 = "false" +wrap_mode1 = "clamp_to_border" +mipmap_input1 = "false" +alias1 = "CRTPass" +float_framebuffer1 = "false" +srgb_framebuffer1 = "true" +scale_type_x1 = "viewport" +scale_x1 = "1.000000" +scale_type_y1 = "viewport" +scale_y1 = "1.000000" +shader2 = "shaders/glow/threshold.slang" +filter_linear2 = "false" +wrap_mode2 = "clamp_to_border" +mipmap_input2 = "false" +alias2 = "" +float_framebuffer2 = "false" +srgb_framebuffer2 = "true" +shader3 = "shaders/glow/blur_horiz.slang" +filter_linear3 = "true" +wrap_mode3 = "clamp_to_border" +mipmap_input3 = "true" +alias3 = "" +float_framebuffer3 = "false" +srgb_framebuffer3 = "true" +scale_type_x3 = "viewport" +scale_x3 = "0.200000" +scale_type_y3 = "viewport" +scale_y3 = "0.200000" +shader4 = "shaders/glow/blur_vert.slang" +filter_linear4 = "true" +wrap_mode4 = "clamp_to_border" +mipmap_input4 = "false" +alias4 = "" +float_framebuffer4 = "false" +srgb_framebuffer4 = "true" +shader5 = "shaders/glow/resolve.slang" +filter_linear5 = "true" +wrap_mode5 = "clamp_to_border" +mipmap_input5 = "false" +alias5 = "" +float_framebuffer5 = "false" +srgb_framebuffer5 = "false" +parameters = "INPUT_GAMMA;BEAM_PROFILE;BEAM_MIN_WIDTH;BEAM_MAX_WIDTH;SCANLINES_STRENGTH;COLOR_BOOST;HFILTER_SHARPNESS;PHOSPHOR_LAYOUT;MASK_INTENSITY;CRT_ANTI_RINGING;InputGamma;OutputGamma;VSCANLINES;GLOW_WHITEPOINT;GLOW_ROLLOFF;BLOOM_STRENGTH;OUTPUT_GAMMA;CURVATURE;warpX;warpY;cornersize;cornersmooth;noise_amt;shadowMask;maskDark;maskLight" +INPUT_GAMMA = "2.400000" +BEAM_PROFILE = "0'.000000" +BEAM_MIN_WIDTH = "0.860000" +BEAM_MAX_WIDTH = "1.000000" +SCANLINES_STRENGTH = "0.720000" +COLOR_BOOST = "1.250000" +PHOSPHOR_LAYOUT = "4.000000" +MASK_INTENSITY = "0.500000" +CRT_ANTI_RINGING = "1.000000" +InputGamma = "1.000000" +OutputGamma = "1.000000" +VSCANLINES = "0.000000" +GLOW_WHITEPOINT = "0.500000" +GLOW_ROLLOFF = "1.200000" +BLOOM_STRENGTH = "0.100000" +OUTPUT_GAMMA = "2.200000" +CURVATURE = "0.000000" +warpX = "0.031000" +warpY = "0.031000" +cornersize = "0.030000" +cornersmooth = "1000.000000" +noise_amt = "1.000000" +shadowMask = "0.000000" +maskDark = "0.500000" +maskLight = "1.500000" diff --git a/crt/crt-hyllian-sinc.slangp b/crt/crt-hyllian-sinc.slangp new file mode 100644 index 0000000..c739cf3 --- /dev/null +++ b/crt/crt-hyllian-sinc.slangp @@ -0,0 +1,4 @@ +shaders = 1 + +shader0 = shaders/hyllian/crt-hyllian-sinc.slang +filter_linear0 = false diff --git a/crt/crt-hyllian.slangp b/crt/crt-hyllian.slangp index 380e93d..4007389 100644 --- a/crt/crt-hyllian.slangp +++ b/crt/crt-hyllian.slangp @@ -1,4 +1,4 @@ shaders = 1 -shader0 = shaders/crt-hyllian.slang +shader0 = shaders/hyllian/crt-hyllian.slang filter_linear0 = false diff --git a/crt/shaders/crt-hyllian.slang b/crt/shaders/crt-hyllian.slang deleted file mode 100644 index df49c9a..0000000 --- a/crt/shaders/crt-hyllian.slang +++ /dev/null @@ -1,185 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - float PHOSPHOR; - float VSCANLINES; - float InputGamma; - float OutputGamma; - float SHARPNESS; - float COLOR_BOOST; - float RED_BOOST; - float GREEN_BOOST; - float BLUE_BOOST; - float SCANLINES_STRENGTH; - float BEAM_MIN_WIDTH; - float BEAM_MAX_WIDTH; - float CRT_ANTI_RINGING; -} param; - -#pragma parameter PHOSPHOR "CRT - Phosphor ON/OFF" 1.0 0.0 1.0 1.0 -#pragma parameter VSCANLINES "CRT - Scanlines Direction" 0.0 0.0 1.0 1.0 -#pragma parameter InputGamma "CRT - Input gamma" 2.5 0.0 5.0 0.1 -#pragma parameter OutputGamma "CRT - Output Gamma" 2.2 0.0 5.0 0.1 -#pragma parameter SHARPNESS "CRT - Sharpness Hack" 1.0 1.0 5.0 1.0 -#pragma parameter COLOR_BOOST "CRT - Color Boost" 1.5 1.0 2.0 0.05 -#pragma parameter RED_BOOST "CRT - Red Boost" 1.0 1.0 2.0 0.01 -#pragma parameter GREEN_BOOST "CRT - Green Boost" 1.0 1.0 2.0 0.01 -#pragma parameter BLUE_BOOST "CRT - Blue Boost" 1.0 1.0 2.0 0.01 -#pragma parameter SCANLINES_STRENGTH "CRT - Scanline Strength" 0.50 0.0 1.0 0.02 -#pragma parameter BEAM_MIN_WIDTH "CRT - Min Beam Width" 0.86 0.0 1.0 0.02 -#pragma parameter BEAM_MAX_WIDTH "CRT - Max Beam Width" 1.0 0.0 1.0 0.02 -#pragma parameter CRT_ANTI_RINGING "CRT - Anti-Ringing" 0.8 0.0 1.0 0.1 - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 OutputSize; - vec4 OriginalSize; - vec4 SourceSize; -} 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 = 1) in vec2 FragCoord; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -/* - Hyllian's CRT Shader - - Copyright (C) 2011-2016 Hyllian - sergiogdb@gmail.com - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#define GAMMA_IN(color) pow(color, vec3(param.InputGamma, param.InputGamma, param.InputGamma)) -#define GAMMA_OUT(color) pow(color, vec3(1.0 / param.OutputGamma, 1.0 / param.OutputGamma, 1.0 / param.OutputGamma)) - -// Horizontal cubic filter. - -// Some known filters use these values: - -// B = 0.0, C = 0.0 => Hermite cubic filter. -// B = 1.0, C = 0.0 => Cubic B-Spline filter. -// B = 0.0, C = 0.5 => Catmull-Rom Spline filter. This is the default used in this shader. -// B = C = 1.0/3.0 => Mitchell-Netravali cubic filter. -// B = 0.3782, C = 0.3109 => Robidoux filter. -// B = 0.2620, C = 0.3690 => Robidoux Sharp filter. -// B = 0.36, C = 0.28 => My best config for ringing elimination in pixel art (Hyllian). - -// For more info, see: http://www.imagemagick.org/Usage/img_diagrams/cubic_survey.gif - -// Change these params to configure the horizontal filter. -float B = 0.0; -float C = 0.5; - -mat4x4 invX = mat4x4( - (-B - 6.0*C)/6.0, (3.0*B + 12.0*C)/6.0, (-3.0*B - 6.0*C)/6.0, B/6.0, - (12.0 - 9.0*B - 6.0*C)/6.0, (-18.0 + 12.0*B + 6.0*C)/6.0, 0.0, (6.0 - 2.0*B)/6.0, - -(12.0 - 9.0*B - 6.0*C)/6.0, (18.0 - 15.0*B - 12.0*C)/6.0, (3.0*B + 6.0*C)/6.0, B/6.0, - (B + 6.0*C)/6.0, -C, 0.0, 0.0 -); - - - -void main() -{ - vec3 color; - - vec2 TextureSize = vec2(param.SHARPNESS*global.SourceSize.x, global.SourceSize.y); - - vec2 dx = mix(vec2(1.0/TextureSize.x, 0.0), vec2(0.0, 1.0/TextureSize.y), param.VSCANLINES); - vec2 dy = mix(vec2(0.0, 1.0/TextureSize.y), vec2(1.0/TextureSize.x, 0.0), param.VSCANLINES); - - vec2 pix_coord = vTexCoord*TextureSize + vec2(-0.5, 0.5); - - vec2 tc = mix((floor(pix_coord) + vec2(0.5, 0.5))/TextureSize, (floor(pix_coord) + vec2(1.0, -0.5))/TextureSize, param.VSCANLINES); - - vec2 fp = mix(fract(pix_coord), fract(pix_coord.yx), param.VSCANLINES); - - vec3 c00 = GAMMA_IN(texture(Source, tc - dx - dy).xyz); - vec3 c01 = GAMMA_IN(texture(Source, tc - dy).xyz); - vec3 c02 = GAMMA_IN(texture(Source, tc + dx - dy).xyz); - vec3 c03 = GAMMA_IN(texture(Source, tc + 2.0*dx - dy).xyz); - vec3 c10 = GAMMA_IN(texture(Source, tc - dx ).xyz); - vec3 c11 = GAMMA_IN(texture(Source, tc ).xyz); - vec3 c12 = GAMMA_IN(texture(Source, tc + dx ).xyz); - vec3 c13 = GAMMA_IN(texture(Source, tc + 2.0*dx ).xyz); - - // Get min/max samples - vec3 min_sample = min(min(c01, c11), min(c02, c12)); - vec3 max_sample = max(max(c01, c11), max(c02, c12)); - - mat4x3 color_matrix0 = mat4x3(c00, c01, c02, c03); - mat4x3 color_matrix1 = mat4x3(c10, c11, c12, c13); - - vec4 invX_Px = vec4(fp.x*fp.x*fp.x, fp.x*fp.x, fp.x, 1.0) * invX; - vec3 color0 = color_matrix0 * invX_Px; - vec3 color1 = color_matrix1 * invX_Px; - - // Anti-ringing - vec3 aux = color0; - color0 = clamp(color0, min_sample, max_sample); - color0 = mix(aux, color0, param.CRT_ANTI_RINGING); - aux = color1; - color1 = clamp(color1, min_sample, max_sample); - color1 = mix(aux, color1, param.CRT_ANTI_RINGING); - - float pos0 = fp.y; - float pos1 = 1 - fp.y; - - vec3 lum0 = mix(vec3(param.BEAM_MIN_WIDTH), vec3(param.BEAM_MAX_WIDTH), color0); - vec3 lum1 = mix(vec3(param.BEAM_MIN_WIDTH), vec3(param.BEAM_MAX_WIDTH), color1); - - vec3 d0 = clamp(pos0/(lum0 + 0.0000001), 0.0, 1.0); - vec3 d1 = clamp(pos1/(lum1 + 0.0000001), 0.0, 1.0); - - d0 = exp(-10.0*param.SCANLINES_STRENGTH*d0*d0); - d1 = exp(-10.0*param.SCANLINES_STRENGTH*d1*d1); - - color = clamp(color0*d0 + color1*d1, 0.0, 1.0); - - color *= param.COLOR_BOOST*vec3(param.RED_BOOST, param.GREEN_BOOST, param.BLUE_BOOST); - - float mod_factor = mix(vTexCoord.x * global.OutputSize.x, vTexCoord.y * global.OutputSize.y, param.VSCANLINES); - - vec3 dotMaskWeights = mix( - vec3(1.0, 0.7, 1.0), - vec3(0.7, 1.0, 0.7), - floor(mod(mod_factor, 2.0)) - ); - - color.rgb *= mix(vec3(1.0), dotMaskWeights, param.PHOSPHOR); - - color = GAMMA_OUT(color); - - FragColor = vec4(color, 1.0); -} diff --git a/crt/shaders/guest/afterglow.slang b/crt/shaders/guest/afterglow.slang index 1f34b5e..a77ed8f 100644 --- a/crt/shaders/guest/afterglow.slang +++ b/crt/shaders/guest/afterglow.slang @@ -23,24 +23,18 @@ layout(push_constant) uniform Push { - float SW, AR, PR, AG, PG, AB, PB, sat; + float AS, PR, PG, PB, sat; } params; -#pragma parameter SW "Afterglow switch ON/OFF" 1.0 0.0 1.0 1.0 -#pragma parameter AR "Afterglow Red (more is more)" 0.07 0.0 1.0 0.01 +#pragma parameter AS "Afterglow Strength" 0.07 0.0 1.0 0.01 #pragma parameter PR "Persistence Red (more is less)" 0.05 0.0 1.0 0.01 -#pragma parameter AG "Afterglow Green" 0.07 0.0 1.0 0.01 #pragma parameter PG "Persistence Green" 0.05 0.0 1.0 0.01 -#pragma parameter AB "Afterglow Blue" 0.07 0.0 1.0 0.01 #pragma parameter PB "Persistence Blue" 0.05 0.0 1.0 0.01 #pragma parameter sat "Afterglow saturation" 0.10 0.0 1.0 0.01 -#define SW params.SW -#define AR params.AR +#define AS params.AS #define PR params.PR -#define AG params.AG #define PG params.PG -#define AB params.AB #define PB params.PB #define sat params.sat @@ -86,7 +80,7 @@ layout(set = 0, binding = 8) uniform sampler2D OriginalHistory6; vec3 afterglow(float number) { - return vec3(AR, AG, AB)*exp2(-vec3(PR, PG, PB)*vec3(number*number)); + return vec3(AS)*exp2(-vec3(PR, PG, PB)*vec3(number*number)); } void main() @@ -107,5 +101,5 @@ void main() float w = 1.0; if ((color.r + color.g + color.b) > 7.0/255.0) w = 0.0; - FragColor = vec4(color + SW*w*glow,1.0); + FragColor = vec4(color + w*glow,1.0); } \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/afterglow0.slang b/crt/shaders/guest/crt-gdv-new/afterglow0.slang new file mode 100644 index 0000000..f5c3501 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/afterglow0.slang @@ -0,0 +1,76 @@ +#version 450 + +/* + Phosphor Afterglow Shader pass 0 + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +layout(push_constant) uniform Push +{ + float PR, PG, PB; +} params; + +#pragma parameter bogus_afterglow "[ AFTERGLOW SETTINGS ]:" 0.0 0.0 1.0 1.0 +#pragma parameter PR " Persistence Red" 0.14 0.0 0.25 0.01 +#pragma parameter PG " Persistence Green" 0.14 0.0 0.25 0.01 +#pragma parameter PB " Persistence Blue" 0.14 0.0 0.25 0.01 + +#define PR params.PR +#define PG params.PG +#define PB params.PB + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +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 OriginalHistory0; +layout(set = 0, binding = 3) uniform sampler2D AfterglowPassFeedback; + +#define TEX0 vTexCoord + + +void main() +{ + vec3 color = COMPAT_TEXTURE(OriginalHistory0, TEX0.xy).rgb; + vec3 accumulate = COMPAT_TEXTURE(AfterglowPassFeedback, TEX0.xy).rgb; + + float w = 1.0; + if ((color.r + color.g + color.b < 3.0/255.0)) { w = 0.0; } + + vec3 result = mix( max(mix(color, accumulate, 0.74 + vec3(PR, PG, PB))- 2.0/255.0, 0.0)*(255.0/255.0), color, w); + + FragColor = vec4(result, w); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang b/crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang new file mode 100644 index 0000000..2060f98 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang @@ -0,0 +1,88 @@ +#version 450 + +/* + Average Luminance Shader + + Copyright (C) 2018-2020 guest(r) - guest.r@gmail.com + + 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. + + Thanks to HunterK for the mipmap hint. :D +*/ + +layout(push_constant) uniform Push +{ + uint FrameCount; + vec4 SourceSize; + float lsmooth; +} params; + +#pragma parameter lsmooth "Raster Bloom Effect Smoothing" 0.90 0.50 0.99 0.01 + +#define lsmooth params.lsmooth + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define SourceSize params.SourceSize +#define InputSize SourceSize +#define TEX0 vTexCoord + +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 * 1.0001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D OriginalHistory0; +layout(set = 0, binding = 3) uniform sampler2D AvgLumPassFeedback; + +#define PassPrev2Texture WhitePointPass + +void main() +{ + if (vTexCoord.x > 0.2 || vTexCoord.y > 0.2) discard; + + float m = max(log2(SourceSize.x), log2(SourceSize.y)); + m = max(m - 1.0, 1.0); + + float ltotal = 0.0; + + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.25, 0.25), m).rgb)); + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.25, 0.75), m).rgb)); + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.75, 0.25), m).rgb)); + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.75, 0.75), m).rgb)); + + ltotal*=0.25; + + ltotal = pow(0.577350269 * ltotal, 0.6); + + float lhistory = texture(AvgLumPassFeedback, vec2(0.1,0.1)).a; + + ltotal = mix(ltotal, lhistory, lsmooth); + + FragColor = vec4(ltotal); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/avg-lum.slang b/crt/shaders/guest/crt-gdv-new/avg-lum.slang new file mode 100644 index 0000000..d2b5538 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/avg-lum.slang @@ -0,0 +1,112 @@ +#version 450 + +/* + Average Luminance Shader, Smart Edge Interpolation Coefficients Calculation + + Copyright (C) 2018-2020 guest(r) - guest.r@gmail.com + + 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. + + Thanks to HunterK for the mipmap hint. :D +*/ + +layout(push_constant) uniform Push +{ + uint FrameCount; + vec4 SourceSize; + float lsmooth; + float sth; +} params; + +#pragma parameter lsmooth "Raster Bloom Effect Smoothing" 0.75 0.50 0.99 0.01 + +#define lsmooth params.lsmooth + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define SourceSize params.SourceSize +#define InputSize SourceSize +#define TEX0 vTexCoord + +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 * 1.0001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D OriginalHistory0; +layout(set = 0, binding = 3) uniform sampler2D AvgLumPassFeedback; + + +// Reference: http://www.compuphase.com/cmetric.htm +// Reference: ScaleFX, author Sp00kyFox + +float dist(vec3 A, vec3 B) +{ + float r = 0.5 * (A.r + B.r); + vec3 d = A - B; + vec3 c = vec3(2. + r, 4., 3. - r); + + return sqrt(dot(c*d, d)) / 3.; +} + +void main() +{ + float m = max(log2(SourceSize.x), log2(SourceSize.y)); + m = max(m - 1.0, 1.0); + + vec2 dx = vec2(1.0/SourceSize.x, 0.0); + vec2 dy = vec2(0.0, 1.0/SourceSize.y); + vec2 y2 = 2.0*dy; + vec2 x2 = 2.0*dx; + + float ltotal = 0.0; + + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.25, 0.25), m).rgb)); + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.25, 0.75), m).rgb)); + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.75, 0.25), m).rgb)); + ltotal+= max(0.0, length(textureLod(OriginalHistory0, vec2(0.75, 0.75), m).rgb)); + + ltotal*=0.25; + + ltotal = pow(0.577350269 * ltotal, 0.6); + + float lhistory = texture(AvgLumPassFeedback, vec2(0.1,0.1)).a; + + ltotal = mix(ltotal, lhistory, lsmooth); + + vec3 l1 = COMPAT_TEXTURE(OriginalHistory0, TEX0.xy ).rgb; + vec3 r1 = COMPAT_TEXTURE(OriginalHistory0, TEX0.xy +dx ).rgb; + vec3 l2 = COMPAT_TEXTURE(OriginalHistory0, TEX0.xy -dx ).rgb; + vec3 r2 = COMPAT_TEXTURE(OriginalHistory0, TEX0.xy +x2 ).rgb; + + float c1 = dist(l2,l1); + float c2 = dist(l1,r1); + float c3 = dist(r2,r1); + + FragColor = vec4(c1,c2,c3,ltotal); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang b/crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang new file mode 100644 index 0000000..e447629 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang @@ -0,0 +1,99 @@ +#version 450 + +/* + Gaussian blur - horizontal pass, dynamic range, resizable + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma format R16G16B16A16_SFLOAT + +layout(push_constant) uniform Push +{ + vec4 LinearizePassSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float SIZEHB; + float SIGMA_HB; +} params; + +#pragma parameter SIZEHB " H. Bloom/Halation Radius" 3.0 1.0 30.0 1.0 +#define SIZEHB params.SIZEHB + +#pragma parameter SIGMA_HB " Horizontal Bloom/Halation Sigma" 0.70 0.5 15.0 0.10 +#define SIGMA_HB params.SIGMA_HB + +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 LinearizePass; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +float invsqrsigma = 1.0/(2.0*SIGMA_HB*SIGMA_HB); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +void main() +{ + vec4 SourceSize1 = params.OriginalSize; + float f = fract(SourceSize1.x * vTexCoord.x); + f = 0.5 - f; + vec2 tex = floor(SourceSize1.xy * vTexCoord)*SourceSize1.zw + 0.5*SourceSize1.zw; + vec3 color = vec3(0.0); + vec2 dx = vec2(SourceSize1.z, 0.0); + + float w; + float wsum = 0.0; + vec3 pixel; + float n = -SIZEHB; + + do + { + pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx).rgb; + w = gaussian(n+f); + color = color + w * pixel; + wsum = wsum + w; + n = n + 1.0; + + } while (n <= SIZEHB); + + color = color / wsum; + + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/bloom_vertical.slang b/crt/shaders/guest/crt-gdv-new/bloom_vertical.slang new file mode 100644 index 0000000..f18b7eb --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/bloom_vertical.slang @@ -0,0 +1,102 @@ +#version 450 + +/* + Gaussian blur - vertical pass, dynamic range, resizable + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma format R16G16B16A16_SFLOAT + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float SIZEVB; + float SIGMA_VB; +} params; + + +#pragma parameter SIZEVB " V. Bloom/Halation Radius" 3.0 1.0 30.0 1.0 +#define SIZEVB params.SIZEVB + +#pragma parameter SIGMA_VB " Vertical Bloom/Halation Sigma" 0.70 0.5 15.0 0.10 +#define SIGMA_VB params.SIGMA_VB + +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; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +float invsqrsigma = 1.0/(2.0*SIGMA_VB*SIGMA_VB); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +void main() +{ + vec4 SourceSize1 = params.SourceSize; + SourceSize1.yw = params.OriginalSize.yw; + + float f = fract(SourceSize1.y * vTexCoord.y); + f = 0.5 - f; + vec2 tex = floor(SourceSize1.xy * vTexCoord)*SourceSize1.zw + 0.5*SourceSize1.zw; + vec3 color = vec3(0.0); + vec2 dy = vec2(0.0, SourceSize1.w); + + float w; + float wsum = 0.0; + vec3 pixel; + float n = -SIZEVB; + + do + { + pixel = COMPAT_TEXTURE(Source, tex + n*dy).rgb; + w = gaussian(n+f); + color = color + w * pixel; + wsum = wsum + w; + n = n + 1.0; + + } while (n <= SIZEVB); + + color = color / wsum; + + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang new file mode 100644 index 0000000..b56b5a7 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang @@ -0,0 +1,729 @@ +#version 450 + +/* + CRT - Guest - Dr. Venom + + Copyright (C) 2018-2020 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + + 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. + +*/ + +layout(push_constant) uniform Push +{ + float TATE, IOS, OS, BLOOM, brightboost, brightboost1, gsl, scanline1, scanline2, beam_min, beam_max, beam_size, + h_sharp, s_sharp, csize, bsize, warpX, warpY, glow, shadowMask, masksize, vertmask, + slotmask, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, spike, intres, inters; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float bloom; + float mclip; + float scans; + float scansub; + float slotms; + float gamma_c; + float mask_gamma; + float gamma_out; +} global; + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 0.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 0.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter gamma_c " Gamma correct" 1.0 0.50 2.0 0.02 +#define gamma_c global.gamma_c // adjust brightness + +#pragma parameter brightboost " Bright Boost Dark Pixels" 1.40 0.50 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost bright Pixels" 1.10 0.50 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#pragma parameter bogus_scanline "[ SCANLINE OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter gsl " Scanline Type" 0.0 0.0 2.0 1.0 +#define gsl params.gsl // Alternate scanlines + +#pragma parameter scanline1 " Scanline beam shape low" 6.0 0.0 20.0 0.5 +#define scanline1 params.scanline1 // scanline param, vertical sharpness + +#pragma parameter scanline2 " Scanline beam shape high" 8.0 3.0 40.0 1.0 +#define scanline2 params.scanline2 // scanline param, vertical sharpness + +#pragma parameter beam_min " Scanline shape dark pixels" 1.30 0.5 3.5 0.05 +#define beam_min params.beam_min // dark area beam min - narrow + +#pragma parameter beam_max " Scanline shape bright pixels" 1.00 0.4 2.5 0.05 +#define beam_max params.beam_max // bright area beam max - wide + +#pragma parameter beam_size " Increased bright scanline beam" 0.60 0.0 1.0 0.05 +#define beam_size params.beam_size // increased max. beam size + +#pragma parameter vertmask " Scanline Color Deconvergence" 0.0 -1.0 1.0 0.1 +#define vertmask params.vertmask // Scanline deconvergence colors + +#pragma parameter scans " Scanline Saturation" 0.60 0.0 1.0 0.05 +#define scans global.scans // scanline saturation + + +// Scanline darken 'edges' effect - need to uncomment it. + +// #pragma parameter scansub " Scanline darken 'edges'" 0.0 0.0 0.30 0.005 +// #define scansub global.scansub // scanline substraction + +#pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 +#define spike params.spike + +#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter h_sharp " Horizontal sharpness" 5.20 0.20 15.0 0.20 +#define h_sharp params.h_sharp // pixel sharpness + +#pragma parameter s_sharp " Substractive sharpness (1.0 recommended)" 0.50 0.0 1.5 0.10 +#define s_sharp params.s_sharp // substractive sharpness + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter intres " Internal Resolution: 1.0:240p, 1.5...y-dowsample" 0.0 0.0 4.0 0.5 // Joint parameter with linearize_ntsc pass, values must match +#define intres params.intres // interlace resolution + +#pragma parameter TATE " TATE Mode" 0.0 0.0 1.0 1.0 +#define TATE params.TATE // Screen orientation + +#pragma parameter IOS " Integer Scaling: Odd:Y, Even:'X'+Y" 0.0 0.0 4.0 1.0 +#define IOS params.IOS // Smart Integer Scaling + +#pragma parameter OS " R. Bloom Overscan Mode" 1.0 0.0 2.0 1.0 +#define OS params.OS // Do overscan + +#pragma parameter BLOOM " Raster bloom %" 0.0 0.0 20.0 1.0 +#define BLOOM params.BLOOM // Bloom overscan percentage + +#pragma parameter csize " Corner size" 0.0 0.0 0.07 0.01 +#define csize params.csize // corner size + +#pragma parameter bsize " Border smoothness" 600.0 100.0 600.0 25.0 +#define bsize params.bsize // border smoothness + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.125 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.125 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter bogus_masks "[ CRT MASK OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter shadowMask " CRT Mask: 0:CGWG, 1-4:Lottes, 5-7:'Trinitron'" 0.0 -1.0 8.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-8)" 0.3 -0.5 1.0 0.05 +#define maskstr params.maskstr // maskstr Mask Strength + +#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 2.0 1.0 +#define masksize params.masksize // Mask Size + +#pragma parameter maskDark " Lottes maskDark" 0.5 0.0 2.0 0.05 +#define maskDark params.maskDark // Dark "Phosphor" + +#pragma parameter maskLight " Lottes maskLight" 1.5 0.0 2.0 0.05 +#define maskLight params.maskLight // Light "Phosphor" + +#pragma parameter mcut " Mask 5-7 Low Strength" 1.15 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-7 cutoff + +#pragma parameter mask_gamma " Mask gamma" 2.40 1.0 5.0 0.05 +#define mask_gamma global.mask_gamma // Mask application gamma + +#pragma parameter slotmask " Slot Mask Strength" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask // Slot Mask ON/OFF + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 6.0 0.5 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1" 1.0 1.0 2.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 2.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.5 0.0 1.0 0.05 +#define mclip global.mclip // Slot Mask Size + +#pragma parameter gamma_out "Gamma out" 2.4 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + +#pragma parameter inters " Interlacing Effect Smoothness" 0.0 0.0 0.5 0.05 // Joint parameter with linearize-ntsc pass, values must match +#define inters params.inters // interlacing effect smoothing + + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define TEX0 vTexCoord + +#define OutputSize global.OutputSize +#define gl_FragCoord (vTexCoord * OutputSize.xy) + +#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 * 1.00001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D LinearizePass; +layout(set = 0, binding = 3) uniform sampler2D AvgLumPass; +layout(set = 0, binding = 4) uniform sampler2D GlowPass; + + +#define eps 1e-10 + +float st(float x) +{ + return exp2(-10.0*x*x); +} + +float sw0(float x, float color, float scanline) +{ + float tmp = mix(beam_min, beam_max, color); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +float sw1(float x, float color, float scanline) +{ + x = mix (x, beam_min*x, max(x-0.4*color,0.0)); + float tmp = mix(1.2*beam_min, beam_max, color); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +float sw2(float x, float color, float scanline) +{ + float tmp = mix(2.5*beam_min, beam_max, color); + tmp = mix(beam_max, tmp, pow(x, color+0.3)); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + pos = floor(pos/masksize); + vec3 mask = vec3(maskDark, maskDark, maskDark); + vec3 one = vec3(1.0); + float dark_compensate = mix(max( clamp( mix (mcut, maskstr, mx),0.0, 1.0) - 0.3, 0.0) + 1.0, 1.0, mx); + float mc = 1.0 - max(maskstr, 0.0); + + // No mask + if (shadowMask == -1.0) + { + mask = vec3(1.0); + } + + // Phosphor. + else if (shadowMask == 0.0) + { + pos.x = fract(pos.x*0.5); + if (pos.x < 0.5) { mask.r = 1.0; mask.g = mc; mask.b = 1.0; } + else { mask.r = mc; mask.g = 1.0; mask.b = mc; } + } + + // Very compressed TV style shadow mask. + else if (shadowMask == 1.0) + { + float line = maskLight; + float odd = 0.0; + + if (fract(pos.x/6.0) < 0.5) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.5) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + + mask*=line; + } + + // Aperture-grille. + else if (shadowMask == 2.0) + { + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // Stretched VGA style shadow mask (same as prior shaders). + else if (shadowMask == 3.0) + { + pos.x += pos.y*3.0; + pos.x = fract(pos.x/6.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // VGA style shadow mask. + else if (shadowMask == 4.0) + { + pos.xy = floor(pos.xy*vec2(1.0, 0.5)); + pos.x += pos.y*3.0; + pos.x = fract(pos.x/6.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // Trinitron mask 5 + else if (shadowMask == 5.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.5) + { mask.r = 1.0; + mask.b = 1.0; + } + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Trinitron mask 6 + else if (shadowMask == 6.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.333) mask.r = 1.0; + else if (pos.x < 0.666) mask.g = 1.0; + else mask.b = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 7 + else if (shadowMask == 7.0) + { + float maskTmp = clamp(mix( mix(1.0, 0.0, mcut), mix(1.0, 0.0, maskstr), mx), 0.0, 1.0) * dark_compensate; + mask = vec3(maskTmp); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.5) mask = vec3(1.0); + } + + // 4k mask + else + { + mask = vec3(mc); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask.r = 1.0; + else if (pos.x < 0.4) mask.rg = 1.0.xx; + else if (pos.x < 0.7) mask.gb = 1.0.xx; + else mask.b = 1.0; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if (slotmask == 0.0) return 1.0; + + pos = floor(pos/slotms); + float mlen = slotwidth*2.0; + float px = fract(pos.x/mlen); + float py = floor(fract(pos.y/(2.0*double_slot))*2.0*double_slot); + float slot_dark = 1.0 - slotmask*(1.0 - 0.125*m); + float slot = 1.0; + if (py == 0.0 && px < 0.5) slot = slot_dark; else + if (py == double_slot && px >= 0.5) slot = slot_dark; + + return slot; +} + +// Distortion of scanlines, and end of screen alpha (PD Lottes Curvature) +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY); + return pos*0.5 + 0.5; +} + +vec2 Overscan(vec2 pos, float dx, float dy){ + pos=pos*2.0-1.0; + pos*=vec2(dx,dy); + return pos*0.5+0.5; +} + + +// Borrowed from maskstr's crt-geom, under GPL + +float corner(vec2 coord) +{ + coord = (coord - vec2(0.5)) * 1.0 + vec2(0.5); + coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); + vec2 cdist = vec2(max(csize, max((1.0-smoothstep(100.0,600.0,bsize))*0.01,0.002))); + coord = (cdist - min(coord,cdist)); + float dist = sqrt(dot(coord,coord)); + return clamp((cdist.x-dist)*bsize,0.0, 1.0); +} + +vec3 declip(vec3 c, float b) +{ + float m = max(max(c.r,c.g),c.b); + if (m > b) c = c*b/m; + return c; +} + +vec3 gc(vec3 c) +{ + float mc = max(max(c.r,c.g),c.b); + float mg = pow(mc, 1.0/gamma_c); + return c * mg/(mc + eps); +} + +void main() +{ + vec4 SourceSize = global.OriginalSize; + + float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.1,0.1)).a; + float gamma_in = 1.0/COMPAT_TEXTURE(LinearizePass, vec2(0.25,0.25)).a; + float intera = COMPAT_TEXTURE(LinearizePass, vec2(0.75,0.25)).a; + bool interb = (intera < 0.75); + + bool notate = (TATE < 0.5); + + float SourceY = mix(SourceSize.y, SourceSize.x, TATE); + float sy = 1.0; + if (intres == 1.0) sy = SourceY/240.0; else + if (intres > 1.25) sy = intres; + if (notate) SourceSize.yw*=vec2(1.0/sy, sy); else SourceSize.xz*=vec2(1.0/sy, sy); + SourceY = SourceY/sy; + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + if (IOS > 0.0){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = mix(diff.y, diff.x, TATE); + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = mix(vec2(TEX0.x, texcoord.y), vec2(texcoord.x, TEX0.y), TATE); + } + + float factor = 1.00 + (1.0-0.5*OS)*BLOOM/100.0 - lum*BLOOM/100.0; + texcoord = Overscan(texcoord, factor, factor); + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + + vec2 coffset = vec2(0.5, 0.5); + + vec2 ps = SourceSize.zw; + vec2 OGL2Pos = pos * SourceSize.xy - coffset; + vec2 fp = fract(OGL2Pos); + + vec2 dx = vec2(ps.x,0.0); + vec2 dy = vec2(0.0, ps.y); + + // Reading the texels + vec2 x2 = 2.0*dx; + vec2 y2 = 2.0*dy; + + vec2 offx = dx; + vec2 off2 = x2; + vec2 offy = dy; + float fpx = fp.x; + if(!notate) + { + offx = dy; + off2 = y2; + offy = dx; + fpx = fp.y; + } + float f = (notate) ? fp.y : fp.x; + + vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps; + + if (interb) pC4.y = pos.y - inters * SourceSize.w; + + float h_sharp1 = pow(h_sharp, 1.4); + + float zero = exp2(-h_sharp1); + + float sharp1 = s_sharp * zero; + + float wl5 = 4.0 + fpx; wl5*=0.5; + float wl4 = 3.0 + fpx; wl4*=0.5; + float wl3 = 2.0 + fpx; wl3*=0.5; + float wl2 = 1.0 + fpx; wl2*=0.5; + float wl1 = fpx; wl1*=0.5; + float wr1 = 1.0 - fpx; wr1*=0.5; + float wr2 = 2.0 - fpx; wr2*=0.5; + float wr3 = 3.0 - fpx; wr3*=0.5; + float wr4 = 4.0 - fpx; wr4*=0.5; + float wr5 = 5.0 - fpx; wr5*=0.5; + + wl5*=wl5; wl5 = exp2(-h_sharp1*wl5); + wl4*=wl4; wl4 = exp2(-h_sharp1*wl4); + wl3*=wl3; wl3 = exp2(-h_sharp1*wl3); + wl2*=wl2; wl2 = exp2(-h_sharp1*wl2); + wl1*=wl1; wl1 = exp2(-h_sharp1*wl1); + wr1*=wr1; wr1 = exp2(-h_sharp1*wr1); + wr2*=wr2; wr2 = exp2(-h_sharp1*wr2); + wr3*=wr3; wr3 = exp2(-h_sharp1*wr3); + wr4*=wr4; wr4 = exp2(-h_sharp1*wr4); + wr5*=wr5; wr5 = exp2(-h_sharp1*wr5); + + float fp1 = 1.-fpx; + + float twl5 = max(wl5 - sharp1, 0.0); + float twl4 = max(wl4 - sharp1, mix(0.0,mix(-0.03, 0.00, fpx),float(s_sharp > 0.05))); float swl4 = max(wl4 - sharp1, 0.0); + float twl3 = max(wl3 - sharp1, mix(0.0,mix(-0.10, -0.03, fpx),float(s_sharp > 0.05))); float swl3 = max(wl3 - sharp1, 0.0); + float twl2 = max(wl2 - sharp1, 0.0); + float twl1 = max(wl1 - sharp1, 0.0); + float twr1 = max(wr1 - sharp1, 0.0); + float twr2 = max(wr2 - sharp1, 0.0); + float twr3 = max(wr3 - sharp1, mix(0.0,mix(-0.10, -0.03, fp1),float(s_sharp > 0.05))); float swr3 = max(wr3 - sharp1, 0.0); + float twr4 = max(wr4 - sharp1, mix(0.0,mix(-0.03, 0.00, fp1),float(s_sharp > 0.05))); float swr4 = max(wr4 - sharp1, 0.0); + float twr5 = max(wr5 - sharp1, 0.0); + + float wtt = 1.0/(twl5+twl4+twl3+twl2+twl1+twr1+twr2+twr3+twr4+twr5); + float wt = 1.0/(swl3+twl2+twl1+twr1+twr2+swr3); + bool sharp = (s_sharp > 0.05); + + vec3 l5 = COMPAT_TEXTURE(LinearizePass, pC4 -2.0*off2).xyz; + vec3 l4 = COMPAT_TEXTURE(LinearizePass, pC4 -3.0*offx).xyz; + vec3 l3 = COMPAT_TEXTURE(LinearizePass, pC4 -off2).xyz; + vec3 l2 = COMPAT_TEXTURE(LinearizePass, pC4 -offx).xyz; + vec3 l1 = COMPAT_TEXTURE(LinearizePass, pC4 ).xyz; + vec3 r1 = COMPAT_TEXTURE(LinearizePass, pC4 +offx).xyz; + vec3 r2 = COMPAT_TEXTURE(LinearizePass, pC4 +off2).xyz; + vec3 r3 = COMPAT_TEXTURE(LinearizePass, pC4 +3.0*offx).xyz; + vec3 r4 = COMPAT_TEXTURE(LinearizePass, pC4 +4.0*offx).xyz; + vec3 r5 = COMPAT_TEXTURE(LinearizePass, pC4 +5.0*offx).xyz; + + vec3 sl3 = l3*l3*l3; sl3*=sl3; + vec3 sl2 = l2*l2*l2; sl2*=sl2; + vec3 sl1 = l1*l1*l1; sl1*=sl1; + vec3 sr1 = r1*r1*r1; sr1*=sr1; + vec3 sr2 = r2*r2*r2; sr2*=sr2; + vec3 sr3 = r3*r3*r3; sr3*=sr3; + + vec3 color1 = (l5*twl5+l4*twl4+l3*twl3+l2*twl2+l1*twl1+r1*twr1+r2*twr2+r3*twr3+r4*twr4+r5*twr5)*wtt; + + vec3 colmin1 = min(min(l1,r1), min(l2,r2)); + vec3 colmax1 = max(max(l1,r1), max(l2,r2)); + vec3 colmin2 = min(min(l3,r3), min(l4,r4)); + vec3 colmax2 = max(max(l3,r3), max(l4,r4)); + vec3 colmin = min(colmin1, colmin2); + vec3 colmax = max(colmax1, colmax2); + + if (sharp) color1 = clamp(color1, colmin, colmax); + + vec3 gtmp = vec3(1.0/6.0); + vec3 scolor1 = color1; + + scolor1 = (sl3*swl3 + sl2*twl2 + sl1*twl1 + sr1*twr1 + sr2*twr2 + sr3*swr3)*wt; + scolor1 = pow(scolor1, gtmp); vec3 mcolor1 = scolor1; + scolor1 = min(mix(color1, scolor1, spike),1.0); + + vec3 color2, scolor2, mcolor2; + + if (interb) pC4.y = pos.y + inters * SourceSize.w; else + pC4+=offy; + + l5 = COMPAT_TEXTURE(LinearizePass, pC4 -2.0*off2).xyz; + l4 = COMPAT_TEXTURE(LinearizePass, pC4 -3.0*offx).xyz; + l3 = COMPAT_TEXTURE(LinearizePass, pC4 -off2).xyz; + l2 = COMPAT_TEXTURE(LinearizePass, pC4 -offx).xyz; + l1 = COMPAT_TEXTURE(LinearizePass, pC4 ).xyz; + r1 = COMPAT_TEXTURE(LinearizePass, pC4 +offx).xyz; + r2 = COMPAT_TEXTURE(LinearizePass, pC4 +off2).xyz; + r3 = COMPAT_TEXTURE(LinearizePass, pC4 +3.0*offx).xyz; + r4 = COMPAT_TEXTURE(LinearizePass, pC4 +4.0*offx).xyz; + r5 = COMPAT_TEXTURE(LinearizePass, pC4 +5.0*offx).xyz; + + sl3 = l3*l3*l3; sl3*=sl3; + sl2 = l2*l2*l2; sl2*=sl2; + sl1 = l1*l1*l1; sl1*=sl1; + sr1 = r1*r1*r1; sr1*=sr1; + sr2 = r2*r2*r2; sr2*=sr2; + sr3 = r3*r3*r3; sr3*=sr3; + + color2 = (l5*twl5+l4*twl4+l3*twl3+l2*twl2+l1*twl1+r1*twr1+r2*twr2+r3*twr3+r4*twr4+r5*twr5)*wtt; + + colmin1 = min(min(l1,r1), min(l2,r2)); + colmax1 = max(max(l1,r1), max(l2,r2)); + colmin2 = min(min(l3,r3), min(l3,r3)); + colmax2 = max(max(l4,r4), max(l4,r4)); + colmin = min(colmin1, colmin2); + colmax = max(colmax1, colmax2); + + if (sharp) color2 = clamp(color2, colmin, colmax); + + scolor2 = color2; + + scolor2 = (sl3*swl3 + sl2*twl2 + sl1*twl1 + sr1*twr1 + sr2*twr2 + sr3*swr3)*wt; + scolor2 = pow(scolor2, gtmp); mcolor2 = scolor2; + scolor2 = min(mix(color2, scolor2, spike),1.0); + + // calculating scanlines + + vec3 ctmp; vec3 mcolor; float w3; vec3 color; + vec3 one = vec3(1.0); + +if (!interb) +{ + float shape1 = mix(scanline1, scanline2, f); + float shape2 = mix(scanline1, scanline2, 1.0-f); + + float wt1 = st(f); + float wt2 = st(1.0-f); + + vec3 color00 = color1*wt1 + color2*wt2; + vec3 scolor0 = scolor1*wt1 + scolor2*wt2; + mcolor = (mcolor1*wt1 + mcolor2*wt2)/(wt1+wt2); + + ctmp = color00/(wt1+wt2); + vec3 sctmp = scolor0/(wt1+wt2); + + float wf1, wf2; + + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = max(max(cref1.r,cref1.g),cref1.b); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = max(max(cref2.r,cref2.g),cref2.b); + + float f1 = f; + float f2 = 1.0-f; + + if (gsl == 0.0) { wf1 = sw0(f1,creff1,shape1); wf2 = sw0(f2,creff2,shape2);} else + if (gsl == 1.0) { wf1 = sw1(f1,creff1,shape1); wf2 = sw1(f2,creff2,shape2);} else + if (gsl == 2.0) { wf1 = sw2(f1,creff1,shape1); wf2 = sw2(f2,creff2,shape2);} + + if ((wf1 + wf2) > 1.0) { float wtmp = 1.0/(wf1+wf2); wf1*=wtmp; wf2*=wtmp; } + + // Scanline darken 'edges' effect - need to uncomment it. + + // float ws1 = max(wf1 - scansub, 0.2*wf1*wf2); wf1 = ws1/(1.0 - wf1 + ws1); + // float ws2 = max(wf2 - scansub, 0.2*wf2*wf1); wf2 = ws2/(1.0 - wf2 + ws2); + + // Scanline saturation application + + vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); + + cref1 = color1 / (max(max(color1.r,color1.g),color1.b) + 0.00001); + cref2 = color2 / (max(max(color2.r,color2.g),color2.b) + 0.00001); + + w1 = mix(w1*mix(one, cref1*cref1*cref1, scans), w1, wf1); + w2 = mix(w2*mix(one, cref2*cref2*cref2, scans), w2, wf2); + + vec3 cd1 = one; vec3 cd2 = one; float vm = sqrt(abs(vertmask)); + + float v_high1 = 1.0 + 0.3*vm; + float v_high2 = 1.0 + 0.6*vm; + float v_low = 1.0 - vm; + + float ds1 = min(pow(2.0*f1 + 0.01, f2), 1.0); + float ds2 = min(pow(2.0*f2 + 0.01, f1), 1.0); + + if (vertmask < 0.0) + { + cd1 = mix(one, vec3(v_high2, v_low, v_low), ds1); + cd2 = mix(one, vec3(v_low, v_high1, v_high1), ds2); + } + else + { + cd1 = mix(one, vec3(v_high1, v_low, v_high1), ds1); + cd2 = mix(one, vec3(v_low, v_high2, v_low), ds2); + } + + color = gc(color1)*w1*cd1 + gc(color2)*w2*cd2; + color = min(color, 1.0); + w3 = wf1+wf2; +} + + if (interb) + { + color = gc(0.5*(color1+color2)); + mcolor = vec3(max(max(color.r,color.g), color.b)); + } + + float mx = max(max(mcolor.r,mcolor.g),mcolor.b); + mx = pow(mx, 1.40/gamma_in); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + float smask = (notate) ? SlotMask(gl_FragCoord.xy * 1.000001, mx) : SlotMask(gl_FragCoord.yx * 1.000001, mx); + + cmask*= (notate) ? Mask(gl_FragCoord.xy * 1.000001, mx) : Mask(gl_FragCoord.yx * 1.000001, mx); + + color = pow(color, vec3(mask_gamma/gamma_in)); + color = color*cmask; + color = min(color,1.0); + color = color*smask; + color = pow(color, vec3(gamma_in/mask_gamma)); + + cmask = min(cmask*smask, 1.0); + + if (interb) ctmp = color; + float colmx = pow( max( max(ctmp.r, ctmp.g), ctmp.b), 1.40/gamma_out); + float bb = mix(brightboost, brightboost1, colmx); + if (interb) bb = (abs(intera-0.5)<0.1) ? pow(0.80*bb, 0.65) : pow(bb, 0.70); + color*=bb; + + vec3 Glow = COMPAT_TEXTURE(GlowPass, pos ).rgb; + + vec3 Bloom = min(Glow*(orig1+color), max(0.5*(colmx + orig1 - color),0.0)); + color = color + bloom*Bloom; + + color = min(color, mix(one, cmask, mclip)); + if (!interb) color = declip(color, pow(w3,0.6)); + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + color = color + 0.5*glow*Glow; + + float gmo = 1.0/gamma_out; + if (interb) gmo = gmo / 0.70; + + color = pow(color, vec3(gmo)); + + FragColor = vec4(color*corner(pos0), 1.0); +} diff --git a/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang new file mode 100644 index 0000000..64f9b69 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang @@ -0,0 +1,729 @@ +#version 450 + +/* + CRT - Guest - Dr. Venom + + Copyright (C) 2018-2020 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + + 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. + +*/ + +layout(push_constant) uniform Push +{ + float TATE, IOS, OS, BLOOM, brightboost, brightboost1, gsl, scanline1, scanline2, beam_min, beam_max, beam_size, + h_sharp, s_sharp, csize, bsize, warpX, warpY, glow, shadowMask, masksize, vertmask, + slotmask, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, spike, intres, inters; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float bloom; + float mclip; + float scans; + float scansub; + float slotms; + float gamma_c; + float mask_gamma; + float gamma_out; +} global; + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 0.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 0.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter gamma_c " Gamma correct" 1.0 0.50 2.0 0.02 +#define gamma_c global.gamma_c // adjust brightness + +#pragma parameter brightboost " Bright Boost Dark Pixels" 1.40 0.50 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost bright Pixels" 1.10 0.50 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#pragma parameter bogus_scanline "[ SCANLINE OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter gsl " Scanline Type" 0.0 0.0 2.0 1.0 +#define gsl params.gsl // Alternate scanlines + +#pragma parameter scanline1 " Scanline beam shape low" 6.0 0.0 20.0 0.5 +#define scanline1 params.scanline1 // scanline param, vertical sharpness + +#pragma parameter scanline2 " Scanline beam shape high" 8.0 3.0 40.0 1.0 +#define scanline2 params.scanline2 // scanline param, vertical sharpness + +#pragma parameter beam_min " Scanline shape dark pixels" 1.30 0.5 3.5 0.05 +#define beam_min params.beam_min // dark area beam min - narrow + +#pragma parameter beam_max " Scanline shape bright pixels" 1.00 0.4 2.5 0.05 +#define beam_max params.beam_max // bright area beam max - wide + +#pragma parameter beam_size " Increased bright scanline beam" 0.60 0.0 1.0 0.05 +#define beam_size params.beam_size // increased max. beam size + +#pragma parameter vertmask " Scanline Color Deconvergence" 0.0 -1.0 1.0 0.1 +#define vertmask params.vertmask // Scanline deconvergence colors + +#pragma parameter scans " Scanline Saturation" 0.60 0.0 1.0 0.05 +#define scans global.scans // scanline saturation + + +// Scanline darken 'edges' effect - need to uncomment it. + +// #pragma parameter scansub " Scanline darken 'edges'" 0.0 0.0 0.30 0.005 +// #define scansub global.scansub // scanline substraction + +#pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 +#define spike params.spike + +#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter h_sharp " Horizontal sharpness" 5.20 0.20 15.0 0.20 +#define h_sharp params.h_sharp // pixel sharpness + +#pragma parameter s_sharp " Substractive sharpness (1.0 recommended)" 0.50 0.0 1.5 0.10 +#define s_sharp params.s_sharp // substractive sharpness + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter intres " Internal Resolution: 1.0:240p, 1.5...y-dowsample" 0.0 0.0 4.0 0.5 // Joint parameter with linearize_ntsc pass, values must match +#define intres params.intres // interlace resolution + +#pragma parameter TATE " TATE Mode" 0.0 0.0 1.0 1.0 +#define TATE params.TATE // Screen orientation + +#pragma parameter IOS " Integer Scaling: Odd:Y, Even:'X'+Y" 0.0 0.0 4.0 1.0 +#define IOS params.IOS // Smart Integer Scaling + +#pragma parameter OS " R. Bloom Overscan Mode" 1.0 0.0 2.0 1.0 +#define OS params.OS // Do overscan + +#pragma parameter BLOOM " Raster bloom %" 0.0 0.0 20.0 1.0 +#define BLOOM params.BLOOM // Bloom overscan percentage + +#pragma parameter csize " Corner size" 0.0 0.0 0.07 0.01 +#define csize params.csize // corner size + +#pragma parameter bsize " Border smoothness" 600.0 100.0 600.0 25.0 +#define bsize params.bsize // border smoothness + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.125 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.125 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter bogus_masks "[ CRT MASK OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter shadowMask " CRT Mask: 0:CGWG, 1-4:Lottes, 5-7:'Trinitron'" 0.0 -1.0 8.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-8)" 0.3 -0.5 1.0 0.05 +#define maskstr params.maskstr // maskstr Mask Strength + +#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 2.0 1.0 +#define masksize params.masksize // Mask Size + +#pragma parameter maskDark " Lottes maskDark" 0.5 0.0 2.0 0.05 +#define maskDark params.maskDark // Dark "Phosphor" + +#pragma parameter maskLight " Lottes maskLight" 1.5 0.0 2.0 0.05 +#define maskLight params.maskLight // Light "Phosphor" + +#pragma parameter mcut " Mask 5-7 Low Strength" 1.15 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-7 cutoff + +#pragma parameter mask_gamma " Mask gamma" 2.40 1.0 5.0 0.05 +#define mask_gamma global.mask_gamma // Mask application gamma + +#pragma parameter slotmask " Slot Mask Strength" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask // Slot Mask ON/OFF + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 6.0 0.5 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1" 1.0 1.0 2.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 2.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.5 0.0 1.0 0.05 +#define mclip global.mclip // Slot Mask Size + +#pragma parameter gamma_out "Gamma out" 2.4 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + +#pragma parameter inters " Interlacing Effect Smoothness" 0.0 0.0 0.5 0.05 // Joint parameter with linearize-ntsc pass, values must match +#define inters params.inters // interlacing effect smoothing + + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define TEX0 vTexCoord + +#define OutputSize global.OutputSize +#define gl_FragCoord (vTexCoord * OutputSize.xy) + +#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 * 1.00001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D LinearizePass; +layout(set = 0, binding = 3) uniform sampler2D AvgLumPass; +layout(set = 0, binding = 4) uniform sampler2D GlowPass; + + +#define eps 1e-10 + +float st(float x) +{ + return exp2(-10.0*x*x); +} + +float sw0(float x, float color, float scanline) +{ + float tmp = mix(beam_min, beam_max, color); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +float sw1(float x, float color, float scanline) +{ + x = mix (x, beam_min*x, max(x-0.4*color,0.0)); + float tmp = mix(1.2*beam_min, beam_max, color); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +float sw2(float x, float color, float scanline) +{ + float tmp = mix(2.5*beam_min, beam_max, color); + tmp = mix(beam_max, tmp, pow(x, color+0.3)); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + pos = floor(pos/masksize); + vec3 mask = vec3(maskDark, maskDark, maskDark); + vec3 one = vec3(1.0); + float dark_compensate = mix(max( clamp( mix (mcut, maskstr, mx),0.0, 1.0) - 0.3, 0.0) + 1.0, 1.0, mx); + float mc = 1.0 - max(maskstr, 0.0); + + // No mask + if (shadowMask == -1.0) + { + mask = vec3(1.0); + } + + // Phosphor. + else if (shadowMask == 0.0) + { + pos.x = fract(pos.x*0.5); + if (pos.x < 0.5) { mask.r = 1.0; mask.g = mc; mask.b = 1.0; } + else { mask.r = mc; mask.g = 1.0; mask.b = mc; } + } + + // Very compressed TV style shadow mask. + else if (shadowMask == 1.0) + { + float line = maskLight; + float odd = 0.0; + + if (fract(pos.x/6.0) < 0.5) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.5) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + + mask*=line; + } + + // Aperture-grille. + else if (shadowMask == 2.0) + { + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // Stretched VGA style shadow mask (same as prior shaders). + else if (shadowMask == 3.0) + { + pos.x += pos.y*3.0; + pos.x = fract(pos.x/6.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // VGA style shadow mask. + else if (shadowMask == 4.0) + { + pos.xy = floor(pos.xy*vec2(1.0, 0.5)); + pos.x += pos.y*3.0; + pos.x = fract(pos.x/6.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // Trinitron mask 5 + else if (shadowMask == 5.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.5) + { mask.r = 1.0; + mask.b = 1.0; + } + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Trinitron mask 6 + else if (shadowMask == 6.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.333) mask.r = 1.0; + else if (pos.x < 0.666) mask.g = 1.0; + else mask.b = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 7 + else if (shadowMask == 7.0) + { + float maskTmp = clamp(mix( mix(1.0, 0.0, mcut), mix(1.0, 0.0, maskstr), mx), 0.0, 1.0) * dark_compensate; + mask = vec3(maskTmp); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.5) mask = vec3(1.0); + } + + // 4k mask + else + { + mask = vec3(mc); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask.r = 1.0; + else if (pos.x < 0.4) mask.rg = 1.0.xx; + else if (pos.x < 0.7) mask.gb = 1.0.xx; + else mask.b = 1.0; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if (slotmask == 0.0) return 1.0; + + pos = floor(pos/slotms); + float mlen = slotwidth*2.0; + float px = fract(pos.x/mlen); + float py = floor(fract(pos.y/(2.0*double_slot))*2.0*double_slot); + float slot_dark = 1.0 - slotmask*(1.0 - 0.125*m); + float slot = 1.0; + if (py == 0.0 && px < 0.5) slot = slot_dark; else + if (py == double_slot && px >= 0.5) slot = slot_dark; + + return slot; +} + +// Distortion of scanlines, and end of screen alpha (PD Lottes Curvature) +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY); + return pos*0.5 + 0.5; +} + +vec2 Overscan(vec2 pos, float dx, float dy){ + pos=pos*2.0-1.0; + pos*=vec2(dx,dy); + return pos*0.5+0.5; +} + + +// Borrowed from maskstr's crt-geom, under GPL + +float corner(vec2 coord) +{ + coord = (coord - vec2(0.5)) * 1.0 + vec2(0.5); + coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); + vec2 cdist = vec2(max(csize, max((1.0-smoothstep(100.0,600.0,bsize))*0.01,0.002))); + coord = (cdist - min(coord,cdist)); + float dist = sqrt(dot(coord,coord)); + return clamp((cdist.x-dist)*bsize,0.0, 1.0); +} + +vec3 declip(vec3 c, float b) +{ + float m = max(max(c.r,c.g),c.b); + if (m > b) c = c*b/m; + return c; +} + +vec3 gc(vec3 c) +{ + float mc = max(max(c.r,c.g),c.b); + float mg = pow(mc, 1.0/gamma_c); + return c * mg/(mc + eps); +} + +void main() +{ + vec4 SourceSize = global.OriginalSize * vec4(2.0, 1.0, 0.5, 1.0); + + float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.1,0.1)).a; + float gamma_in = 1.0/COMPAT_TEXTURE(LinearizePass, vec2(0.25,0.25)).a; + float intera = COMPAT_TEXTURE(LinearizePass, vec2(0.75,0.25)).a; + bool interb = (intera < 0.75); + + bool notate = (TATE < 0.5); + + float SourceY = mix(SourceSize.y, SourceSize.x, TATE); + float sy = 1.0; + if (intres == 1.0) sy = SourceY/240.0; else + if (intres > 1.25) sy = intres; + if (notate) SourceSize.yw*=vec2(1.0/sy, sy); else SourceSize.xz*=vec2(1.0/sy, sy); + SourceY = SourceY/sy; + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + if (IOS > 0.0){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = mix(diff.y, diff.x, TATE); + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = mix(vec2(TEX0.x, texcoord.y), vec2(texcoord.x, TEX0.y), TATE); + } + + float factor = 1.00 + (1.0-0.5*OS)*BLOOM/100.0 - lum*BLOOM/100.0; + texcoord = Overscan(texcoord, factor, factor); + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + + vec2 coffset = vec2(0.5, 0.5); + + vec2 ps = SourceSize.zw; + vec2 OGL2Pos = pos * SourceSize.xy - coffset; + vec2 fp = fract(OGL2Pos); + + vec2 dx = vec2(ps.x,0.0); + vec2 dy = vec2(0.0, ps.y); + + // Reading the texels + vec2 x2 = 2.0*dx; + vec2 y2 = 2.0*dy; + + vec2 offx = dx; + vec2 off2 = x2; + vec2 offy = dy; + float fpx = fp.x; + if(!notate) + { + offx = dy; + off2 = y2; + offy = dx; + fpx = fp.y; + } + float f = (notate) ? fp.y : fp.x; + + vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps; + + if (interb) pC4.y = pos.y - inters * SourceSize.w; + + float h_sharp1 = pow(h_sharp, 1.4); + + float zero = exp2(-h_sharp1); + + float sharp1 = s_sharp * zero; + + float wl5 = 4.0 + fpx; wl5*=0.5; + float wl4 = 3.0 + fpx; wl4*=0.5; + float wl3 = 2.0 + fpx; wl3*=0.5; + float wl2 = 1.0 + fpx; wl2*=0.5; + float wl1 = fpx; wl1*=0.5; + float wr1 = 1.0 - fpx; wr1*=0.5; + float wr2 = 2.0 - fpx; wr2*=0.5; + float wr3 = 3.0 - fpx; wr3*=0.5; + float wr4 = 4.0 - fpx; wr4*=0.5; + float wr5 = 5.0 - fpx; wr5*=0.5; + + wl5*=wl5; wl5 = exp2(-h_sharp1*wl5); + wl4*=wl4; wl4 = exp2(-h_sharp1*wl4); + wl3*=wl3; wl3 = exp2(-h_sharp1*wl3); + wl2*=wl2; wl2 = exp2(-h_sharp1*wl2); + wl1*=wl1; wl1 = exp2(-h_sharp1*wl1); + wr1*=wr1; wr1 = exp2(-h_sharp1*wr1); + wr2*=wr2; wr2 = exp2(-h_sharp1*wr2); + wr3*=wr3; wr3 = exp2(-h_sharp1*wr3); + wr4*=wr4; wr4 = exp2(-h_sharp1*wr4); + wr5*=wr5; wr5 = exp2(-h_sharp1*wr5); + + float fp1 = 1.-fpx; + + float twl5 = max(wl5 - sharp1, 0.0); + float twl4 = max(wl4 - sharp1, mix(0.0,mix(-0.03, 0.00, fpx),float(s_sharp > 0.05))); float swl4 = max(wl4 - sharp1, 0.0); + float twl3 = max(wl3 - sharp1, mix(0.0,mix(-0.10, -0.03, fpx),float(s_sharp > 0.05))); float swl3 = max(wl3 - sharp1, 0.0); + float twl2 = max(wl2 - sharp1, 0.0); + float twl1 = max(wl1 - sharp1, 0.0); + float twr1 = max(wr1 - sharp1, 0.0); + float twr2 = max(wr2 - sharp1, 0.0); + float twr3 = max(wr3 - sharp1, mix(0.0,mix(-0.10, -0.03, fp1),float(s_sharp > 0.05))); float swr3 = max(wr3 - sharp1, 0.0); + float twr4 = max(wr4 - sharp1, mix(0.0,mix(-0.03, 0.00, fp1),float(s_sharp > 0.05))); float swr4 = max(wr4 - sharp1, 0.0); + float twr5 = max(wr5 - sharp1, 0.0); + + float wtt = 1.0/(twl5+twl4+twl3+twl2+twl1+twr1+twr2+twr3+twr4+twr5); + float wt = 1.0/(swl3+twl2+twl1+twr1+twr2+swr3); + bool sharp = (s_sharp > 0.05); + + vec3 l5 = COMPAT_TEXTURE(LinearizePass, pC4 -2.0*off2).xyz; + vec3 l4 = COMPAT_TEXTURE(LinearizePass, pC4 -3.0*offx).xyz; + vec3 l3 = COMPAT_TEXTURE(LinearizePass, pC4 -off2).xyz; + vec3 l2 = COMPAT_TEXTURE(LinearizePass, pC4 -offx).xyz; + vec3 l1 = COMPAT_TEXTURE(LinearizePass, pC4 ).xyz; + vec3 r1 = COMPAT_TEXTURE(LinearizePass, pC4 +offx).xyz; + vec3 r2 = COMPAT_TEXTURE(LinearizePass, pC4 +off2).xyz; + vec3 r3 = COMPAT_TEXTURE(LinearizePass, pC4 +3.0*offx).xyz; + vec3 r4 = COMPAT_TEXTURE(LinearizePass, pC4 +4.0*offx).xyz; + vec3 r5 = COMPAT_TEXTURE(LinearizePass, pC4 +5.0*offx).xyz; + + vec3 sl3 = l3*l3*l3; sl3*=sl3; + vec3 sl2 = l2*l2*l2; sl2*=sl2; + vec3 sl1 = l1*l1*l1; sl1*=sl1; + vec3 sr1 = r1*r1*r1; sr1*=sr1; + vec3 sr2 = r2*r2*r2; sr2*=sr2; + vec3 sr3 = r3*r3*r3; sr3*=sr3; + + vec3 color1 = (l5*twl5+l4*twl4+l3*twl3+l2*twl2+l1*twl1+r1*twr1+r2*twr2+r3*twr3+r4*twr4+r5*twr5)*wtt; + + vec3 colmin1 = min(min(l1,r1), min(l2,r2)); + vec3 colmax1 = max(max(l1,r1), max(l2,r2)); + vec3 colmin2 = min(min(l3,r3), min(l4,r4)); + vec3 colmax2 = max(max(l3,r3), max(l4,r4)); + vec3 colmin = min(colmin1, colmin2); + vec3 colmax = max(colmax1, colmax2); + + if (sharp) color1 = clamp(color1, colmin, colmax); + + vec3 gtmp = vec3(1.0/6.0); + vec3 scolor1 = color1; + + scolor1 = (sl3*swl3 + sl2*twl2 + sl1*twl1 + sr1*twr1 + sr2*twr2 + sr3*swr3)*wt; + scolor1 = pow(scolor1, gtmp); vec3 mcolor1 = scolor1; + scolor1 = min(mix(color1, scolor1, spike),1.0); + + vec3 color2, scolor2, mcolor2; + + if (interb) pC4.y = pos.y + inters * SourceSize.w; else + pC4+=offy; + + l5 = COMPAT_TEXTURE(LinearizePass, pC4 -2.0*off2).xyz; + l4 = COMPAT_TEXTURE(LinearizePass, pC4 -3.0*offx).xyz; + l3 = COMPAT_TEXTURE(LinearizePass, pC4 -off2).xyz; + l2 = COMPAT_TEXTURE(LinearizePass, pC4 -offx).xyz; + l1 = COMPAT_TEXTURE(LinearizePass, pC4 ).xyz; + r1 = COMPAT_TEXTURE(LinearizePass, pC4 +offx).xyz; + r2 = COMPAT_TEXTURE(LinearizePass, pC4 +off2).xyz; + r3 = COMPAT_TEXTURE(LinearizePass, pC4 +3.0*offx).xyz; + r4 = COMPAT_TEXTURE(LinearizePass, pC4 +4.0*offx).xyz; + r5 = COMPAT_TEXTURE(LinearizePass, pC4 +5.0*offx).xyz; + + sl3 = l3*l3*l3; sl3*=sl3; + sl2 = l2*l2*l2; sl2*=sl2; + sl1 = l1*l1*l1; sl1*=sl1; + sr1 = r1*r1*r1; sr1*=sr1; + sr2 = r2*r2*r2; sr2*=sr2; + sr3 = r3*r3*r3; sr3*=sr3; + + color2 = (l5*twl5+l4*twl4+l3*twl3+l2*twl2+l1*twl1+r1*twr1+r2*twr2+r3*twr3+r4*twr4+r5*twr5)*wtt; + + colmin1 = min(min(l1,r1), min(l2,r2)); + colmax1 = max(max(l1,r1), max(l2,r2)); + colmin2 = min(min(l3,r3), min(l3,r3)); + colmax2 = max(max(l4,r4), max(l4,r4)); + colmin = min(colmin1, colmin2); + colmax = max(colmax1, colmax2); + + if (sharp) color2 = clamp(color2, colmin, colmax); + + scolor2 = color2; + + scolor2 = (sl3*swl3 + sl2*twl2 + sl1*twl1 + sr1*twr1 + sr2*twr2 + sr3*swr3)*wt; + scolor2 = pow(scolor2, gtmp); mcolor2 = scolor2; + scolor2 = min(mix(color2, scolor2, spike),1.0); + + // calculating scanlines + + vec3 ctmp; vec3 mcolor; float w3; vec3 color; + vec3 one = vec3(1.0); + +if (!interb) +{ + float shape1 = mix(scanline1, scanline2, f); + float shape2 = mix(scanline1, scanline2, 1.0-f); + + float wt1 = st(f); + float wt2 = st(1.0-f); + + vec3 color00 = color1*wt1 + color2*wt2; + vec3 scolor0 = scolor1*wt1 + scolor2*wt2; + mcolor = (mcolor1*wt1 + mcolor2*wt2)/(wt1+wt2); + + ctmp = color00/(wt1+wt2); + vec3 sctmp = scolor0/(wt1+wt2); + + float wf1, wf2; + + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = max(max(cref1.r,cref1.g),cref1.b); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = max(max(cref2.r,cref2.g),cref2.b); + + float f1 = f; + float f2 = 1.0-f; + + if (gsl == 0.0) { wf1 = sw0(f1,creff1,shape1); wf2 = sw0(f2,creff2,shape2);} else + if (gsl == 1.0) { wf1 = sw1(f1,creff1,shape1); wf2 = sw1(f2,creff2,shape2);} else + if (gsl == 2.0) { wf1 = sw2(f1,creff1,shape1); wf2 = sw2(f2,creff2,shape2);} + + if ((wf1 + wf2) > 1.0) { float wtmp = 1.0/(wf1+wf2); wf1*=wtmp; wf2*=wtmp; } + + // Scanline darken 'edges' effect - need to uncomment it. + + // float ws1 = max(wf1 - scansub, 0.2*wf1*wf2); wf1 = ws1/(1.0 - wf1 + ws1); + // float ws2 = max(wf2 - scansub, 0.2*wf2*wf1); wf2 = ws2/(1.0 - wf2 + ws2); + + // Scanline saturation application + + vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); + + cref1 = color1 / (max(max(color1.r,color1.g),color1.b) + 0.00001); + cref2 = color2 / (max(max(color2.r,color2.g),color2.b) + 0.00001); + + w1 = mix(w1*mix(one, cref1*cref1*cref1, scans), w1, wf1); + w2 = mix(w2*mix(one, cref2*cref2*cref2, scans), w2, wf2); + + vec3 cd1 = one; vec3 cd2 = one; float vm = sqrt(abs(vertmask)); + + float v_high1 = 1.0 + 0.3*vm; + float v_high2 = 1.0 + 0.6*vm; + float v_low = 1.0 - vm; + + float ds1 = min(pow(2.0*f1 + 0.01, f2), 1.0); + float ds2 = min(pow(2.0*f2 + 0.01, f1), 1.0); + + if (vertmask < 0.0) + { + cd1 = mix(one, vec3(v_high2, v_low, v_low), ds1); + cd2 = mix(one, vec3(v_low, v_high1, v_high1), ds2); + } + else + { + cd1 = mix(one, vec3(v_high1, v_low, v_high1), ds1); + cd2 = mix(one, vec3(v_low, v_high2, v_low), ds2); + } + + color = gc(color1)*w1*cd1 + gc(color2)*w2*cd2; + color = min(color, 1.0); + w3 = wf1+wf2; +} + + if (interb) + { + color = gc(0.5*(color1+color2)); + mcolor = vec3(max(max(color.r,color.g), color.b)); + } + + float mx = max(max(mcolor.r,mcolor.g),mcolor.b); + mx = pow(mx, 1.40/gamma_in); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + float smask = (notate) ? SlotMask(gl_FragCoord.xy * 1.000001, mx) : SlotMask(gl_FragCoord.yx * 1.000001, mx); + + cmask*= (notate) ? Mask(gl_FragCoord.xy * 1.000001, mx) : Mask(gl_FragCoord.yx * 1.000001, mx); + + color = pow(color, vec3(mask_gamma/gamma_in)); + color = color*cmask; + color = min(color,1.0); + color = color*smask; + color = pow(color, vec3(gamma_in/mask_gamma)); + + cmask = min(cmask*smask, 1.0); + + if (interb) ctmp = color; + float colmx = pow( max( max(ctmp.r, ctmp.g), ctmp.b), 1.40/gamma_out); + float bb = mix(brightboost, brightboost1, colmx); + if (interb) bb = (abs(intera-0.5)<0.1) ? pow(0.80*bb, 0.65) : pow(bb, 0.70); + color*=bb; + + vec3 Glow = COMPAT_TEXTURE(GlowPass, pos ).rgb; + + vec3 Bloom = min(Glow*(orig1+color), max(0.5*(colmx + orig1 - color),0.0)); + color = color + bloom*Bloom; + + color = min(color, mix(one, cmask, mclip)); + if (!interb) color = declip(color, pow(w3,0.6)); + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + color = color + 0.5*glow*Glow; + + float gmo = 1.0/gamma_out; + if (interb) gmo = gmo / 0.70; + + color = pow(color, vec3(gmo)); + + FragColor = vec4(color*corner(pos0), 1.0); +} diff --git a/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang new file mode 100644 index 0000000..2294b14 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang @@ -0,0 +1,756 @@ +#version 450 + +/* + CRT - Guest - Dr. Venom + + Copyright (C) 2018-2020 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + + 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. + +*/ + +layout(push_constant) uniform Push +{ + float TATE, IOS, OS, BLOOM, brightboost, brightboost1, gsl, scanline1, scanline2, beam_min, beam_max, beam_size, + h_sharp, s_sharp, csize, bsize, warpX, warpY, glow, shadowMask, masksize, vertmask, + slotmask, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, spike, inters; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float bloom; + float halation; + float scans; + float scansub; + float slotms; + float mclip; + float gamma_c; + float mask_gamma; + float smart_ei; + float ei_limit; + float sth; + float gamma_out; +} global; + + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 0.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 0.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter halation " Halation Strength" 0.0 0.0 0.5 0.025 +#define halation global.halation // halation effect + +#pragma parameter gamma_c " Gamma correct" 1.0 0.50 2.0 0.02 +#define gamma_c global.gamma_c // adjust brightness + +#pragma parameter brightboost " Bright Boost Dark Pixels" 1.40 0.50 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost bright Pixels" 1.10 0.50 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#pragma parameter bogus_scanline "[ SCANLINE OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter gsl " Scanline Type" 0.0 0.0 2.0 1.0 +#define gsl params.gsl // Alternate scanlines + +#pragma parameter scanline1 " Scanline beam shape low" 6.0 0.0 20.0 0.5 +#define scanline1 params.scanline1 // scanline param, vertical sharpness + +#pragma parameter scanline2 " Scanline beam shape high" 8.0 3.0 40.0 1.0 +#define scanline2 params.scanline2 // scanline param, vertical sharpness + +#pragma parameter beam_min " Scanline shape dark pixels" 1.30 0.5 3.5 0.05 +#define beam_min params.beam_min // dark area beam min - narrow + +#pragma parameter beam_max " Scanline shape bright pixels" 1.00 0.4 2.5 0.05 +#define beam_max params.beam_max // bright area beam max - wide + +#pragma parameter beam_size " Increased bright scanline beam" 0.60 0.0 1.0 0.05 +#define beam_size params.beam_size // increased max. beam size + +#pragma parameter vertmask " Scanline Color Deconvergence" 0.0 -1.0 1.0 0.1 +#define vertmask params.vertmask // Scanline deconvergence colors + +#pragma parameter scans " Scanline Saturation" 0.60 0.0 1.0 0.05 +#define scans global.scans // scanline saturation + + +// Scanline darken 'edges' effect - need to uncomment it. + +// #pragma parameter scansub " Scanline darken 'edges'" 0.0 0.0 0.30 0.005 +// #define scansub global.scansub // scanline substraction + +#pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 +#define spike params.spike + +#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter h_sharp " Horizontal sharpness" 5.20 0.20 15.0 0.20 +#define h_sharp params.h_sharp // pixel sharpness + +#pragma parameter s_sharp " Substractive sharpness (1.0 recommended)" 0.50 0.0 1.5 0.10 +#define s_sharp params.s_sharp // substractive sharpness + +#pragma parameter smart_ei " Smart Edges Effect Strength" 0.0 0.0 20.0 0.25 +#define smart_ei global.smart_ei // smart edge handling + +#pragma parameter ei_limit " Smart Edges Effect Strength Limit" 2.0 1.0 12.0 0.1 +#define ei_limit global.ei_limit // smart edge handling + +#pragma parameter sth " Smart Edges Smoothing Threshold" 0.20 0.0 1.0 0.01 +#define sth global.sth // corner size + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter TATE " TATE Mode" 0.0 0.0 1.0 1.0 +#define TATE params.TATE // Screen orientation + +#pragma parameter IOS " Integer Scaling: Odd:Y, Even:'X'+Y" 0.0 0.0 4.0 1.0 +#define IOS params.IOS // Smart Integer Scaling + +#pragma parameter OS " R. Bloom Overscan Mode" 1.0 0.0 2.0 1.0 +#define OS params.OS // Do overscan + +#pragma parameter BLOOM " Raster bloom %" 0.0 0.0 20.0 1.0 +#define BLOOM params.BLOOM // Bloom overscan percentage + +#pragma parameter csize " Corner size" 0.0 0.0 0.07 0.01 +#define csize params.csize // corner size + +#pragma parameter bsize " Border smoothness" 600.0 100.0 600.0 25.0 +#define bsize params.bsize // border smoothness + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.125 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.125 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter bogus_masks "[ CRT MASK OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter shadowMask " CRT Mask: 0:CGWG, 1-4:Lottes, 5-7:'Trinitron'" 0.0 -1.0 8.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-8)" 0.3 -0.5 1.0 0.05 +#define maskstr params.maskstr // CGWG Mask Strength + +#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 2.0 1.0 +#define masksize params.masksize // Mask Size + +#pragma parameter maskDark " Lottes maskDark" 0.5 0.0 2.0 0.05 +#define maskDark params.maskDark // Dark "Phosphor" + +#pragma parameter maskLight " Lottes maskLight" 1.5 0.0 2.0 0.05 +#define maskLight params.maskLight // Light "Phosphor" + +#pragma parameter mcut " Mask 5-7 Low Strength" 1.15 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-7 cutoff + +#pragma parameter mask_gamma " Mask gamma" 2.40 1.0 5.0 0.05 +#define mask_gamma global.mask_gamma // Mask application gamma + +#pragma parameter slotmask " Slot Mask Strength" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask // Slot Mask ON/OFF + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 6.0 0.5 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1" 1.0 1.0 2.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 2.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.5 0.0 1.0 0.05 +#define mclip global.mclip // Slot Mask Size + +#pragma parameter gamma_out "Gamma out" 2.4 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + +#pragma parameter inters " Interlacing Effect Smoothness" 0.0 0.0 0.5 0.05 // Joint parameter with linearize pass, values must match +#define inters params.inters // interlacing effect smoothing + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define TEX0 vTexCoord + +#define SourceSize global.OriginalSize +#define OutputSize global.OutputSize +#define gl_FragCoord (vTexCoord * OutputSize.xy) + +#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 * 1.000001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D LinearizePass; +layout(set = 0, binding = 3) uniform sampler2D AvgLumPass; +layout(set = 0, binding = 4) uniform sampler2D GlowPass; +layout(set = 0, binding = 5) uniform sampler2D BloomPass; + +#define eps 1e-10 + +float st(float x) +{ + return exp2(-10.0*x*x); +} + +float sw0(float x, float color, float scanline) +{ + float tmp = mix(beam_min, beam_max, color); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +float sw1(float x, float color, float scanline) +{ + x = mix (x, beam_min*x, max(x-0.4*color,0.0)); + float tmp = mix(1.2*beam_min, beam_max, color); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +float sw2(float x, float color, float scanline) +{ + float tmp = mix(2.5*beam_min, beam_max, color); + tmp = mix(beam_max, tmp, pow(x, color+0.3)); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + pos = floor(pos/masksize); + vec3 mask = vec3(maskDark, maskDark, maskDark); + vec3 one = vec3(1.0); + float dark_compensate = mix(max( clamp( mix (mcut, maskstr, mx),0.0, 1.0) - 0.3, 0.0) + 1.0, 1.0, mx); + float mc = 1.0 - max(maskstr, 0.0); + + // No mask + if (shadowMask == -1.0) + { + mask = vec3(1.0); + } + + // Phosphor. + else if (shadowMask == 0.0) + { + pos.x = fract(pos.x*0.5); + if (pos.x < 0.5) { mask.r = 1.0; mask.g = mc; mask.b = 1.0; } + else { mask.r = mc; mask.g = 1.0; mask.b = mc; } + } + + // Very compressed TV style shadow mask. + else if (shadowMask == 1.0) + { + float line = maskLight; + float odd = 0.0; + + if (fract(pos.x/6.0) < 0.5) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.5) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + + mask*=line; + } + + // Aperture-grille. + else if (shadowMask == 2.0) + { + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // Stretched VGA style shadow mask (same as prior shaders). + else if (shadowMask == 3.0) + { + pos.x += pos.y*3.0; + pos.x = fract(pos.x/6.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // VGA style shadow mask. + else if (shadowMask == 4.0) + { + pos.xy = floor(pos.xy*vec2(1.0, 0.5)); + pos.x += pos.y*3.0; + pos.x = fract(pos.x/6.0); + + if (pos.x < 0.333) mask.r = maskLight; + else if (pos.x < 0.666) mask.g = maskLight; + else mask.b = maskLight; + } + + // Trinitron mask 5 + else if (shadowMask == 5.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.5) + { mask.r = 1.0; + mask.b = 1.0; + } + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Trinitron mask 6 + else if (shadowMask == 6.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.333) mask.r = 1.0; + else if (pos.x < 0.666) mask.g = 1.0; + else mask.b = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 7 + else if (shadowMask == 7.0) + { + float maskTmp = clamp(mix( mix(1.0, 0.0, mcut), mix(1.0, 0.0, maskstr), mx), 0.0, 1.0) * dark_compensate; + mask = vec3(maskTmp); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.5) mask = vec3(1.0); + } + + // 4k mask + else + { + mask = vec3(mc); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask.r = 1.0; + else if (pos.x < 0.4) mask.rg = 1.0.xx; + else if (pos.x < 0.7) mask.gb = 1.0.xx; + else mask.b = 1.0; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if (slotmask == 0.0) return 1.0; + + pos = floor(pos/slotms); + float mlen = slotwidth*2.0; + float px = fract(pos.x/mlen); + float py = floor(fract(pos.y/(2.0*double_slot))*2.0*double_slot); + float slot_dark = 1.0 - slotmask*(1.0 - 0.125*m); + float slot = 1.0; + if (py == 0.0 && px < 0.5) slot = slot_dark; else + if (py == double_slot && px >= 0.5) slot = slot_dark; + + return slot; +} + +// Distortion of scanlines, and end of screen alpha (PD Lottes Curvature) +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY); + return pos*0.5 + 0.5; +} + +vec2 Overscan(vec2 pos, float dx, float dy){ + pos=pos*2.0-1.0; + pos*=vec2(dx,dy); + return pos*0.5+0.5; +} + + +// Borrowed from cgwg's crt-geom, under GPL + +float corner(vec2 coord) +{ + coord = (coord - vec2(0.5)) * 1.0 + vec2(0.5); + coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); + vec2 cdist = vec2(max(csize, max((1.0-smoothstep(100.0,600.0,bsize))*0.01,0.002))); + coord = (cdist - min(coord,cdist)); + float dist = sqrt(dot(coord,coord)); + return clamp((cdist.x-dist)*bsize,0.0, 1.0); +} + +vec3 declip(vec3 c, float b) +{ + float m = max(max(c.r,c.g),c.b); + if (m > b) c = c*b/m; + return c; +} + +vec3 gc(vec3 c) +{ + float mc = max(max(c.r,c.g),c.b); + float mg = pow(mc, 1.0/gamma_c); + return c * mg/(mc + eps); +} + +void main() +{ + + float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.1,0.1)).a; + float gamma_in = 1.0/COMPAT_TEXTURE(LinearizePass, vec2(0.25,0.25)).a; + float intera = COMPAT_TEXTURE(LinearizePass, vec2(0.75,0.25)).a; + bool interb = (intera < 0.75); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + if (IOS > 0.0){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = mix(diff.y, diff.x, TATE); + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = mix(vec2(TEX0.x, texcoord.y), vec2(texcoord.x, TEX0.y), TATE); + } + + float factor = 1.00 + (1.0-0.5*OS)*BLOOM/100.0 - lum*BLOOM/100.0; + texcoord = Overscan(texcoord, factor, factor); + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + + bool notate = (TATE < 0.5); + bool smarte = (smart_ei > 0.0 && notate); // smart edge interpolation on / off + + vec2 coffset = vec2(0.5, 0.5); + + vec2 ps = SourceSize.zw; + vec2 OGL2Pos = pos * SourceSize.xy - coffset; + vec2 fp = fract(OGL2Pos); + + vec2 dx = vec2(ps.x,0.0); + vec2 dy = vec2(0.0, ps.y); + + // Reading the texels + vec2 x2 = 2.0*dx; + vec2 y2 = 2.0*dy; + + vec2 offx = dx; + vec2 off2 = x2; + vec2 offy = dy; + float fpx = fp.x; + if(!notate) + { + offx = dy; + off2 = y2; + offy = dx; + fpx = fp.y; + } + float f = (notate) ? fp.y : fp.x; + + vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps; + + if (interb) pC4.y = pos.y - inters * SourceSize.w; + + float zero = exp2(-h_sharp); + float sharp1 = s_sharp * zero; + + float wl3 = 2.0 + fpx; + float wl2 = 1.0 + fpx; + float wl1 = fpx; + float wr1 = 1.0 - fpx; + float wr2 = 2.0 - fpx; + float wr3 = 3.0 - fpx; + + wl3*=wl3; wl3 = exp2(-h_sharp*wl3); + wl2*=wl2; wl2 = exp2(-h_sharp*wl2); + wl1*=wl1; wl1 = exp2(-h_sharp*wl1); + wr1*=wr1; wr1 = exp2(-h_sharp*wr1); + wr2*=wr2; wr2 = exp2(-h_sharp*wr2); + wr3*=wr3; wr3 = exp2(-h_sharp*wr3); + + float fp1 = 1.-fpx; + + float wnorm = max(wl1, wr1); + + float twl3 = max(wl3 - sharp1, 0.0); + float twl2 = max(wl2 - sharp1, mix(-0.12, 0.0, 1.0-fp1*fp1)); float swl2 = max(twl2, 0.0); + float twl1 = max(wl1 - sharp1, 0.0); + float twr1 = max(wr1 - sharp1, 0.0); + float twr2 = max(wr2 - sharp1, mix(-0.12, 0.0, 1.0-fpx*fpx)); float swr2 = max(twr2, 0.0); + float twr3 = max(wr3 - sharp1, 0.0); + + bool sharp = (sharp1 > 0.0); + + float rwl3, rwl2, rwr2; + + float rwl1 = twl1; + float rwr1 = twr1; + vec3 c1, c2; + + if (smarte) + { + rwl3 = wl3; rwl2 = wl2; + rwl1 = wl1; rwr1 = wr1; + rwr2 = wr2; + twl3 = 0.0; twr3 = 0.0; + vec3 t = COMPAT_TEXTURE(AvgLumPass, pC4 - offy).xyz; + vec3 a = COMPAT_TEXTURE(AvgLumPass, pC4 ).xyz; + vec3 b = COMPAT_TEXTURE(AvgLumPass, pC4 + offy).xyz; + vec3 d = COMPAT_TEXTURE(AvgLumPass, pC4 +dy+dy).xyz; + c1 = (h_sharp > 2.6) ? a : min(a,(t + a + b)/3.0); c1 = max(c1 - sth, 0.0); + c2 = (h_sharp > 2.6) ? b : min(b,(a + b + d)/3.0); c2 = max(c2 - sth, 0.0); + } + + float wts = 1.0/(swl2 + rwl1 + rwr1 + swr2); + + vec3 l3, l2, l1, r1, r2, r3, sl2, sl1, sr1, sr2, color1, color2, color0, ct, colmin, colmax; + + l3 = COMPAT_TEXTURE(LinearizePass, pC4 -off2).rgb; + l2 = COMPAT_TEXTURE(LinearizePass, pC4 -offx).rgb; + l1 = COMPAT_TEXTURE(LinearizePass, pC4 ).rgb; + r1 = COMPAT_TEXTURE(LinearizePass, pC4 +offx).rgb; + r2 = COMPAT_TEXTURE(LinearizePass, pC4 +off2).rgb; + r3 = COMPAT_TEXTURE(LinearizePass, pC4 +offx+off2).rgb; + + sl2 = l2*l2*l2; sl2*=sl2; + sl1 = l1*l1*l1; sl1*=sl1; + sr1 = r1*r1*r1; sr1*=sr1; + sr2 = r2*r2*r2; sr2*=sr2; + + colmin = min(min(l1,r1), min(l2,r2)); + colmax = max(max(l1,r1), max(l2,r2)); + + if (smarte) + { + float pc = min(1.0 + smart_ei*c1.y, ei_limit); + float pl = min(1.0 + smart_ei*max(c1.y,c1.x), ei_limit); + float pr = min(1.0 + smart_ei*max(c1.y,c1.z), ei_limit); + twl1 = pow(rwl1, pc); twr1 = pow(rwr1, pc); + twl2 = pow(rwl2, pl); twr2 = pow(rwr2, pr); + float wmax = max(twl1, twr1); + float sharp_ei = s_sharp*pow(zero, pc)/wmax; + twl2 = max(twl2/wmax - sharp_ei, mix(-0.15, 0.0, 1.0-fp1*fp1)); + twl1 = max(twl1/wmax - sharp_ei, 0.0); + twr1 = max(twr1/wmax - sharp_ei, 0.0); + twr2 = max(twr2/wmax - sharp_ei, mix(-0.15, 0.0, 1.0-fpx*fpx)); + } + color1 = (l3*twl3 + l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2 + r3*twr3)/(twl3+twl2+twl1+twr1+twr2+twr3); + + if (sharp) color1 = clamp(color1, colmin, colmax); + + + vec3 gtmp = vec3(1.0/6.0); + vec3 scolor1 = color1; + + scolor1 = (sl2*swl2 + sl1*rwl1 + sr1*rwr1 + sr2*swr2)*wts; + scolor1 = pow(scolor1, gtmp); vec3 mcolor1 = scolor1; + scolor1 = min(mix(color1, scolor1, spike),1.0); + + vec3 scolor2, mcolor2; + + if (interb) pC4.y = pos.y + inters * SourceSize.w; else + pC4+=offy; + + l3 = COMPAT_TEXTURE(LinearizePass, pC4 -off2).rgb; + l2 = COMPAT_TEXTURE(LinearizePass, pC4 -offx).rgb; + l1 = COMPAT_TEXTURE(LinearizePass, pC4 ).rgb; + r1 = COMPAT_TEXTURE(LinearizePass, pC4 +offx).rgb; + r2 = COMPAT_TEXTURE(LinearizePass, pC4 +off2).rgb; + r3 = COMPAT_TEXTURE(LinearizePass, pC4 +offx+off2).rgb; + + sl2 = l2*l2*l2; sl2*=sl2; + sl1 = l1*l1*l1; sl1*=sl1; + sr1 = r1*r1*r1; sr1*=sr1; + sr2 = r2*r2*r2; sr2*=sr2; + + colmin = min(min(l1,r1), min(l2,r2)); + colmax = max(max(l1,r1), max(l2,r2)); + + if (smarte) + { + float pc = min(1.0 + smart_ei*c2.y, ei_limit); + float pl = min(1.0 + smart_ei*max(c2.y,c2.x), ei_limit); + float pr = min(1.0 + smart_ei*max(c2.y,c2.z), ei_limit); + twl1 = pow(rwl1, pc); twr1 = pow(rwr1, pc); + twl2 = pow(rwl2, pl); twr2 = pow(rwr2, pr); + float wmax = max(twl1, twr1); + float sharp_ei = s_sharp*pow(zero, pc)/wmax; + twl2 = max(twl2/wmax - sharp_ei, mix(-0.15, 0.0, 1.0-fp1*fp1)); + twl1 = max(twl1/wmax - sharp_ei, 0.0); + twr1 = max(twr1/wmax - sharp_ei, 0.0); + twr2 = max(twr2/wmax - sharp_ei, mix(-0.15, 0.0, 1.0-fpx*fpx)); + } + color2 = (l3*twl3 + l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2 + r3*twr3)/(twl3+twl2+twl1+twr1+twr2+twr3); + + if (sharp) color2 = clamp(color2, colmin, colmax); + + scolor2 = color2; + scolor2 = (sl2*swl2 + sl1*rwl1 + sr1*rwr1 + sr2*swr2)*wts; + scolor2 = pow(scolor2, gtmp); mcolor2 = scolor2; + scolor2 = min(mix(color2, scolor2, spike),1.0); + + // calculating scanlines + + vec3 ctmp; vec3 mcolor; float w3; vec3 color; + vec3 one = vec3(1.0); + +if (!interb) +{ + float shape1 = mix(scanline1, scanline2, f); + float shape2 = mix(scanline1, scanline2, 1.0-f); + + float wt1 = st(f); + float wt2 = st(1.0-f); + + vec3 color00 = color1*wt1 + color2*wt2; + vec3 scolor0 = scolor1*wt1 + scolor2*wt2; + mcolor = (mcolor1*wt1 + mcolor2*wt2)/(wt1+wt2); + + ctmp = color00/(wt1+wt2); + vec3 sctmp = scolor0/(wt1+wt2); + + float wf1, wf2; + + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = max(max(cref1.r,cref1.g),cref1.b); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = max(max(cref2.r,cref2.g),cref2.b); + + float f1 = f; + float f2 = 1.0-f; + + if (gsl == 0.0) { wf1 = sw0(f1,creff1,shape1); wf2 = sw0(f2,creff2,shape2);} else + if (gsl == 1.0) { wf1 = sw1(f1,creff1,shape1); wf2 = sw1(f2,creff2,shape2);} else + if (gsl == 2.0) { wf1 = sw2(f1,creff1,shape1); wf2 = sw2(f2,creff2,shape2);} + + if ((wf1 + wf2) > 1.0) { float wtmp = 1.0/(wf1+wf2); wf1*=wtmp; wf2*=wtmp; } + + // Scanline darken 'edges' effect - need to uncomment it. + + // float ws1 = max(wf1 - scansub, 0.2*wf1*wf2); wf1 = ws1/(1.0 - wf1 + ws1); + // float ws2 = max(wf2 - scansub, 0.2*wf2*wf1); wf2 = ws2/(1.0 - wf2 + ws2); + + // Scanline saturation application + + vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); + + cref1 = color1 / (max(max(color1.r,color1.g),color1.b) + 0.00001); + cref2 = color2 / (max(max(color2.r,color2.g),color2.b) + 0.00001); + + w1 = mix(w1*mix(one, cref1*cref1*cref1, scans), w1, wf1); + w2 = mix(w2*mix(one, cref2*cref2*cref2, scans), w2, wf2); + + vec3 cd1 = one; vec3 cd2 = one; float vm = sqrt(abs(vertmask)); + + float v_high1 = 1.0 + 0.3*vm; + float v_high2 = 1.0 + 0.6*vm; + float v_low = 1.0 - vm; + + float ds1 = min(pow(2.0*f1 + 0.01, f2), 1.0); + float ds2 = min(pow(2.0*f2 + 0.01, f1), 1.0); + + if (vertmask < 0.0) + { + cd1 = mix(one, vec3(v_high2, v_low, v_low), ds1); + cd2 = mix(one, vec3(v_low, v_high1, v_high1), ds2); + } + else + { + cd1 = mix(one, vec3(v_high1, v_low, v_high1), ds1); + cd2 = mix(one, vec3(v_low, v_high2, v_low), ds2); + } + + color = gc(color1)*w1*cd1 + gc(color2)*w2*cd2; + color = min(color, 1.0); + w3 = wf1+wf2; +} + + if (interb) + { + color = gc(0.5*(color1+color2)); + mcolor = vec3(max(max(color.r,color.g), color.b)); + } + + float mx = max(max(mcolor.r,mcolor.g),mcolor.b); + mx = pow(mx, 1.40/gamma_in); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + float smask = (notate) ? SlotMask(gl_FragCoord.xy * 1.000001, mx) : SlotMask(gl_FragCoord.yx * 1.000001, mx); + + cmask*= (notate) ? Mask(gl_FragCoord.xy * 1.000001, mx) : Mask(gl_FragCoord.yx * 1.000001, mx); + + color = pow(color, vec3(mask_gamma/gamma_in)); + color = color*cmask; + color = min(color,1.0); + color = color*smask; + color = pow(color, vec3(gamma_in/mask_gamma)); + + cmask = min(cmask*smask, 1.0); + + if (interb) ctmp = color; + float colmx = pow( max( max(ctmp.r, ctmp.g), ctmp.b), 1.40/gamma_out); + float bb = mix(brightboost, brightboost1, colmx); + if (interb) bb = (abs(intera-0.5)<0.1) ? pow(0.80*bb, 0.65) : pow(bb, 0.70); + color*=bb; + + vec3 Glow = COMPAT_TEXTURE(GlowPass, pos ).rgb; + vec3 Bloom = COMPAT_TEXTURE(BloomPass, pos ).rgb; + + vec3 Bloom1 = min(Bloom*(orig1+color), max(0.5*(colmx + orig1 - color),0.0)); + color = color + bloom*Bloom1; + + color = min(color, mix(one, cmask, mclip)); + if (!interb) color = declip(color, pow(w3,0.6)); + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + color = color + 0.5*glow*Glow; + + color = color + 0.5*(Bloom+Bloom*Bloom)*halation; + + float gmo = 1.0/gamma_out; + if (interb) gmo = gmo / 0.70; + + color = pow(color, vec3(gmo)); + + FragColor = vec4(color*corner(pos0), float(!notate)); +} diff --git a/crt/shaders/guest/crt-gdv-new/deconvergence.slang b/crt/shaders/guest/crt-gdv-new/deconvergence.slang new file mode 100644 index 0000000..32bf562 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/deconvergence.slang @@ -0,0 +1,123 @@ +#version 450 + +/* + CRT - Guest - Dr. Venom - Deconvergence pass + + Copyright (C) 2021 guest(r) - guest.r@gmail.com + + 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. + +*/ + +layout(push_constant) uniform Push +{ + vec4 OutputSize; + uint FrameCount; + float deconr; + float decons; + float cswitch; +} params; + +#pragma parameter bogus_hdeconvergence "[ HORIZONTAL DECONVERGENCE ]: " 0.0 0.0 1.0 1.0 +#pragma parameter deconr " Hor. Deconvergence Color/Range" 0.0 -12.0 12.0 0.5 +#define deconr params.deconr // Horizontal deconvergence colors range +#pragma parameter decons " Horizontal Deconvergence Strength" 0.5 0.0 1.0 0.05 +#define decons params.decons // Horizontal deconvergence colors strength +#pragma parameter cswitch " Switch Deconvergence Colors" 1.0 -1.0 1.0 2.0 +#define cswitch params.cswitch // Switch colors left/right + +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; +layout(set = 0, binding = 3) uniform sampler2D AvgLumPass; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +void main() +{ + + vec3 color = COMPAT_TEXTURE(Source, vTexCoord).rgb; + vec3 result = color; + + if (abs(deconr) > 0.5) + { + float step = cswitch/1920.0; + + vec2 dx = vec2(step, 0.0); + + float shift = step * abs(deconr); + vec4 coords = vec4(shift, -shift, 0.0, -0.5*shift); + + vec2 rc = coords.rb; + vec2 gc = coords.gb; + vec2 bc = coords.rb; + + if (deconr < -0.05) { rc = coords.rb; gc = coords.ab; bc = coords.gb; } + + float TATE = round(COMPAT_TEXTURE(Source, vec2(0.5)).a); + + if (TATE > 0.5) + { + rc = -rc.yx; gc = -gc.yx, bc = -bc.yx; dx = -dx.yx; + } + + float r1 = COMPAT_TEXTURE(Source, vTexCoord + rc).r; + float g1 = COMPAT_TEXTURE(Source, vTexCoord + gc).g; + float b1 = COMPAT_TEXTURE(Source, vTexCoord + bc).b; + + float r2 = COMPAT_TEXTURE(Source, vTexCoord + rc -dx).r; + float g2 = COMPAT_TEXTURE(Source, vTexCoord + gc -dx).g; + float b2 = COMPAT_TEXTURE(Source, vTexCoord + bc -dx).b; + + float r3 = COMPAT_TEXTURE(Source, vTexCoord + rc +dx).r; + float g3 = COMPAT_TEXTURE(Source, vTexCoord + gc +dx).g; + float b3 = COMPAT_TEXTURE(Source, vTexCoord + bc +dx).b; + + vec3 result1 = vec3(r1,g1,b1); + vec3 result2 = vec3(r2,g2,b2); + vec3 result3 = vec3(r3,g3,b3); + + result1 = mix(color, result1, decons); + result2 = mix(color, result2, decons); + result3 = mix(color, result3, decons); + result = (result1+result2+result3)/3.0; + result = plant(result, 0.5*(max(max(color.r,color.g),color.b)+max(max(result.r,result.g),result.b))); + } + + FragColor = vec4(result, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang b/crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang new file mode 100644 index 0000000..8e70131 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang @@ -0,0 +1,101 @@ +#version 450 + +/* + Gaussian blur - horizontal pass, dynamic range, resizable + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma format R16G16B16A16_SFLOAT + +layout(push_constant) uniform Push +{ + vec4 LinearizePassSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float SIZEH; + float SIGMA_H; +} params; + +#pragma parameter bogus_glow "[ GLOW/BLOOM PASS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter SIZEH " H. Glow Radius" 5.0 1.0 30.0 1.0 +#define SIZEH params.SIZEH + +#pragma parameter SIGMA_H " Horizontal Glow Sigma" 1.25 0.5 15.0 0.10 +#define SIGMA_H params.SIGMA_H + +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 LinearizePass; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +float invsqrsigma = 1.0/(2.0*SIGMA_H*SIGMA_H); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +void main() +{ + vec4 SourceSize1 = params.OriginalSize; + float f = fract(SourceSize1.x * vTexCoord.x); + f = 0.5 - f; + vec2 tex = floor(SourceSize1.xy * vTexCoord)*SourceSize1.zw + 0.5*SourceSize1.zw; + vec3 color = vec3(0.0); + vec2 dx = vec2(SourceSize1.z, 0.0); + + float w; + float wsum = 0.0; + vec3 pixel; + float n = -SIZEH; + + do + { + pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx).rgb; + w = gaussian(n+f); + color = color + w * pixel; + wsum = wsum + w; + n = n + 1.0; + + } while (n <= SIZEH); + + color = color / wsum; + + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang b/crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang new file mode 100644 index 0000000..6dc6f17 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang @@ -0,0 +1,100 @@ +#version 450 + +/* + Gaussian blur - vertical pass, dynamic range, resizable + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma format R16G16B16A16_SFLOAT + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float SIZEV; + float SIGMA_V; +} params; + + +#pragma parameter SIZEV " V. Glow Radius" 5.0 1.0 30.0 1.0 +#define SIZEV params.SIZEV + +#pragma parameter SIGMA_V " Vertical Glow Sigma" 1.25 0.5 15.0 0.10 +#define SIGMA_V params.SIGMA_V + +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; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +float invsqrsigma = 1.0/(2.0*SIGMA_V*SIGMA_V); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +void main() +{ + vec4 SourceSize1 = params.SourceSize; + float f = fract(SourceSize1.y * vTexCoord.y); + f = 0.5 - f; + vec2 tex = floor(SourceSize1.xy * vTexCoord)*SourceSize1.zw + 0.5*SourceSize1.zw; + vec3 color = vec3(0.0); + vec2 dy = vec2(0.0, SourceSize1.w); + + float w; + float wsum = 0.0; + vec3 pixel; + float n = -SIZEV; + + do + { + pixel = COMPAT_TEXTURE(Source, tex + n*dy).rgb; + w = gaussian(n+f); + color = color + w * pixel; + wsum = wsum + w; + n = n + 1.0; + + } while (n <= SIZEV); + + color = color / wsum; + + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang b/crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang new file mode 100644 index 0000000..9cd1ce2 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang @@ -0,0 +1,163 @@ +#version 450 + +/* + Interlacing + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma format R32G32B32A32_SFLOAT + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float decon; + float deconstr; + float GAMMA_INPUT; + float inter; + float interm; + float intres; + float inters; + float iscan; +} params; + +#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 1.0 5.0 0.05 +#define GAMMA_INPUT params.GAMMA_INPUT + +#pragma parameter bogus_deconvergence "[ HORIZONTAL DECONVERGENCE ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter decon " Horizontal Deconvergence Range" 1.0 -8.0 8.0 0.250 +#define decon params.decon // Horizontal deconvergence range + +#pragma parameter deconstr " Horizontal Deconvergence Strength" 0.0 0.0 1.0 0.05 +#define deconstr params.deconstr // Horizontal deconvergence strength + +#pragma parameter bogus_interlacing "[ INTERLACING OPTIONS ]: " 0.0 0.0 0.0 1.0 + +#pragma parameter inter " Interlace Trigger Resolution :" 350.0 0.0 800.0 25.0 +#define inter params.inter // interlace resolution + +#pragma parameter interm " Interlace Mode: OFF, Normal 1-3, Interpolation 4-5" 1.0 0.0 5.0 1.0 +#define interm params.interm // interlace mode +#define interm params.interm // interlace mode + +#pragma parameter inters " Interlacing Effect Smoothness" 0.0 0.0 0.5 0.05 // Joint parameter with main pass, values must match +#define inters params.inters // interlacing effect smoothing + +#pragma parameter iscan " Interlacing Scanline Effect" 0.20 0.0 1.0 0.05 +#define iscan params.iscan // interlacing effect scanlining + +#pragma parameter intres " Internal Resolution: 1.0:240p, 1.5...y-dowsample" 0.0 0.0 4.0 0.5 // Joint parameter with main pass, values must match +#define intres params.intres // interlace resolution + +#define SourceSize params.SourceSize + +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 * 1.00001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D PrePass; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + + +void main() +{ + vec2 dx1 = vec2(1.0/SourceSize.x, 0.0)*decon; + vec2 dx2 = -dx1*decon; + vec2 dy = vec2(0.0, 1.0/SourceSize.y); + + vec3 c1 = COMPAT_TEXTURE(PrePass, vTexCoord).rgb; + vec3 c2 = COMPAT_TEXTURE(PrePass, vTexCoord + dy).rgb; + + float r1 = COMPAT_TEXTURE(PrePass, vTexCoord + dx1).r; + float b1 = COMPAT_TEXTURE(PrePass, vTexCoord + dx2).b; + float r2 = COMPAT_TEXTURE(PrePass, vTexCoord + dx1 + dy).r; + float b2 = COMPAT_TEXTURE(PrePass, vTexCoord + dx2 + dy).b; + + vec3 res1 = c1; + vec3 res2 = c2; + + res1.rb = mix(c1.rb, vec2(r1,b1), deconstr); + res2.rb = mix(c2.rb, vec2(r2,b2), deconstr); + + vec3 res = res1; + float gamma_in = 1.0/GAMMA_INPUT; + float intera = 1.0; + + float yres_div = 1.0; if (intres > 1.25) yres_div = intres; + + if (inter < SourceSize.y/yres_div && interm > 0.5 && intres != 1.0) + { + intera = 0.5; + float line_no = floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)); + float frame_no = floor(mod(float(params.FrameCount),2.0)); + float ii = abs(line_no-frame_no); + + float m1 = max(max(c1.r,c1.g),c1.b); + float m2 = max(max(c2.r,c2.g),c2.b); + vec3 df = abs(c1-c2); + + float d = max(max(df.r,df.g),df.b); + if (interm == 2.0) d = mix(0.1*d,10.0*d, step(m1/(m2+0.0001),m2/(m1+0.0001))); + + float r; + + if (interm < 3.5) + { + r = max(m1*ii, (1.0-iscan)*min(m1,m2)); + res = plant( mix(mix(res1,res2, min(mix(m1, (1.0-m2), min(m1,1.0-m1))/(d+0.00001),1.0)), res1, ii), r); + if (interm == 3.0) res = (1.0-0.5*iscan)*mix(res2, res1, ii); + intera = 0.0; + } + if (interm == 5.0) { res = mix(res2, res1, 0.5); intera = 0.5; } + + res = pow(res, vec3(GAMMA_INPUT*0.70)); + gamma_in = 1.0/(GAMMA_INPUT*0.70); + } + else res = pow(res, vec3(GAMMA_INPUT)); + + if (vTexCoord.x > 0.5) gamma_in = intera; + + FragColor = vec4(res, gamma_in); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/linearize.slang b/crt/shaders/guest/crt-gdv-new/linearize.slang new file mode 100644 index 0000000..3327d5c --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/linearize.slang @@ -0,0 +1,133 @@ +#version 450 + +/* + Interlacing + + Copyright (C) 2020 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma format R32G32B32A32_SFLOAT + +layout(push_constant) uniform Push +{ + vec4 OriginalSize; + vec4 OutputSize; + vec4 SourceSize; + uint FrameCount; + float GAMMA_INPUT; + float inter; + float interm; + float inters; + float iscan; +} params; + + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + + +#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 1.0 5.0 0.05 +#define GAMMA_INPUT params.GAMMA_INPUT + +#pragma parameter bogus_interlacing "[ INTERLACING OPTIONS ]: " 0.0 0.0 0.0 1.0 + +#pragma parameter inter " Interlace Trigger Resolution :" 350.0 0.0 800.0 25.0 +#define inter params.inter // interlace resolution + +#pragma parameter interm " Interlace Mode: OFF, Normal 1-3, Interpolation 4-5" 1.0 0.0 5.0 1.0 +#define interm params.interm // interlace mode + +#pragma parameter inters " Interlacing Effect Smoothness" 0.0 0.0 0.5 0.05 // // Joint parameter with main pass, values must match +#define inters params.inters // interlacing effect smoothing + +#pragma parameter iscan " Interlacing Scanline Effect" 0.20 0.0 1.0 0.05 +#define iscan params.iscan // interlacing effect scanlining + + +#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 * 1.000001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D PrePass; + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define SourceSize params.SourceSize + + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + + +void main() +{ + vec3 c1 = COMPAT_TEXTURE(PrePass, vTexCoord).rgb; + vec3 c2 = COMPAT_TEXTURE(PrePass, vTexCoord + vec2(0.0, 1.0/params.OriginalSize.y)).rgb; + vec3 c = c1; + + float intera = 1.0; + float gamma_in = 1.0/GAMMA_INPUT; + + if (inter < params.OriginalSize.y && interm > 0.5) + { + intera = 0.5; + float line_no = floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)); + float frame_no = floor(mod(float(params.FrameCount),2.0)); + float ii = abs(line_no-frame_no); + + float m1 = max(max(c1.r,c1.g),c1.b); + float m2 = max(max(c2.r,c2.g),c2.b); + vec3 df = abs(c1-c2); + + float d = max(max(df.r,df.g),df.b); + if (interm == 2.0) d = mix(0.1*d,10.0*d, step(m1/(m2+0.0001),m2/(m1+0.0001))); + + float r; + + if (interm < 3.5) + { + r = max(m1*ii, (1.0-iscan)*min(m1,m2)); + c = plant( mix(mix(c1,c2, min(mix(m1, 1.0-m2, min(m1,1.0-m1))/(d+0.00001),1.0)), c1, ii), r); + if (interm == 3.0) c = (1.0-0.5*iscan)*mix(c2, c1, ii); + intera = 0.0; + } + if (interm == 5.0) { c = mix(c2, c1, 0.5); intera = 0.5; } + + c = pow(c, vec3(GAMMA_INPUT*0.70)); + gamma_in = 1.0/(GAMMA_INPUT*0.70); + } + else c = pow(c, vec3(GAMMA_INPUT)); + + if (vTexCoord.x > 0.5) gamma_in = intera; + + FragColor = vec4(c, gamma_in); +} \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang b/crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang new file mode 100644 index 0000000..e065f38 --- /dev/null +++ b/crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang @@ -0,0 +1,316 @@ +#version 450 + +/* + CRT GDV Afterglow, color altering + + Copyright (C) 2019-2021 guest(r) and Dr. Venom + + 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. + +*/ + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float TNTC; + float LS; + float LUTLOW, LUTBR; + float CP, CS; + float WP; + float wp_saturation; + float AS, sat; +} params; + +#pragma parameter AS " Afterglow Strength" 0.07 0.0 0.50 0.01 +#define AS params.AS + +#pragma parameter sat " Afterglow saturation" 0.10 0.0 1.0 0.01 +#define sat params.sat + +#pragma parameter bogus_color "[ COLOR TWEAKS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter TNTC " LUT Number/Colors (1.0-4.0)" 0.0 0.0 4.0 1.0 +#define TNTC params.TNTC + +#pragma parameter LS " LUT Size" 32.0 32.0 64.0 32.0 +#define LS params.LS + +#pragma parameter LUTLOW " Fix LUT Dark - Range" 5.0 0.0 50.0 1.0 +#define LUTLOW params.LUTLOW + +#pragma parameter LUTBR " Fix LUT Brightness" 1.0 0.0 1.0 0.10 +#define LUTBR params.LUTBR + +#pragma parameter CP " CRT Color Profile" 0.0 -1.0 5.0 1.0 +#pragma parameter CS " Color Space: sRGB, DCI, Adobe, Rec.2020" 0.0 0.0 3.0 1.0 + +#define CP params.CP +#define CS params.CS + +#pragma parameter WP " Color Temperature %" 0.0 -100.0 100.0 5.0 +#pragma parameter wp_saturation " Saturation Adjustment" 1.0 0.0 2.0 0.05 + +#define WP params.WP +#define wp_saturation params.wp_saturation + +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 StockPass; +layout(set = 0, binding = 3) uniform sampler2D AfterglowPass; +layout(set = 0, binding = 4) uniform sampler2D SamplerLUT1; +layout(set = 0, binding = 5) uniform sampler2D SamplerLUT2; +layout(set = 0, binding = 6) uniform sampler2D SamplerLUT3; +layout(set = 0, binding = 7) uniform sampler2D SamplerLUT4; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + + +// Color profile matrices + +const mat3 Profile0 = +mat3( + 0.412391, 0.212639, 0.019331, + 0.357584, 0.715169, 0.119195, + 0.180481, 0.072192, 0.950532 +); + +const mat3 Profile1 = +mat3( + 0.430554, 0.222004, 0.020182, + 0.341550, 0.706655, 0.129553, + 0.178352, 0.071341, 0.939322 +); + +const mat3 Profile2 = +mat3( + 0.396686, 0.210299, 0.006131, + 0.372504, 0.713766, 0.115356, + 0.181266, 0.075936, 0.967571 +); + +const mat3 Profile3 = +mat3( + 0.393521, 0.212376, 0.018739, + 0.365258, 0.701060, 0.111934, + 0.191677, 0.086564, 0.958385 +); + +const mat3 Profile4 = +mat3( + 0.392258, 0.209410, 0.016061, + 0.351135, 0.725680, 0.093636, + 0.166603, 0.064910, 0.850324 +); + +const mat3 Profile5 = +mat3( + 0.377923, 0.195679, 0.010514, + 0.317366, 0.722319, 0.097826, + 0.207738, 0.082002, 1.076960 +); + +const mat3 ToSRGB = +mat3( + 3.240970, -0.969244, 0.055630, +-1.537383, 1.875968, -0.203977, +-0.498611, 0.041555, 1.056972 +); + +const mat3 ToDCI = +mat3( + 2.725394, -0.795168, 0.041242, +-1.018003, 1.689732, -0.087639, +-0.440163, 0.022647, 1.100929 +); + +const mat3 ToAdobe = +mat3( + 2.041588, -0.969244, 0.013444, +-0.565007, 1.875968, -0.11836, +-0.344731, 0.041555, 1.015175 +); + +const mat3 ToREC = +mat3( + 1.716651, -0.666684, 0.017640, +-0.355671, 1.616481, -0.042771, +-0.253366, 0.015769, 0.942103 +); + +// Color temperature matrices + +const mat3 D65_to_D55 = mat3 ( + 0.4850339153, 0.2500956126, 0.0227359648, + 0.3488957224, 0.6977914447, 0.1162985741, + 0.1302823568, 0.0521129427, 0.6861537456); + + +const mat3 D65_to_D93 = mat3 ( + 0.3683017655, 0.1899055978, 0.0172641453, + 0.3555467892, 0.7110935785, 0.1185155964, + 0.2475020592, 0.0990008237, 1.3035108450); + + +vec3 fix_lut(vec3 lutcolor, vec3 ref) +{ + float r = length(ref); + float l = length(lutcolor); + float m = max(max(ref.r,ref.g),ref.b); + ref = normalize(lutcolor + 0.0000001) * mix(r, l, pow(m,1.25)); + return mix(lutcolor, ref, LUTBR); +} + + +void main() +{ + vec4 imgColor = COMPAT_TEXTURE(StockPass, vTexCoord.xy); + vec4 aftglow = COMPAT_TEXTURE(AfterglowPass, vTexCoord.xy); + + float w = 1.0-aftglow.w; + + float l = length(aftglow.rgb); + aftglow.rgb = AS*w*normalize(pow(aftglow.rgb + 0.01, vec3(sat)))*l; + + vec3 color = imgColor.rgb; + + if (int(TNTC) == 0) + { + color.rgb = imgColor.rgb; + } + else + { + float lutlow = LUTLOW/255.0; float invLS = 1.0/LS; + vec3 lut_ref = imgColor.rgb + lutlow*(1.0 - pow(imgColor.rgb, 0.333.xxx)); + float lutb = lut_ref.b * (1.0-0.5*invLS); + lut_ref.rg = lut_ref.rg * (1.0-invLS) + 0.5*invLS; + float tile1 = ceil (lutb * (LS-1.0)); + float tile0 = max(tile1 - 1.0, 0.0); + float f = fract(lutb * (LS-1.0)); if (f == 0.0) f = 1.0; + vec2 coord0 = vec2(tile0 + lut_ref.r, lut_ref.g)*vec2(invLS, 1.0); + vec2 coord1 = vec2(tile1 + lut_ref.r, lut_ref.g)*vec2(invLS, 1.0); + vec4 color1, color2, res; + + if (int(TNTC) == 1) + { + color1 = COMPAT_TEXTURE(SamplerLUT1, coord0); + color2 = COMPAT_TEXTURE(SamplerLUT1, coord1); + res = mix(color1, color2, f); + } + else if (int(TNTC) == 2) + { + color1 = COMPAT_TEXTURE(SamplerLUT2, coord0); + color2 = COMPAT_TEXTURE(SamplerLUT2, coord1); + res = mix(color1, color2, f); + } + else if (int(TNTC) == 3) + { + color1 = COMPAT_TEXTURE(SamplerLUT3, coord0); + color2 = COMPAT_TEXTURE(SamplerLUT3, coord1); + res = mix(color1, color2, f); + } + else if (int(TNTC) == 4) + { + color1 = COMPAT_TEXTURE(SamplerLUT4, coord0); + color2 = COMPAT_TEXTURE(SamplerLUT4, coord1); + res = mix(color1, color2, f); + } + + if (imgColor.r == 0.0) res.r = 0.0; + if (imgColor.g == 0.0) res.g = 0.0; + if (imgColor.b == 0.0) res.b = 0.0; + + res.rgb = fix_lut (res.rgb, imgColor.rgb); + + color = mix(imgColor.rgb, res.rgb, min(TNTC,1.0)); + } + + vec3 c = clamp(color, 0.0, 1.0); + + float p; + mat3 m_out; + + if (CS == 0.0) { p = 2.4; m_out = ToSRGB; } else + if (CS == 1.0) { p = 2.6; m_out = ToDCI; } else + if (CS == 2.0) { p = 2.2; m_out = ToAdobe;} else + if (CS == 3.0) { p = 2.4; m_out = ToREC; } + + color = pow(c, vec3(p)); + + mat3 m_in = Profile0; + + if (CP == 0.0) { m_in = Profile0; } else + if (CP == 1.0) { m_in = Profile1; } else + if (CP == 2.0) { m_in = Profile2; } else + if (CP == 3.0) { m_in = Profile3; } else + if (CP == 4.0) { m_in = Profile4; } else + if (CP == 5.0) { m_in = Profile5; } + + color = m_in*color; + color = m_out*color; + + color = clamp(color, 0.0, 1.0); + + color = pow(color, vec3(1.0/p)); + + if (CP == -1.0) color = c; + + vec3 scolor1 = normalize(pow(color + 0.000000001, vec3(wp_saturation)))*length(color); + float luma = dot(color, vec3(0.2126, 0.7152, 0.0722)); + vec3 scolor2 = mix(vec3(luma), color, wp_saturation); + color = (wp_saturation > 1.0) ? scolor1 : scolor2; + + p = 2.2; + + color = pow(color, vec3(p)); + + color = clamp(color, 0.0, 1.0); + + vec3 warmer = D65_to_D55*color; + warmer = ToSRGB*warmer; + + vec3 cooler = D65_to_D93*color; + cooler = ToSRGB*cooler; + + float m = abs(WP)/100.0; + + vec3 comp = (WP < 0.0) ? cooler : warmer; + + color = mix(color, comp, m); + color = pow(max(color, 0.0), vec3(1.0/p)); + + color = color + aftglow.rgb; + + FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/fast/smoothing - kopija.slang b/crt/shaders/guest/fast/smoothing - kopija.slang new file mode 100644 index 0000000..6222be8 --- /dev/null +++ b/crt/shaders/guest/fast/smoothing - kopija.slang @@ -0,0 +1,84 @@ +#version 450 + +/* + Smart Smoothing Difference Shader + + Copyright (C) 2019 guest(r) - guest.r@gmail.com + + 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. + +*/ + +#pragma name SmoothPass + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float STH; +} params; + +#pragma parameter STH "Smart Smoothing Threshold" 0.7 0.4 1.2 0.05 +#define STH params.STH +#define SourceSize params.SourceSize +#define COMPAT_TEXTURE(c,d) texture(c,d) + +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; + +float df (vec3 A, vec3 B) +{ + float diff = length(A-B); + float luma = clamp(length(0.5*min(A,B) + 0.25*(A+B) + 1e-8), 0.0001, 1.0); + float diff1 = diff/luma; + return 1.0 - clamp(7.0*(max(1.5*diff,diff1)-STH), 0.0, 1.0); +} + +void main() +{ + vec2 dx = vec2(SourceSize.z, 0.0); + vec2 dy = vec2(0.0, SourceSize.w); + + vec3 l1 = COMPAT_TEXTURE(Source, vTexCoord.xy -dx).xyz; + vec3 ct = COMPAT_TEXTURE(Source, vTexCoord.xy ).xyz; + vec3 r1 = COMPAT_TEXTURE(Source, vTexCoord.xy +dx).xyz; + + float dl = df(ct, l1); + float dr = df(ct, r1); + + float resx = dl; float resy = dr; + + FragColor = vec4(resx,resy,1.0,1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/fast/smoothing.slang b/crt/shaders/guest/fast/smoothing.slang index 6222be8..3c9d8f2 100644 --- a/crt/shaders/guest/fast/smoothing.slang +++ b/crt/shaders/guest/fast/smoothing.slang @@ -60,10 +60,8 @@ layout(set = 0, binding = 2) uniform sampler2D Source; float df (vec3 A, vec3 B) { - float diff = length(A-B); - float luma = clamp(length(0.5*min(A,B) + 0.25*(A+B) + 1e-8), 0.0001, 1.0); - float diff1 = diff/luma; - return 1.0 - clamp(7.0*(max(1.5*diff,diff1)-STH), 0.0, 1.0); + float diff = abs(A-B); float m = max(max(diff.r,diff.g),diff.b); + return smoothstep(0.0, 0.99, m); } void main() diff --git a/crt/shaders/guest/linearize.slang b/crt/shaders/guest/linearize.slang index c2e49ce..ca8c99d 100644 --- a/crt/shaders/guest/linearize.slang +++ b/crt/shaders/guest/linearize.slang @@ -5,7 +5,7 @@ layout(push_constant) uniform Push float GAMMA_INPUT; } params; -#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 0.1 5.0 0.05 +#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 1.0 5.0 0.05 #define GAMMA_INPUT params.GAMMA_INPUT layout(std140, set = 0, binding = 0) uniform UBO @@ -29,10 +29,11 @@ layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D AfterglowPass; -#define PassPrev3Texture AfterglowPass + #define COMPAT_TEXTURE(c,d) texture(c,d) void main() { - FragColor = pow(vec4(COMPAT_TEXTURE(PassPrev3Texture, vTexCoord)), vec4(GAMMA_INPUT)); + float gamma_in = 1.0/GAMMA_INPUT; + FragColor = vec4(pow(vec3(COMPAT_TEXTURE(AfterglowPass, vTexCoord)), vec3(GAMMA_INPUT)), gamma_in); } \ No newline at end of file diff --git a/crt/shaders/guest/lut/custom_lut.png b/crt/shaders/guest/lut/custom_lut.png new file mode 100644 index 0000000000000000000000000000000000000000..b67e852dbfc1c1aa8dc305086ea39ccad1b22df1 GIT binary patch literal 78916 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 KMNUMnLSTaJ2a|CC literal 0 HcmV?d00001 diff --git a/crt/shaders/crt-hyllian-3d.slang b/crt/shaders/hyllian/crt-hyllian-3d.slang similarity index 100% rename from crt/shaders/crt-hyllian-3d.slang rename to crt/shaders/hyllian/crt-hyllian-3d.slang diff --git a/crt/shaders/hyllian/crt-hyllian-curvature.slang b/crt/shaders/hyllian/crt-hyllian-curvature.slang new file mode 100644 index 0000000..c5caeb3 --- /dev/null +++ b/crt/shaders/hyllian/crt-hyllian-curvature.slang @@ -0,0 +1,260 @@ +#version 450 + +layout(push_constant) uniform Push +{ + float BEAM_PROFILE; + float HFILTER_PROFILE; + float BEAM_MIN_WIDTH; + float BEAM_MAX_WIDTH; + float SCANLINES_STRENGTH; + float COLOR_BOOST; + float HFILTER_SHARPNESS; + float PHOSPHOR_LAYOUT; + float MASK_INTENSITY; + float CRT_ANTI_RINGING; + float InputGamma; + float OutputGamma; + float VSCANLINES; + float CRT_CURVATURE; + float CRT_warpX; + float CRT_warpY; + float CRT_cornersize; + float CRT_cornersmooth; +} param; + +#pragma parameter BEAM_PROFILE "BEAM PROFILE (BP)" 0.0 0.0 6.0 1.0 +#pragma parameter HFILTER_PROFILE "HORIZONTAL FILTER PROFILE (HFP)" 0.0 0.0 6.0 1.0 +#pragma parameter BEAM_MIN_WIDTH " Custom [If BP=0.00] MIN BEAM WIDTH" 0.86 0.0 1.0 0.02 +#pragma parameter BEAM_MAX_WIDTH " Custom [If BP=0.00] MAX BEAM WIDTH" 1.0 0.0 1.0 0.02 +#pragma parameter SCANLINES_STRENGTH " Custom [If BP=0.00] SCANLINES STRENGTH" 0.58 0.0 1.0 0.02 +#pragma parameter COLOR_BOOST " Custom [If BP=0.00] COLOR BOOST" 1.25 1.0 2.0 0.05 +#pragma parameter HFILTER_SHARPNESS " Custom [If HFP=0.00] SHARPNESS" 1.0 0.0 1.0 0.02 +#pragma parameter PHOSPHOR_LAYOUT "PHOSPHOR LAYOUT" 4.0 0.0 19.0 1.0 +#pragma parameter MASK_INTENSITY "MASK INTENSITY" 0.5 0.0 1.0 0.1 +#pragma parameter CRT_ANTI_RINGING "ANTI RINGING" 1.0 0.0 1.0 0.1 +#pragma parameter InputGamma "INPUT GAMMA" 2.4 0.0 5.0 0.1 +#pragma parameter OutputGamma "OUTPUT GAMMA" 2.2 0.0 5.0 0.1 +#pragma parameter VSCANLINES "SCANLINES DIRECTION" 0.0 0.0 1.0 1.0 +#pragma parameter CRT_CURVATURE "CRT-Curvature" 1.0 0.0 1.0 1.0 +#pragma parameter CRT_warpX "CRT-Curvature X-Axis" 0.031 0.0 0.125 0.01 +#pragma parameter CRT_warpY "CRT-Curvature Y-Axis" 0.041 0.0 0.125 0.01 +vec2 CRT_Distortion = vec2(param.CRT_warpX, param.CRT_warpY) * 15.; +#pragma parameter CRT_cornersize "CRT-Corner Size" 0.01 0.001 1.0 0.005 +#define cornersize param.CRT_cornersize +#pragma parameter CRT_cornersmooth "CRT-Corner Smoothness" 1000.0 80.0 2000.0 100.0 +#define cornersmooth param.CRT_cornersmooth + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; +} 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 = 1) in vec2 FragCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +/* + Hyllian's CRT Shader + + Copyright (C) 2011-2020 Hyllian - sergiogdb@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "../../../include/subpixel_masks.h" + +#define GAMMA_IN(color) pow(color, vec3(param.InputGamma, param.InputGamma, param.InputGamma)) +#define GAMMA_OUT(color) pow(color, vec3(1.0 / param.OutputGamma, 1.0 / param.OutputGamma, 1.0 / param.OutputGamma)) + + +const vec2 corner_aspect = vec2(1.0, 0.75); + +float corner(vec2 coord) +{ + coord = (coord - vec2(0.5)) + vec2(0.5, 0.5); + coord = min(coord, vec2(1.0) - coord) * corner_aspect; + vec2 cdist = vec2(cornersize); + coord = (cdist - min(coord, cdist)); + float dist = sqrt(dot(coord, coord)); + + return clamp((cdist.x - dist)*cornersmooth, 0.0, 1.0); +} + + +vec2 Warp(vec2 texCoord){ + + vec2 curvedCoords = texCoord * 2.0 - 1.0; + float curvedCoordsDistance = sqrt(curvedCoords.x*curvedCoords.x+curvedCoords.y*curvedCoords.y); + + curvedCoords = curvedCoords / curvedCoordsDistance; + + curvedCoords = curvedCoords * (1.0-pow(vec2(1.0-(curvedCoordsDistance/1.4142135623730950488016887242097)),(1.0/(1.0+CRT_Distortion*0.2)))); + + curvedCoords = curvedCoords / (1.0-pow(vec2(0.29289321881345247559915563789515),(1.0/(vec2(1.0)+CRT_Distortion*0.2)))); + + curvedCoords = curvedCoords * 0.5 + 0.5; + return curvedCoords; +} + + +// Horizontal cubic filter. +// Some known filters use these values: + +// B = 0.0, C = 0.0 => Hermite cubic filter. +// B = 1.0, C = 0.0 => Cubic B-Spline filter. +// B = 0.0, C = 0.5 => Catmull-Rom Spline filter. This is the default used in this shader. +// B = C = 1.0/3.0 => Mitchell-Netravali cubic filter. +// B = 0.3782, C = 0.3109 => Robidoux filter. +// B = 0.2620, C = 0.3690 => Robidoux Sharp filter. + +// For more info, see: http://www.imagemagick.org/Usage/img_diagrams/cubic_survey.gif +mat4x4 get_hfilter_profile() +{ + float bf = 1.0 - param.HFILTER_SHARPNESS; + float cf = param.HFILTER_SHARPNESS*0.5; // B+2C=1 Mitchel-Netravali recommendation line. + + if (param.HFILTER_PROFILE == 1) {bf = 0.0; cf = 0.0;} + if (param.HFILTER_PROFILE == 2) {bf = 0.0; cf = 0.5;} + if (param.HFILTER_PROFILE == 3) {bf = 0.2620; cf = 0.3690;} + if (param.HFILTER_PROFILE == 4) {bf = 1.0/3.0; cf = 1.0/3.0;} + if (param.HFILTER_PROFILE == 5) {bf = 0.3782; cf = 0.3109;} + if (param.HFILTER_PROFILE == 6) {bf = 1.0; cf = 0.0;} + + return mat4x4( (-bf - 6.0*cf)/6.0, (3.0*bf + 12.0*cf)/6.0, (-3.0*bf - 6.0*cf)/6.0, bf/6.0, + (12.0 - 9.0*bf - 6.0*cf)/6.0, (-18.0 + 12.0*bf + 6.0*cf)/6.0, 0.0, (6.0 - 2.0*bf)/6.0, + -(12.0 - 9.0*bf - 6.0*cf)/6.0, (18.0 - 15.0*bf - 12.0*cf)/6.0, (3.0*bf + 6.0*cf)/6.0, bf/6.0, + (bf + 6.0*cf)/6.0, -cf, 0.0, 0.0); + + +} + + +#define scanlines_strength (4.0*profile.x) +#define beam_min_width profile.y +#define beam_max_width profile.z +#define color_boost profile.w + +vec4 get_beam_profile() +{ + vec4 bp = vec4(param.SCANLINES_STRENGTH, param.BEAM_MIN_WIDTH, param.BEAM_MAX_WIDTH, param.COLOR_BOOST); + + if (param.BEAM_PROFILE == 1) bp = vec4(0.40, 1.00, 1.00, 1.00); // Catmull-rom +if (param.BEAM_PROFILE == 2) bp = vec4(0.72, 1.00, 1.00, 1.25); // Catmull-rom +if (param.BEAM_PROFILE == 3) bp = vec4(0.60, 0.50, 1.00, 1.25); // Hermite +if (param.BEAM_PROFILE == 4) bp = vec4(0.60, 0.72, 1.00, 1.25); // Hermite +if (param.BEAM_PROFILE == 5) bp = vec4(0.68, 0.68, 1.00, 1.25); // Hermite +if (param.BEAM_PROFILE == 6) bp = vec4(0.70, 0.50, 1.00, 1.80); // Catmull-rom + + return bp; +} + + +void main() +{ + vec4 profile = get_beam_profile(); + + vec2 TextureSize = vec2(global.SourceSize.x, global.SourceSize.y); + + vec2 dx = mix(vec2(1.0/TextureSize.x, 0.0), vec2(0.0, 1.0/TextureSize.y), param.VSCANLINES); + vec2 dy = mix(vec2(0.0, 1.0/TextureSize.y), vec2(1.0/TextureSize.x, 0.0), param.VSCANLINES); + + vec2 pp = vTexCoord.xy; + pp = (param.CRT_CURVATURE > 0.5) ? Warp(pp) : pp; + + + vec2 pix_coord = pp.xy*TextureSize + vec2(-0.5, 0.5); + + vec2 tc = mix((floor(pix_coord) + vec2(0.5, 0.5))/TextureSize, (floor(pix_coord) + vec2(1.0, -0.5))/TextureSize, param.VSCANLINES); + + vec2 fp = mix(fract(pix_coord), fract(pix_coord.yx), param.VSCANLINES); + + vec3 c00 = GAMMA_IN(texture(Source, tc - dx - dy).xyz); + vec3 c01 = GAMMA_IN(texture(Source, tc - dy).xyz); + vec3 c02 = GAMMA_IN(texture(Source, tc + dx - dy).xyz); + vec3 c03 = GAMMA_IN(texture(Source, tc + 2.0*dx - dy).xyz); + vec3 c10 = GAMMA_IN(texture(Source, tc - dx ).xyz); + vec3 c11 = GAMMA_IN(texture(Source, tc ).xyz); + vec3 c12 = GAMMA_IN(texture(Source, tc + dx ).xyz); + vec3 c13 = GAMMA_IN(texture(Source, tc + 2.0*dx ).xyz); + + mat4x4 invX = get_hfilter_profile(); + + mat4x3 color_matrix0 = mat4x3(c00, c01, c02, c03); + mat4x3 color_matrix1 = mat4x3(c10, c11, c12, c13); + + vec4 invX_Px = vec4(fp.x*fp.x*fp.x, fp.x*fp.x, fp.x, 1.0) * invX; + vec3 color0 = color_matrix0 * invX_Px; + vec3 color1 = color_matrix1 * invX_Px; + + // Get min/max samples + vec3 min_sample0 = min(c01,c02); + vec3 max_sample0 = max(c01,c02); + vec3 min_sample1 = min(c11,c12); + vec3 max_sample1 = max(c11,c12); + + // Anti-ringing + vec3 aux = color0; + color0 = clamp(color0, min_sample0, max_sample0); + color0 = mix(aux, color0, param.CRT_ANTI_RINGING); + aux = color1; + color1 = clamp(color1, min_sample1, max_sample1); + color1 = mix(aux, color1, param.CRT_ANTI_RINGING); + + float pos0 = fp.y; + float pos1 = 1 - fp.y; + + vec3 lum0 = mix(vec3(beam_min_width), vec3(beam_max_width), color0); + vec3 lum1 = mix(vec3(beam_min_width), vec3(beam_max_width), color1); + + vec3 d0 = scanlines_strength*pos0/(lum0+0.0000001); + vec3 d1 = scanlines_strength*pos1/(lum1+0.0000001); + + d0 = exp(-d0*d0); + d1 = exp(-d1*d1); + + vec3 color = color_boost*(color0*d0+color1*d1); + + vec2 mask_coords =vTexCoord.xy * global.OutputSize.xy; + + mask_coords = mix(mask_coords.xy, mask_coords.yx, param.VSCANLINES); + + color.rgb*=mask_weights(mask_coords, param.MASK_INTENSITY, int(param.PHOSPHOR_LAYOUT)); + + color = GAMMA_OUT(color); + + FragColor = vec4(color, 1.0); + + FragColor *= (param.CRT_CURVATURE > 0.5) ? corner(pp) : 1.0; +} diff --git a/crt/shaders/crt-hyllian-glow/crt-hyllian.slang b/crt/shaders/hyllian/crt-hyllian-glow/crt-hyllian.slang similarity index 100% rename from crt/shaders/crt-hyllian-glow/crt-hyllian.slang rename to crt/shaders/hyllian/crt-hyllian-glow/crt-hyllian.slang diff --git a/crt/shaders/crt-hyllian-glow/resolve2.slang b/crt/shaders/hyllian/crt-hyllian-glow/resolve2.slang similarity index 100% rename from crt/shaders/crt-hyllian-glow/resolve2.slang rename to crt/shaders/hyllian/crt-hyllian-glow/resolve2.slang diff --git a/crt/shaders/crt-hyllian-multipass/crt-hyllian-pass0.slang b/crt/shaders/hyllian/crt-hyllian-multipass/crt-hyllian-pass0.slang similarity index 100% rename from crt/shaders/crt-hyllian-multipass/crt-hyllian-pass0.slang rename to crt/shaders/hyllian/crt-hyllian-multipass/crt-hyllian-pass0.slang diff --git a/crt/shaders/crt-hyllian-multipass/crt-hyllian-pass1.slang b/crt/shaders/hyllian/crt-hyllian-multipass/crt-hyllian-pass1.slang similarity index 100% rename from crt/shaders/crt-hyllian-multipass/crt-hyllian-pass1.slang rename to crt/shaders/hyllian/crt-hyllian-multipass/crt-hyllian-pass1.slang diff --git a/crt/shaders/hyllian/crt-hyllian-sinc.slang b/crt/shaders/hyllian/crt-hyllian-sinc.slang new file mode 100644 index 0000000..1c55892 --- /dev/null +++ b/crt/shaders/hyllian/crt-hyllian-sinc.slang @@ -0,0 +1,197 @@ +#version 450 + +layout(push_constant) uniform Push +{ + float BEAM_PROFILE; +// float HFILTER_PROFILE; + float BEAM_MIN_WIDTH; + float BEAM_MAX_WIDTH; + float SCANLINES_STRENGTH; + float COLOR_BOOST; + float HFILTER_SHARPNESS; + float PHOSPHOR_LAYOUT; + float MASK_INTENSITY; + float CRT_ANTI_RINGING; + float InputGamma; + float OutputGamma; + float VSCANLINES; +} param; + +#pragma parameter BEAM_PROFILE "BEAM PROFILE (BP)" 0.0 0.0 3.0 1.0 +#pragma parameter BEAM_MIN_WIDTH " Custom [If BP=0.00] MIN BEAM WIDTH" 0.86 0.0 1.0 0.02 +#pragma parameter BEAM_MAX_WIDTH " Custom [If BP=0.00] MAX BEAM WIDTH" 1.0 0.0 1.0 0.02 +#pragma parameter SCANLINES_STRENGTH " Custom [If BP=0.00] SCANLINES STRENGTH" 0.72 0.0 1.0 0.02 +#pragma parameter COLOR_BOOST " Custom [If BP=0.00] COLOR BOOST" 1.25 1.0 2.0 0.05 +#pragma parameter HFILTER_SHARPNESS " Custom [If HFP=0.00] SHARPNESS" 1.0 0.0 1.0 0.02 +#pragma parameter PHOSPHOR_LAYOUT "PHOSPHOR LAYOUT" 4.0 0.0 19.0 1.0 +#pragma parameter MASK_INTENSITY "MASK INTENSITY" 0.5 0.0 1.0 0.1 +#pragma parameter CRT_ANTI_RINGING "ANTI RINGING" 1.0 0.0 1.0 0.1 +#pragma parameter InputGamma "INPUT GAMMA" 2.4 0.0 5.0 0.1 +#pragma parameter OutputGamma "OUTPUT GAMMA" 2.2 0.0 5.0 0.1 +#pragma parameter VSCANLINES "SCANLINES DIRECTION" 0.0 0.0 1.0 1.0 + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; +} 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 = 1) in vec2 FragCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +/* + Hyllian's CRT Shader + + Copyright (C) 2011-2020 Hyllian - sergiogdb@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "../../../include/subpixel_masks.h" + +#define GAMMA_IN(color) pow(color, vec3(param.InputGamma, param.InputGamma, param.InputGamma)) +#define GAMMA_OUT(color) pow(color, vec3(1.0 / param.OutputGamma, 1.0 / param.OutputGamma, 1.0 / param.OutputGamma)) + + +#define scanlines_strength (2.0*profile.x) +#define beam_min_width profile.y +#define beam_max_width profile.z +#define color_boost profile.w + +vec4 get_beam_profile() +{ + vec4 bp = vec4(param.SCANLINES_STRENGTH, param.BEAM_MIN_WIDTH, param.BEAM_MAX_WIDTH, param.COLOR_BOOST); + +if (param.BEAM_PROFILE == 1) bp = vec4(0.70, 0.58, 1.00, 1.25); +if (param.BEAM_PROFILE == 2) bp = vec4(0.70, 1.00, 1.00, 1.25); +if (param.BEAM_PROFILE == 3) bp = vec4(0.58, 1.00, 1.00, 1.25); + + return bp; +} + + +#define pi 3.1415926535897932384626433832795 +#define wa (0.5*pi) +#define wb (pi) + +vec4 resampler4(vec4 x) +{ + vec4 res; + res.x = (x.x<=0.001) ? wa*wb : sin(x.x*wa)*sin(x.x*wb)/(x.x*x.x); + res.y = (x.y<=0.001) ? wa*wb : sin(x.y*wa)*sin(x.y*wb)/(x.y*x.y); + res.z = (x.z<=0.001) ? wa*wb : sin(x.z*wa)*sin(x.z*wb)/(x.z*x.z); + res.w = (x.w<=0.001) ? wa*wb : sin(x.w*wa)*sin(x.w*wb)/(x.w*x.w); + return res; +} + +vec3 resampler3(vec3 x) +{ + vec3 res; + res.x = (x.x<=0.001) ? wa*wb : sin(x.x*wa)*sin(x.x*wb)/(x.x*x.x); + res.y = (x.y<=0.001) ? wa*wb : sin(x.y*wa)*sin(x.y*wb)/(x.y*x.y); + res.z = (x.z<=0.001) ? wa*wb : sin(x.z*wa)*sin(x.z*wb)/(x.z*x.z); + return res; +} + +void main() +{ + vec4 profile = get_beam_profile(); + + vec2 TextureSize = vec2(global.SourceSize.x, global.SourceSize.y); + + vec2 dx = mix(vec2(1.0/TextureSize.x, 0.0), vec2(0.0, 1.0/TextureSize.y), param.VSCANLINES); + vec2 dy = mix(vec2(0.0, 1.0/TextureSize.y), vec2(1.0/TextureSize.x, 0.0), param.VSCANLINES); + + vec2 pix_coord = vTexCoord*TextureSize + vec2(-0.5, 0.5); + + vec2 tc = mix((floor(pix_coord) + vec2(0.5, 0.5))/TextureSize, (floor(pix_coord) + vec2(1.0, -0.5))/TextureSize, param.VSCANLINES); + + vec2 fp = mix(fract(pix_coord), fract(pix_coord.yx), param.VSCANLINES); + + vec3 c00 = GAMMA_IN(texture(Source, tc - dx - dy).xyz); + vec3 c01 = GAMMA_IN(texture(Source, tc - dy).xyz); + vec3 c02 = GAMMA_IN(texture(Source, tc + dx - dy).xyz); + vec3 c03 = GAMMA_IN(texture(Source, tc + 2.0*dx - dy).xyz); + vec3 c10 = GAMMA_IN(texture(Source, tc - dx ).xyz); + vec3 c11 = GAMMA_IN(texture(Source, tc ).xyz); + vec3 c12 = GAMMA_IN(texture(Source, tc + dx ).xyz); + vec3 c13 = GAMMA_IN(texture(Source, tc + 2.0*dx ).xyz); + + mat4x3 color_matrix0 = mat4x3(c00, c01, c02, c03); + mat4x3 color_matrix1 = mat4x3(c10, c11, c12, c13); + + vec4 weights = resampler4(vec4(1.0+fp.x, fp.x, 1.0-fp.x, 2.0-fp.x)); + + vec3 color0 = (color_matrix0 * weights)/dot(weights, vec4(1.0)); + vec3 color1 = (color_matrix1 * weights)/dot(weights, vec4(1.0)); + + // Get min/max samples + vec3 min_sample0 = min(c01,c02); + vec3 max_sample0 = max(c01,c02); + vec3 min_sample1 = min(c11,c12); + vec3 max_sample1 = max(c11,c12); + + // Anti-ringing + vec3 aux = color0; + color0 = clamp(color0, min_sample0, max_sample0); + color0 = mix(aux, color0, param.CRT_ANTI_RINGING); + aux = color1; + color1 = clamp(color1, min_sample1, max_sample1); + color1 = mix(aux, color1, param.CRT_ANTI_RINGING); + + float pos0 = fp.y; + float pos1 = 1 - fp.y; + + vec3 lum0 = mix(vec3(beam_min_width), vec3(beam_max_width), color0); + vec3 lum1 = mix(vec3(beam_min_width), vec3(beam_max_width), color1); + + vec3 d0 = clamp(scanlines_strength*pos0/(lum0+0.0000001), 0.0, 1.0); + vec3 d1 = clamp(scanlines_strength*pos1/(lum1+0.0000001), 0.0, 1.0); + + d0 = resampler3(d0); + d1 = resampler3(d1); + + vec3 color = color_boost*(color0*d0+color1*d1)/(wa*wb); + + vec2 mask_coords = vTexCoord.xy * global.OutputSize.xy; + + mask_coords = mix(mask_coords.xy, mask_coords.yx, param.VSCANLINES); + + color.rgb*=mask_weights(mask_coords, param.MASK_INTENSITY, int(param.PHOSPHOR_LAYOUT)); + + color = GAMMA_OUT(color); + + FragColor = vec4(color, 1.0); +} diff --git a/crt/shaders/hyllian/crt-hyllian.slang b/crt/shaders/hyllian/crt-hyllian.slang new file mode 100644 index 0000000..13bfddc --- /dev/null +++ b/crt/shaders/hyllian/crt-hyllian.slang @@ -0,0 +1,214 @@ +#version 450 + +layout(push_constant) uniform Push +{ + float BEAM_PROFILE; + float HFILTER_PROFILE; + float BEAM_MIN_WIDTH; + float BEAM_MAX_WIDTH; + float SCANLINES_STRENGTH; + float COLOR_BOOST; + float HFILTER_SHARPNESS; + float PHOSPHOR_LAYOUT; + float MASK_INTENSITY; + float CRT_ANTI_RINGING; + float InputGamma; + float OutputGamma; + float VSCANLINES; +} param; + +#pragma parameter BEAM_PROFILE "BEAM PROFILE (BP)" 0.0 0.0 6.0 1.0 +#pragma parameter HFILTER_PROFILE "HORIZONTAL FILTER PROFILE (HFP)" 0.0 0.0 6.0 1.0 +#pragma parameter BEAM_MIN_WIDTH " Custom [If BP=0.00] MIN BEAM WIDTH" 0.86 0.0 1.0 0.02 +#pragma parameter BEAM_MAX_WIDTH " Custom [If BP=0.00] MAX BEAM WIDTH" 1.0 0.0 1.0 0.02 +#pragma parameter SCANLINES_STRENGTH " Custom [If BP=0.00] SCANLINES STRENGTH" 0.58 0.0 1.0 0.02 +#pragma parameter COLOR_BOOST " Custom [If BP=0.00] COLOR BOOST" 1.25 1.0 2.0 0.05 +#pragma parameter HFILTER_SHARPNESS " Custom [If HFP=0.00] SHARPNESS" 1.0 0.0 1.0 0.02 +#pragma parameter PHOSPHOR_LAYOUT "PHOSPHOR LAYOUT" 4.0 0.0 19.0 1.0 +#pragma parameter MASK_INTENSITY "MASK INTENSITY" 0.5 0.0 1.0 0.1 +#pragma parameter CRT_ANTI_RINGING "ANTI RINGING" 1.0 0.0 1.0 0.1 +#pragma parameter InputGamma "INPUT GAMMA" 2.4 0.0 5.0 0.1 +#pragma parameter OutputGamma "OUTPUT GAMMA" 2.2 0.0 5.0 0.1 +#pragma parameter VSCANLINES "SCANLINES DIRECTION" 0.0 0.0 1.0 1.0 + + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; +} 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 = 1) in vec2 FragCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +/* + Hyllian's CRT Shader + + Copyright (C) 2011-2020 Hyllian - sergiogdb@gmail.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "../../../include/subpixel_masks.h" + +#define GAMMA_IN(color) pow(color, vec3(param.InputGamma, param.InputGamma, param.InputGamma)) +#define GAMMA_OUT(color) pow(color, vec3(1.0 / param.OutputGamma, 1.0 / param.OutputGamma, 1.0 / param.OutputGamma)) + + + + +// Horizontal cubic filter. +// Some known filters use these values: + +// B = 0.0, C = 0.0 => Hermite cubic filter. +// B = 1.0, C = 0.0 => Cubic B-Spline filter. +// B = 0.0, C = 0.5 => Catmull-Rom Spline filter. This is the default used in this shader. +// B = C = 1.0/3.0 => Mitchell-Netravali cubic filter. +// B = 0.3782, C = 0.3109 => Robidoux filter. +// B = 0.2620, C = 0.3690 => Robidoux Sharp filter. + +// For more info, see: http://www.imagemagick.org/Usage/img_diagrams/cubic_survey.gif +mat4x4 get_hfilter_profile() +{ + float bf = 1.0 - param.HFILTER_SHARPNESS; + float cf = param.HFILTER_SHARPNESS*0.5; // B+2C=1 Mitchel-Netravali recommendation line. + + if (param.HFILTER_PROFILE == 1) {bf = 0.0; cf = 0.0;} + if (param.HFILTER_PROFILE == 2) {bf = 0.0; cf = 0.5;} + if (param.HFILTER_PROFILE == 3) {bf = 0.2620; cf = 0.3690;} + if (param.HFILTER_PROFILE == 4) {bf = 1.0/3.0; cf = 1.0/3.0;} + if (param.HFILTER_PROFILE == 5) {bf = 0.3782; cf = 0.3109;} + if (param.HFILTER_PROFILE == 6) {bf = 1.0; cf = 0.0;} + + return mat4x4( (-bf - 6.0*cf)/6.0, (3.0*bf + 12.0*cf)/6.0, (-3.0*bf - 6.0*cf)/6.0, bf/6.0, + (12.0 - 9.0*bf - 6.0*cf)/6.0, (-18.0 + 12.0*bf + 6.0*cf)/6.0, 0.0, (6.0 - 2.0*bf)/6.0, + -(12.0 - 9.0*bf - 6.0*cf)/6.0, (18.0 - 15.0*bf - 12.0*cf)/6.0, (3.0*bf + 6.0*cf)/6.0, bf/6.0, + (bf + 6.0*cf)/6.0, -cf, 0.0, 0.0); + + +} + + +#define scanlines_strength (4.0*profile.x) +#define beam_min_width profile.y +#define beam_max_width profile.z +#define color_boost profile.w + +vec4 get_beam_profile() +{ + vec4 bp = vec4(param.SCANLINES_STRENGTH, param.BEAM_MIN_WIDTH, param.BEAM_MAX_WIDTH, param.COLOR_BOOST); + + if (param.BEAM_PROFILE == 1) bp = vec4(0.40, 1.00, 1.00, 1.00); // Catmull-rom +if (param.BEAM_PROFILE == 2) bp = vec4(0.72, 1.00, 1.00, 1.25); // Catmull-rom +if (param.BEAM_PROFILE == 3) bp = vec4(0.60, 0.50, 1.00, 1.25); // Hermite +if (param.BEAM_PROFILE == 4) bp = vec4(0.60, 0.72, 1.00, 1.25); // Hermite +if (param.BEAM_PROFILE == 5) bp = vec4(0.68, 0.68, 1.00, 1.25); // Hermite +if (param.BEAM_PROFILE == 6) bp = vec4(0.70, 0.50, 1.00, 1.80); // Catmull-rom + + return bp; +} + + +void main() +{ + vec4 profile = get_beam_profile(); + + vec2 TextureSize = vec2(global.SourceSize.x, global.SourceSize.y); + + vec2 dx = mix(vec2(1.0/TextureSize.x, 0.0), vec2(0.0, 1.0/TextureSize.y), param.VSCANLINES); + vec2 dy = mix(vec2(0.0, 1.0/TextureSize.y), vec2(1.0/TextureSize.x, 0.0), param.VSCANLINES); + + vec2 pix_coord = vTexCoord.xy*TextureSize + vec2(-0.5, 0.5); + + vec2 tc = mix((floor(pix_coord) + vec2(0.5, 0.5))/TextureSize, (floor(pix_coord) + vec2(1.0, -0.5))/TextureSize, param.VSCANLINES); + + vec2 fp = mix(fract(pix_coord), fract(pix_coord.yx), param.VSCANLINES); + + vec3 c00 = GAMMA_IN(texture(Source, tc - dx - dy).xyz); + vec3 c01 = GAMMA_IN(texture(Source, tc - dy).xyz); + vec3 c02 = GAMMA_IN(texture(Source, tc + dx - dy).xyz); + vec3 c03 = GAMMA_IN(texture(Source, tc + 2.0*dx - dy).xyz); + vec3 c10 = GAMMA_IN(texture(Source, tc - dx ).xyz); + vec3 c11 = GAMMA_IN(texture(Source, tc ).xyz); + vec3 c12 = GAMMA_IN(texture(Source, tc + dx ).xyz); + vec3 c13 = GAMMA_IN(texture(Source, tc + 2.0*dx ).xyz); + + mat4x4 invX = get_hfilter_profile(); + + mat4x3 color_matrix0 = mat4x3(c00, c01, c02, c03); + mat4x3 color_matrix1 = mat4x3(c10, c11, c12, c13); + + vec4 invX_Px = vec4(fp.x*fp.x*fp.x, fp.x*fp.x, fp.x, 1.0) * invX; + vec3 color0 = color_matrix0 * invX_Px; + vec3 color1 = color_matrix1 * invX_Px; + + // Get min/max samples + vec3 min_sample0 = min(c01,c02); + vec3 max_sample0 = max(c01,c02); + vec3 min_sample1 = min(c11,c12); + vec3 max_sample1 = max(c11,c12); + + // Anti-ringing + vec3 aux = color0; + color0 = clamp(color0, min_sample0, max_sample0); + color0 = mix(aux, color0, param.CRT_ANTI_RINGING); + aux = color1; + color1 = clamp(color1, min_sample1, max_sample1); + color1 = mix(aux, color1, param.CRT_ANTI_RINGING); + + float pos0 = fp.y; + float pos1 = 1 - fp.y; + + vec3 lum0 = mix(vec3(beam_min_width), vec3(beam_max_width), color0); + vec3 lum1 = mix(vec3(beam_min_width), vec3(beam_max_width), color1); + + vec3 d0 = scanlines_strength*pos0/(lum0+0.0000001); + vec3 d1 = scanlines_strength*pos1/(lum1+0.0000001); + + d0 = exp(-d0*d0); + d1 = exp(-d1*d1); + + vec3 color = color_boost*(color0*d0+color1*d1); + + vec2 mask_coords =vTexCoord.xy * global.OutputSize.xy; + + mask_coords = mix(mask_coords.xy, mask_coords.yx, param.VSCANLINES); + + color.rgb*=mask_weights(mask_coords, param.MASK_INTENSITY, int(param.PHOSPHOR_LAYOUT)); + + color = GAMMA_OUT(color); + + FragColor = vec4(color, 1.0); +} diff --git a/crt/shaders/mame_hlsl/shaders/mame_parameters.inc b/crt/shaders/mame_hlsl/shaders/mame_parameters.inc index 435a3eb..18b3e65 100644 --- a/crt/shaders/mame_hlsl/shaders/mame_parameters.inc +++ b/crt/shaders/mame_hlsl/shaders/mame_parameters.inc @@ -146,6 +146,7 @@ layout(std140, set = 0, binding = 0) uniform UBO // float lengthratio; // float lengthscale; // float beamsmooth; + float TATE_toggle; } global; // Effect Toggles and Settings Used In Multiple Passes @@ -153,6 +154,8 @@ layout(std140, set = 0, binding = 0) uniform UBO bool NTSCSignal = bool(global.ntscsignal); #pragma parameter scanlinetoggle "Scanline Toggle" 1.0 0.0 1.0 1.0 bool ScanlineToggle = bool(global.scanlinetoggle); +#pragma parameter TATE_toggle "TATE Mode" 0.0 0.0 1.0 1.0 +bool TATE = bool(global.TATE_toggle); //#pragma parameter bloomtoggle "Bloom Enable" 0.0 0.0 1.0 1.0 //bool BloomToggle = bool(global.bloomtoggle); #pragma parameter chromatoggle "Chromaticity Toggle" 0.0 0.0 1.0 1.0 @@ -327,4 +330,4 @@ vec4 u_screen_offset = vec4(global.screenoffset_x, global.screenoffset_y, 0.,0.) vec4 u_screen_count = vec4(1.); vec4 u_screen_dims = vec4(params.OutputSize); vec4 u_quad_dims = params.FinalViewportSize; -vec4 u_time = vec4(params.FrameCount);//vec4(mod(params.FrameCount, global.time / 16.)); \ No newline at end of file +vec4 u_time = vec4(params.FrameCount);//vec4(mod(params.FrameCount, global.time / 16.)); diff --git a/crt/shaders/mame_hlsl/shaders/mame_post.slang b/crt/shaders/mame_hlsl/shaders/mame_post.slang index 7b31f99..ceb9779 100644 --- a/crt/shaders/mame_hlsl/shaders/mame_post.slang +++ b/crt/shaders/mame_hlsl/shaders/mame_post.slang @@ -128,6 +128,7 @@ void main() if (u_shadow_alpha.x > 0.0) { vec2 ShadowCoord = GetShadowCoord(v_texcoord0.xy, BaseCoord.xy); + ShadowCoord = (!TATE) ? ShadowCoord.xy : ShadowCoord.yx; vec4 ShadowColor = texture(s_shadow, ShadowCoord); vec3 ShadowMaskColor = mix(vec3(1.0, 1.0, 1.0), ShadowColor.rgb, u_shadow_alpha.xxx); @@ -161,4 +162,4 @@ void main() FragColor = vec4(BaseColor.rgb * v_color0.rgb, BaseColor.a); } -} \ No newline at end of file +} diff --git a/crt/shaders/mame_hlsl/shaders/mame_scanline.slang b/crt/shaders/mame_hlsl/shaders/mame_scanline.slang index 0e7fea8..1828cb1 100644 --- a/crt/shaders/mame_hlsl/shaders/mame_scanline.slang +++ b/crt/shaders/mame_hlsl/shaders/mame_scanline.slang @@ -91,7 +91,7 @@ void main() float ColorBrightness = 0.299 * BaseColor.r + 0.587 * BaseColor.g + 0.114 * BaseColor.b; - float ScanCoord = BaseCoord.y; + float ScanCoord = (!TATE) ? BaseCoord.y : BaseCoord.x; ScanCoord += (global.scanline_crawl > 0.0) ? -1.* u_time.x * params.OutputSize.w * u_jitter_amount.x : 0.0; ScanCoord += u_swap_xy.x > 0.0 ? u_quad_dims.x <= u_source_dims.x * 2.0 @@ -114,4 +114,4 @@ void main() FragColor = vec4(BaseColor.rgb * v_color0.rgb, BaseColor.a); } -} \ No newline at end of file +} diff --git a/dithering/shaders/sgenpt-mix.slang b/dithering/shaders/sgenpt-mix.slang index b7120ae..d13fc93 100644 --- a/dithering/shaders/sgenpt-mix.slang +++ b/dithering/shaders/sgenpt-mix.slang @@ -1,7 +1,7 @@ #version 450 /* - SGENPT-MIX - Sega Genesis Pseudo Transparency Mixer Shader - v4 + SGENPT-MIX - Sega Genesis Pseudo Transparency Mixer Shader - v8b 2011-2020 Hyllian - sergiogdb@gmail.com @@ -32,14 +32,29 @@ layout(push_constant) uniform Push vec4 OriginalSize; vec4 OutputSize; uint FrameCount; - float SGPT_SHARPNESS; float SGPT_BLEND_OPTION; + float SGPT_BLEND_LEVEL; + float SGPT_ADJUST_VIEW; + float SGPT_LINEAR_GAMMA; } params; -#pragma parameter SGPT_SHARPNESS "SGENPT-MIX Sharpness" 1.0 0.0 1.0 0.1 -#pragma parameter SGPT_BLEND_OPTION "OFF | Transparency | Checkerboard" 1.0 0.0 2.0 1.0 -#define SGPT_SHARPNESS params.SGPT_SHARPNESS +#pragma parameter SGPT_BLEND_OPTION "0.OFF | 1.VL | 2.CB | 3.CB-S | 4.Both | 5.Both2 | 6.Both-S" 4.0 0.0 6.0 1.0 +#pragma parameter SGPT_BLEND_LEVEL "SGENPT-MIX Both Blend Level" 1.0 0.0 1.0 0.1 +#pragma parameter SGPT_ADJUST_VIEW "SGENPT-MIX Adjust View" 0.0 0.0 1.0 1.0 +#pragma parameter SGPT_LINEAR_GAMMA "SGENPT-MIX Use Linear Gamma" 1.0 0.0 1.0 1.0 #define SGPT_BLEND_OPTION params.SGPT_BLEND_OPTION +#define SGPT_BLEND_LEVEL params.SGPT_BLEND_LEVEL +#define SGPT_ADJUST_VIEW params.SGPT_ADJUST_VIEW +#define SGPT_LINEAR_GAMMA params.SGPT_LINEAR_GAMMA + +#define GAMMA_EXP (SGPT_LINEAR_GAMMA+1.0) +#define GAMMA_IN(color) pow(color, vec3(GAMMA_EXP, GAMMA_EXP, GAMMA_EXP)) +#define GAMMA_OUT(color) pow(color, vec3(1.0 / GAMMA_EXP, 1.0 / GAMMA_EXP, 1.0 / GAMMA_EXP)) + +const vec3 Y = vec3(.2126, .7152, .0722); + +vec3 min_s(vec3 central, vec3 adj1, vec3 adj2) {return min(central, max(adj1, adj2));} +vec3 max_s(vec3 central, vec3 adj1, vec3 adj2) {return max(central, min(adj1, adj2));} layout(std140, set = 0, binding = 0) uniform UBO { @@ -69,36 +84,79 @@ void main() vec2 dy = vec2(0.0, 1.0)*params.SourceSize.zw; // Reading the texels. - vec3 C = texture(Source, vTexCoord ).xyz; - vec3 L = texture(Source, vTexCoord -dx).xyz; - vec3 R = texture(Source, vTexCoord +dx).xyz; - vec3 U = texture(Source, vTexCoord -dy).xyz; - vec3 D = texture(Source, vTexCoord +dy).xyz; + vec3 C = GAMMA_IN(texture(Source, vTexCoord ).xyz); + vec3 L = GAMMA_IN(texture(Source, vTexCoord -dx).xyz); + vec3 R = GAMMA_IN(texture(Source, vTexCoord +dx).xyz); + vec3 U = GAMMA_IN(texture(Source, vTexCoord -dy).xyz); + vec3 D = GAMMA_IN(texture(Source, vTexCoord +dy).xyz); + vec3 UL = GAMMA_IN(texture(Source, vTexCoord -dx -dy).xyz); + vec3 UR = GAMMA_IN(texture(Source, vTexCoord +dx -dy).xyz); + vec3 DL = GAMMA_IN(texture(Source, vTexCoord -dx +dy).xyz); + vec3 DR = GAMMA_IN(texture(Source, vTexCoord +dx +dy).xyz); vec3 color = C; - if (SGPT_BLEND_OPTION > 0.0) + // Get min/max samples + vec3 min_sample = min_s(C, L, R); + vec3 max_sample = max_s(C, L, R); + + float diff = dot(max(max(C, L), max(C, R)) - min(min(C, L), min(C, R)), Y); + + if (SGPT_BLEND_OPTION == 1) // Only Vertical Lines { - // Get min/max samples - vec3 min_sample = min(C, max(L, R)); - vec3 max_sample = max(C, min(L, R)); + min_sample = max_s(min_sample, min_s(C, DL, DR), min_s(C, UL, UR)); + max_sample = min_s(max_sample, max_s(C, DL, DR), max_s(C, UL, UR)); - color = 0.5*C + 0.25*(L + R); + diff *= (1.0 - SGPT_BLEND_LEVEL); - if (SGPT_BLEND_OPTION > 1.0) - { - // Get min/max samples - min_sample = max(min_sample, min(C, max(U, D))); - max_sample = min(max_sample, max(C, min(U, D))); + color = 0.5*( 1.0 + diff )*C + 0.25*( 1.0 - diff )*(L + R); + } + else if (SGPT_BLEND_OPTION == 2) // Only Checkerboard + { + min_sample = max(min_sample, min_s(C, U, D)); + max_sample = min(max_sample, max_s(C, U, D)); - color = 0.5*C + 0.125*(L + R + U + D); - } + diff *= (1.0 - SGPT_BLEND_LEVEL); - // Sharpness control - vec3 aux = color; - color = clamp(color, min_sample, max_sample); - color = mix(aux, color, SGPT_SHARPNESS); + color = 0.5*( 1.0 + diff )*C + 0.125*( 1.0 - diff )*(L + R + U + D); + } + else if (SGPT_BLEND_OPTION == 3) // Only Checkerboard - Soft + { + min_sample = min_s(min_sample, U, D); + max_sample = max_s(max_sample, U, D); + + diff *= (1.0 - SGPT_BLEND_LEVEL); + + color = 0.5*( 1.0 + diff )*C + 0.125*( 1.0 - diff )*(L + R + U + D); + } + else if (SGPT_BLEND_OPTION == 4) // VL-CB + { + diff *= (1.0 - SGPT_BLEND_LEVEL); + + color = 0.5*( 1.0 + diff )*C + 0.25*( 1.0 - diff )*(L + R); + } + else if (SGPT_BLEND_OPTION == 5) // VL-CB-2 + { + min_sample = min_s(min_sample, U, D); + max_sample = max_s(max_sample, U, D); + + diff *= (1.0 - SGPT_BLEND_LEVEL); + + color = 0.5*( 1.0 + diff )*C + 0.25*( 1.0 - diff )*(L + R); + } + else if (SGPT_BLEND_OPTION == 6) // VL-CB-Soft + { + min_sample = min(min_sample, min(min_s(D, DL, DR), min_s(U, UL, UR))); + max_sample = max(max_sample, max(max_s(D, DL, DR), max_s(U, UL, UR))); + + diff *= (1.0 - SGPT_BLEND_LEVEL); + + color = 0.5*( 1.0 + diff )*C + 0.25*( 1.0 - diff )*(L + R); } - FragColor = vec4(color, 1.0); + color = clamp(color, min_sample, max_sample); + + color = mix(color, vec3(dot(abs(C-color), vec3(1.0, 1.0, 1.0))), SGPT_ADJUST_VIEW); + + FragColor = vec4(GAMMA_OUT(color), 1.0); } diff --git a/presets/crt-guest-dr-venom2-hires.slangp b/presets/crt-guest-dr-venom2-hires.slangp new file mode 100644 index 0000000..fcdc08b --- /dev/null +++ b/presets/crt-guest-dr-venom2-hires.slangp @@ -0,0 +1,64 @@ +shaders = 8 + +shader0 = ../stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 +alias0 = StockPass + +shader1 = ../crt/shaders/guest/crt-gdv-new/afterglow0.slang +filter_linear1 = false +scale_type1 = source +scale1 = 1.0 +alias1 = AfterglowPass + +shader2 = ../crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang +filter_linear2 = false +scale_type2 = source +scale2 = 1.0 +alias2 = PrePass + +textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4" +SamplerLUT1 = ../crt/shaders/guest/lut/sony_trinitron1.png +SamplerLUT1_linear = true +SamplerLUT2 = ../crt/shaders/guest/lut/sony_trinitron2.png +SamplerLUT2_linear = true +SamplerLUT3 = ../crt/shaders/guest/lut/other1.png +SamplerLUT3_linear = true +SamplerLUT4 = ../crt/shaders/guest/lut/custom_lut.png +SamplerLUT4_linear = true + +shader3 = ../crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang +filter_linear3 = true +scale_type3 = source +scale3 = 1.0 +mipmap_input3 = true +alias3 = AvgLumPass + +shader4 = ../crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang +filter_linear4 = true +scale_type4 = source +scale4 = 1.0 +alias4 = LinearizePass +float_framebuffer4 = true # comment this line for max precision + +shader5 = ../crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang +filter_linear5 = true +scale_type_x5 = viewport +scale_x5 = 0.5 +scale_type_y5 = source +scale_y5 = 1.0 + +shader6 = ../crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang +filter_linear6 = true +scale_type_x6 = viewport +scale_x6 = 0.5 +scale_type_y6 = viewport +scale_y6 = 0.5 +alias6 = GlowPass + +shader7 = ../crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang +filter_linear7 = true +scale_type7 = viewport +scale_x7 = 1.0 +scale_y7 = 1.0 diff --git a/presets/crt-guest-dr-venom2-ntsc.slangp b/presets/crt-guest-dr-venom2-ntsc.slangp new file mode 100644 index 0000000..da20a9a --- /dev/null +++ b/presets/crt-guest-dr-venom2-ntsc.slangp @@ -0,0 +1,81 @@ +shaders = 10 + +shader0 = ../stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 +alias0 = StockPass + +shader1 = ../crt/shaders/guest/crt-gdv-new/afterglow0.slang +filter_linear1 = false +scale_type1 = source +scale1 = 1.0 +alias1 = AfterglowPass + +shader2 = ../crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang +filter_linear2 = false +scale_type2 = source +scale2 = 1.0 + +textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4" +SamplerLUT1 = ../crt/shaders/guest/lut/sony_trinitron1.png +SamplerLUT1_linear = true +SamplerLUT2 = ../crt/shaders/guest/lut/sony_trinitron2.png +SamplerLUT2_linear = true +SamplerLUT3 = ../crt/shaders/guest/lut/other1.png +SamplerLUT3_linear = true +SamplerLUT4 = ../crt/shaders/guest/lut/custom_lut.png +SamplerLUT4_linear = true + +shader3 = ../ntsc/shaders/ntsc-adaptive/ntsc-pass1.slang +shader4 = ../ntsc/shaders/ntsc-adaptive/ntsc-pass2.slang + +filter_linear3 = false +filter_linear4 = false + +scale_type_x3 = source +scale_type_y3 = source +scale_x3 = 4.0 +scale_y3 = 1.0 +frame_count_mod3 = 2 +float_framebuffer3 = true + +scale_type4 = source +scale_x4 = 0.5 +scale_y4 = 1.0 +alias4 = PrePass + +shader5 = ../crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang +filter_linear5 = true +scale_type5 = source +scale5 = 1.0 +mipmap_input5 = true +alias5 = AvgLumPass + +shader6 = ../crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang +filter_linear6 = true +scale_type6 = source +scale6 = 1.0 +alias6 = LinearizePass +float_framebuffer6 = true # comment this line for max precision + +shader7 = ../crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang +filter_linear7 = true +scale_type_x7 = viewport +scale_x7 = 0.5 +scale_type_y7 = source +scale_y7 = 1.0 + +shader8 = ../crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang +filter_linear8 = true +scale_type_x8 = viewport +scale_x8 = 0.5 +scale_type_y8 = viewport +scale_y8 = 0.5 +alias8 = GlowPass + +shader9 = ../crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang +filter_linear9 = true +scale_type9 = viewport +scale_x9 = 1.0 +scale_y9 = 1.0 diff --git a/presets/crt-hyllian-curvature-ntsc.slangp b/presets/crt-hyllian-curvature-ntsc.slangp new file mode 100644 index 0000000..dc93893 --- /dev/null +++ b/presets/crt-hyllian-curvature-ntsc.slangp @@ -0,0 +1,100 @@ +shaders = "7" +shader0 = "../ntsc/shaders/ntsc-adaptive/ntsc-pass1.slang" +filter_linear0 = "false" +wrap_mode0 = "clamp_to_border" +frame_count_mod0 = "2" +mipmap_input0 = "false" +alias0 = "" +float_framebuffer0 = "true" +srgb_framebuffer0 = "false" +scale_type_x0 = "source" +scale_x0 = "4.000000" +scale_type_y0 = "source" +scale_y0 = "1.000000" +shader1 = "../ntsc/shaders/ntsc-adaptive/ntsc-pass2.slang" +filter_linear1 = "false" +wrap_mode1 = "clamp_to_border" +mipmap_input1 = "false" +alias1 = "" +float_framebuffer1 = "false" +srgb_framebuffer1 = "true" +scale_type_x1 = "source" +scale_x1 = "0.500000" +scale_type_y1 = "source" +scale_y1 = "1.000000" +shader2 = "../crt/shaders/hyllian/crt-hyllian-curvature.slang" +filter_linear2 = "false" +wrap_mode2 = "clamp_to_border" +mipmap_input2 = "false" +alias2 = "CRTPass" +float_framebuffer2 = "false" +srgb_framebuffer2 = "true" +scale_type_x2 = "viewport" +scale_x2 = "1.000000" +scale_type_y2 = "viewport" +scale_y2 = "1.000000" +shader3 = "../crt/shaders/glow/threshold.slang" +filter_linear3 = "false" +wrap_mode3 = "clamp_to_border" +mipmap_input3 = "false" +alias3 = "" +float_framebuffer3 = "false" +srgb_framebuffer3 = "true" +shader4 = "../crt/shaders/glow/blur_horiz.slang" +filter_linear4 = "true" +wrap_mode4 = "clamp_to_border" +mipmap_input4 = "true" +alias4 = "" +float_framebuffer4 = "false" +srgb_framebuffer4 = "true" +scale_type_x4 = "viewport" +scale_x4 = "0.200000" +scale_type_y4 = "viewport" +scale_y4 = "0.200000" +shader5 = "../crt/shaders/glow/blur_vert.slang" +filter_linear5 = "true" +wrap_mode5 = "clamp_to_border" +mipmap_input5 = "false" +alias5 = "" +float_framebuffer5 = "false" +srgb_framebuffer5 = "true" +shader6 = "../crt/shaders/glow/resolve.slang" +filter_linear6 = "true" +wrap_mode6 = "clamp_to_border" +mipmap_input6 = "false" +alias6 = "" +float_framebuffer6 = "false" +srgb_framebuffer6 = "false" +parameters = "linearize;BEAM_PROFILE;HFILTER_PROFILE;BEAM_MIN_WIDTH;BEAM_MAX_WIDTH;SCANLINES_STRENGTH;COLOR_BOOST;HFILTER_SHARPNESS;PHOSPHOR_LAYOUT;MASK_INTENSITY;CRT_ANTI_RINGING;InputGamma;OutputGamma;VSCANLINES;CRT_CURVATURE;CRT_warpX;CRT_warpY;CRT_cornersize;CRT_cornersmooth;GLOW_WHITEPOINT;GLOW_ROLLOFF;BLOOM_STRENGTH;OUTPUT_GAMMA;CURVATURE;warpX;warpY;cornersize;cornersmooth;noise_amt;shadowMask;maskDark;maskLight" +BEAM_PROFILE = "0.000000" +HFILTER_PROFILE = "0.000000" +BEAM_MIN_WIDTH = "0.860000" +BEAM_MAX_WIDTH = "1.000000" +SCANLINES_STRENGTH = "0.580000" +COLOR_BOOST = "1.250000" +HFILTER_SHARPNESS = "1.000000" +PHOSPHOR_LAYOUT = "4.000000" +MASK_INTENSITY = "0.500000" +CRT_ANTI_RINGING = "1.000000" +InputGamma = "1.000000" +OutputGamma = "1.000000" +VSCANLINES = "0.000000" +CRT_CURVATURE = "1.000000" +CRT_warpX = "0.031000" +CRT_warpY = "0.041000" +CRT_cornersize = "0.010000" +CRT_cornersmooth = "1000.000000" +GLOW_WHITEPOINT = "0.500000" +GLOW_ROLLOFF = "1.200000" +BLOOM_STRENGTH = "0.100000" +OUTPUT_GAMMA = "2.200000" +CURVATURE = "0.000000" +warpX = "0.031000" +warpY = "0.041000" +cornersize = "0.010000" +cornersmooth = "1000.000000" +noise_amt = "1.000000" +shadowMask = "0.000000" +maskDark = "0.500000" +maskLight = "1.500000" +linearize = "1.0" diff --git a/presets/crt-hyllian-sinc-smartblur-sgenpt.slangp b/presets/crt-hyllian-sinc-smartblur-sgenpt.slangp new file mode 100644 index 0000000..b0c34c4 --- /dev/null +++ b/presets/crt-hyllian-sinc-smartblur-sgenpt.slangp @@ -0,0 +1,63 @@ +shaders = "4" +shader0 = "../dithering/shaders/sgenpt-mix.slang" +filter_linear0 = "false" +wrap_mode0 = "clamp_to_border" +mipmap_input0 = "false" +alias0 = "" +float_framebuffer0 = "false" +srgb_framebuffer0 = "false" +scale_type_x0 = "source" +scale_x0 = "2.000000" +scale_type_y0 = "source" +scale_y0 = "2.000000" +shader1 = "../blurs/smart-blur.slang" +filter_linear1 = "false" +wrap_mode1 = "clamp_to_border" +mipmap_input1 = "false" +alias1 = "" +float_framebuffer1 = "false" +srgb_framebuffer1 = "false" +scale_type_x1 = "source" +scale_x1 = "1.000000" +scale_type_y1 = "source" +scale_y1 = "1.000000" +shader2 = "../stock.slang" +filter_linear2 = "false" +wrap_mode2 = "clamp_to_border" +mipmap_input2 = "false" +alias2 = "" +float_framebuffer2 = "false" +srgb_framebuffer2 = "false" +scale_type_x2 = "source" +scale_x2 = "0.500000" +scale_type_y2 = "source" +scale_y2 = "0.500000" +shader3 = "../crt/shaders/hyllian/crt-hyllian-sinc.slang" +filter_linear3 = "false" +wrap_mode3 = "clamp_to_border" +mipmap_input3 = "false" +alias3 = "" +float_framebuffer3 = "false" +srgb_framebuffer3 = "false" +parameters = "SGPT_BLEND_OPTION;SGPT_BLEND_LEVEL;SGPT_ADJUST_VIEW;SGPT_LINEAR_GAMMA;SB_BLUR_LEVEL;SB_RED_THRESHOLD;SB_GREEN_THRESHOLD;SB_BLUE_THRESHOLD;BEAM_PROFILE;HFILTER_PROFILE;BEAM_MIN_WIDTH;BEAM_MAX_WIDTH;SCANLINES_STRENGTH;COLOR_BOOST;HFILTER_SHARPNESS;PHOSPHOR_LAYOUT;MASK_INTENSITY;CRT_ANTI_RINGING;InputGamma;OutputGamma;VSCANLINES" +SGPT_BLEND_OPTION = "2.000000" +SGPT_BLEND_LEVEL = "0.200000" +SGPT_ADJUST_VIEW = "0.000000" +SGPT_LINEAR_GAMMA = "1.000000" +SB_BLUR_LEVEL = "0.660000" +SB_RED_THRESHOLD = "0.200000" +SB_GREEN_THRESHOLD = "0.200000" +SB_BLUE_THRESHOLD = "0.200000" +BEAM_PROFILE = "0.000000" +HFILTER_PROFILE = "0.000000" +BEAM_MIN_WIDTH = "0.860000" +BEAM_MAX_WIDTH = "1.000000" +SCANLINES_STRENGTH = "0.720000" +COLOR_BOOST = "1.250000" +HFILTER_SHARPNESS = "1.000000" +PHOSPHOR_LAYOUT = "4.000000" +MASK_INTENSITY = "0.500000" +CRT_ANTI_RINGING = "1.000000" +InputGamma = "2.400000" +OutputGamma = "2.200000" +VSCANLINES = "0.000000" diff --git a/presets/crt-hyllian-smartblur-sgenpt.slangp b/presets/crt-hyllian-smartblur-sgenpt.slangp new file mode 100644 index 0000000..7131880 --- /dev/null +++ b/presets/crt-hyllian-smartblur-sgenpt.slangp @@ -0,0 +1,63 @@ +shaders = "4" +shader0 = "../dithering/shaders/sgenpt-mix.slang" +filter_linear0 = "false" +wrap_mode0 = "clamp_to_border" +mipmap_input0 = "false" +alias0 = "" +float_framebuffer0 = "false" +srgb_framebuffer0 = "false" +scale_type_x0 = "source" +scale_x0 = "2.000000" +scale_type_y0 = "source" +scale_y0 = "2.000000" +shader1 = "../blurs/smart-blur.slang" +filter_linear1 = "false" +wrap_mode1 = "clamp_to_border" +mipmap_input1 = "false" +alias1 = "" +float_framebuffer1 = "false" +srgb_framebuffer1 = "false" +scale_type_x1 = "source" +scale_x1 = "1.000000" +scale_type_y1 = "source" +scale_y1 = "1.000000" +shader2 = "../stock.slang" +filter_linear2 = "false" +wrap_mode2 = "clamp_to_border" +mipmap_input2 = "false" +alias2 = "" +float_framebuffer2 = "false" +srgb_framebuffer2 = "false" +scale_type_x2 = "source" +scale_x2 = "0.500000" +scale_type_y2 = "source" +scale_y2 = "0.500000" +shader3 = "../crt/shaders/hyllian/crt-hyllian.slang" +filter_linear3 = "false" +wrap_mode3 = "clamp_to_border" +mipmap_input3 = "false" +alias3 = "" +float_framebuffer3 = "false" +srgb_framebuffer3 = "false" +parameters = "SGPT_BLEND_OPTION;SGPT_BLEND_LEVEL;SGPT_ADJUST_VIEW;SGPT_LINEAR_GAMMA;SB_BLUR_LEVEL;SB_RED_THRESHOLD;SB_GREEN_THRESHOLD;SB_BLUE_THRESHOLD;BEAM_PROFILE;HFILTER_PROFILE;BEAM_MIN_WIDTH;BEAM_MAX_WIDTH;SCANLINES_STRENGTH;COLOR_BOOST;HFILTER_SHARPNESS;PHOSPHOR_LAYOUT;MASK_INTENSITY;CRT_ANTI_RINGING;InputGamma;OutputGamma;VSCANLINES" +SGPT_BLEND_OPTION = "2.000000" +SGPT_BLEND_LEVEL = "0.200000" +SGPT_ADJUST_VIEW = "0.000000" +SGPT_LINEAR_GAMMA = "1.000000" +SB_BLUR_LEVEL = "0.660000" +SB_RED_THRESHOLD = "0.200000" +SB_GREEN_THRESHOLD = "0.200000" +SB_BLUE_THRESHOLD = "0.200000" +BEAM_PROFILE = "0.000000" +HFILTER_PROFILE = "0.000000" +BEAM_MIN_WIDTH = "0.860000" +BEAM_MAX_WIDTH = "1.000000" +SCANLINES_STRENGTH = "0.580000" +COLOR_BOOST = "1.250000" +HFILTER_SHARPNESS = "1.000000" +PHOSPHOR_LAYOUT = "4.000000" +MASK_INTENSITY = "0.500000" +CRT_ANTI_RINGING = "1.000000" +InputGamma = "2.400000" +OutputGamma = "2.200000" +VSCANLINES = "0.000000"