From 2fa315660650c9e10be0e6867a533e459034cd17 Mon Sep 17 00:00:00 2001 From: hunterk Date: Thu, 28 Jul 2022 19:48:28 -0500 Subject: [PATCH] big update to crt-guest shaders --- crt/crt-guest-advanced-fast.slangp | 32 +- ...es.slangp => crt-guest-advanced-hd.slangp} | 81 +- crt/crt-guest-advanced-ntsc.slangp | 147 +++ crt/crt-guest-advanced.slangp | 5 +- crt/crt-guest-dr-venom-fast.slangp | 40 - crt/crt-guest-dr-venom.slangp | 71 -- crt/crt-guest-sm.slangp | 46 - .../guest/{README => advanced/README_old} | 0 crt/shaders/guest/advanced/afterglow0.slang | 10 +- .../guest/advanced/avg-lum-hires.slang | 87 -- crt/shaders/guest/advanced/avg-lum-ntsc.slang | 12 +- crt/shaders/guest/advanced/avg-lum.slang | 12 +- .../guest/advanced/bloom_horizontal.slang | 4 +- .../advanced/bloom_horizontal_ntsc.slang | 101 -- .../guest/advanced/bloom_vertical.slang | 4 +- .../guest/advanced/bloom_vertical_ntsc.slang | 101 -- crt/shaders/guest/advanced/convert-ntsc.slang | 69 -- .../advanced/crt-guest-advanced-hires.slang | 775 --------------- .../crt-guest-advanced-ntsc-pass1.slang | 165 ++++ .../crt-guest-advanced-ntsc-pass2.slang | 399 ++++++++ .../advanced/crt-guest-advanced-ntsc.slang | 904 ----------------- .../guest/advanced/crt-guest-advanced.slang | 502 +++------- .../guest/advanced/custom-fast-sharpen.slang | 20 +- .../guest/advanced/deconvergence-ntsc.slang | 697 +++++++++++++ .../guest/advanced/deconvergence.slang | 674 +++++++++++-- .../grade/pre-shaders-afterglow-grade.slang | 929 ++++++++++++++++++ .../guest/advanced/linearize-ntsc.slang | 35 +- crt/shaders/guest/advanced/linearize.slang | 11 +- .../guest/advanced/lut/inv-trinitron-lut.png | Bin 12268 -> 15627 bytes crt/shaders/guest/advanced/lut/nec-lut.png | Bin 13681 -> 15243 bytes crt/shaders/guest/advanced/lut/ntsc-lut.png | Bin 20394 -> 22140 bytes .../guest/advanced/lut/trinitron-lut.png | Bin 20781 -> 14866 bytes .../guest/advanced/ntsc/ntsc-pass1.slang | 153 +++ .../guest/advanced/ntsc/ntsc-pass2.slang | 299 ++++++ .../guest/advanced/ntsc/ntsc-pass3.slang | 122 +++ .../advanced/pre-shaders-afterglow.slang | 66 +- .../stock.slang} | 9 +- crt/shaders/guest/afterglow.slang | 105 -- crt/shaders/guest/avg-lum.slang | 88 -- crt/shaders/guest/blur_horiz.slang | 59 -- crt/shaders/guest/blur_vert.slang | 60 -- crt/shaders/guest/color-profiles.slang | 160 --- .../guest/crt-gdv-new/avg-lum-ntsc.slang | 88 -- crt/shaders/guest/crt-gdv-new/avg-lum.slang | 112 --- .../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/shaders/guest/crt-guest-dr-venom.slang | 615 ------------ crt/shaders/guest/crt-sm/blur_horiz-sm.slang | 66 -- crt/shaders/guest/crt-sm/blur_vert-sm.slang | 66 -- .../guest/crt-sm/crt-guest-sm-rot0.slang | 59 -- crt/shaders/guest/crt-sm/crt-guest-sm.slang | 479 --------- crt/shaders/guest/crt-sm/d65-d50-sm.slang | 97 -- crt/shaders/guest/crt-sm/linearize-sm.slang | 41 - crt/shaders/guest/d65-d50.slang | 88 -- crt/shaders/guest/fast/bloom_horizontal.slang | 6 +- crt/shaders/guest/fast/bloom_vertical.slang | 3 +- .../guest/fast/crt-guest-advanced-pass1.slang | 20 +- .../fast/crt-guest-advanced-pass1f.slang | 32 +- .../guest/fast/crt-guest-advanced-pass2.slang | 352 ++----- .../fast/crt-guest-advanced-pass2f.slang | 190 +++- .../guest/fast/crt-guest-dr-venom-pass1.slang | 126 --- .../guest/fast/crt-guest-dr-venom-pass2.slang | 328 ------- crt/shaders/guest/fast/deconvergence-f.slang | 640 ++++++++++-- crt/shaders/guest/fast/linearize.slang | 11 +- crt/shaders/guest/fast/linearizef.slang | 7 +- crt/shaders/guest/fast/pre-shaders.slang | 70 +- crt/shaders/guest/fast/pre-shadersf.slang | 71 +- crt/shaders/guest/fast/smoothing.slang | 84 -- ...{linearize-multipass.slang => stock.slang} | 8 +- .../{crt-gdv-new => hd}/afterglow0.slang | 15 +- .../bloom_horizontal.slang | 22 +- .../{crt-gdv-new => hd}/bloom_vertical.slang | 19 +- .../hd/crt-guest-advanced-hd-pass1.slang | 193 ++++ .../hd/crt-guest-advanced-hd-pass2.slang | 461 +++++++++ crt/shaders/guest/hd/deconvergence-hd.slang | 682 +++++++++++++ .../linearize-hd.slang} | 62 +- .../pre-shaders-afterglow.slang | 130 ++- .../guest/{linearize.slang => hd/stock.slang} | 16 +- crt/shaders/guest/lut/README | 1 - crt/shaders/guest/lut/custom_lut.png | Bin 14233 -> 0 bytes crt/shaders/guest/lut/lut.slang | 112 --- crt/shaders/guest/lut/other1.png | Bin 11494 -> 0 bytes crt/shaders/guest/lut/sony_trinitron1.png | Bin 10273 -> 0 bytes crt/shaders/guest/lut/sony_trinitron2.png | Bin 17309 -> 0 bytes presets/crt-guest-advanced-ntsc.slangp | 134 --- ...rt-lottes-multipass-interlaced-glow.slangp | 0 92 files changed, 6156 insertions(+), 9238 deletions(-) rename crt/{crt-guest-advanced-hires.slangp => crt-guest-advanced-hd.slangp} (61%) create mode 100644 crt/crt-guest-advanced-ntsc.slangp delete mode 100644 crt/crt-guest-dr-venom-fast.slangp delete mode 100644 crt/crt-guest-dr-venom.slangp delete mode 100644 crt/crt-guest-sm.slangp rename crt/shaders/guest/{README => advanced/README_old} (100%) delete mode 100644 crt/shaders/guest/advanced/avg-lum-hires.slang delete mode 100644 crt/shaders/guest/advanced/bloom_horizontal_ntsc.slang delete mode 100644 crt/shaders/guest/advanced/bloom_vertical_ntsc.slang delete mode 100644 crt/shaders/guest/advanced/convert-ntsc.slang delete mode 100644 crt/shaders/guest/advanced/crt-guest-advanced-hires.slang create mode 100644 crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang create mode 100644 crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang delete mode 100644 crt/shaders/guest/advanced/crt-guest-advanced-ntsc.slang create mode 100644 crt/shaders/guest/advanced/deconvergence-ntsc.slang create mode 100644 crt/shaders/guest/advanced/grade/pre-shaders-afterglow-grade.slang create mode 100644 crt/shaders/guest/advanced/ntsc/ntsc-pass1.slang create mode 100644 crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang create mode 100644 crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang rename crt/shaders/guest/{linearize_scanlines.slang => advanced/stock.slang} (69%) delete mode 100644 crt/shaders/guest/afterglow.slang delete mode 100644 crt/shaders/guest/avg-lum.slang delete mode 100644 crt/shaders/guest/blur_horiz.slang delete mode 100644 crt/shaders/guest/blur_vert.slang delete mode 100644 crt/shaders/guest/color-profiles.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/avg-lum.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/deconvergence.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang delete mode 100644 crt/shaders/guest/crt-gdv-new/linearize.slang delete mode 100644 crt/shaders/guest/crt-guest-dr-venom.slang delete mode 100644 crt/shaders/guest/crt-sm/blur_horiz-sm.slang delete mode 100644 crt/shaders/guest/crt-sm/blur_vert-sm.slang delete mode 100644 crt/shaders/guest/crt-sm/crt-guest-sm-rot0.slang delete mode 100644 crt/shaders/guest/crt-sm/crt-guest-sm.slang delete mode 100644 crt/shaders/guest/crt-sm/d65-d50-sm.slang delete mode 100644 crt/shaders/guest/crt-sm/linearize-sm.slang delete mode 100644 crt/shaders/guest/d65-d50.slang delete mode 100644 crt/shaders/guest/fast/crt-guest-dr-venom-pass1.slang delete mode 100644 crt/shaders/guest/fast/crt-guest-dr-venom-pass2.slang delete mode 100644 crt/shaders/guest/fast/smoothing.slang rename crt/shaders/guest/fast/{linearize-multipass.slang => stock.slang} (68%) rename crt/shaders/guest/{crt-gdv-new => hd}/afterglow0.slang (72%) rename crt/shaders/guest/{crt-gdv-new => hd}/bloom_horizontal.slang (78%) rename crt/shaders/guest/{crt-gdv-new => hd}/bloom_vertical.slang (81%) create mode 100644 crt/shaders/guest/hd/crt-guest-advanced-hd-pass1.slang create mode 100644 crt/shaders/guest/hd/crt-guest-advanced-hd-pass2.slang create mode 100644 crt/shaders/guest/hd/deconvergence-hd.slang rename crt/shaders/guest/{advanced/linearize-hires.slang => hd/linearize-hd.slang} (64%) rename crt/shaders/guest/{crt-gdv-new => hd}/pre-shaders-afterglow.slang (68%) rename crt/shaders/guest/{linearize.slang => hd/stock.slang} (56%) delete mode 100644 crt/shaders/guest/lut/README delete mode 100644 crt/shaders/guest/lut/custom_lut.png delete mode 100644 crt/shaders/guest/lut/lut.slang delete mode 100644 crt/shaders/guest/lut/other1.png delete mode 100644 crt/shaders/guest/lut/sony_trinitron1.png delete mode 100644 crt/shaders/guest/lut/sony_trinitron2.png delete mode 100644 presets/crt-guest-advanced-ntsc.slangp rename {crt => presets}/crt-lottes-multipass-interlaced-glow.slangp (100%) diff --git a/crt/crt-guest-advanced-fast.slangp b/crt/crt-guest-advanced-fast.slangp index f705d6c..c38c793 100644 --- a/crt/crt-guest-advanced-fast.slangp +++ b/crt/crt-guest-advanced-fast.slangp @@ -10,12 +10,12 @@ SamplerLUT3_linear = true SamplerLUT4 = shaders/guest/advanced/lut/ntsc-lut.png SamplerLUT4_linear = true -shader0 = ../stock.slang +shader0 = shaders/guest/fast/stock.slang filter_linear0 = false scale_type0 = source scale0 = 1.0 -shader1 = ../stock.slang +shader1 = shaders/guest/fast/stock.slang filter_linear1 = false scale_type1 = source scale1 = 1.0 @@ -34,33 +34,35 @@ scale3 = 1.0 float_framebuffer3 = true alias3 = LinearizePass -shader4 = shaders/guest/fast/bloom_horizontal.slang +shader4 = shaders/guest/fast/crt-guest-advanced-pass1.slang filter_linear4 = true -scale_type_x4 = absolute -scale_x4 = 800.0 +scale_type_x4 = viewport +scale_x4 = 1.0 scale_type_y4 = source -scale_y4 = 1.0 +scale_y4 = 1.0 float_framebuffer4 = true +alias4 = Pass1 -shader5 = shaders/guest/fast/bloom_vertical.slang +shader5 = shaders/guest/fast/bloom_horizontal.slang filter_linear5 = true -scale_type_x5 = source -scale_x5 = 1.0 +scale_type_x5 = absolute +scale_x5 = 640.0 scale_type_y5 = source -scale_y5 = 2.0 +scale_y5 = 1.0 float_framebuffer5 = true -alias5 = BloomPass -shader6 = shaders/guest/fast/crt-guest-advanced-pass1.slang +shader6 = shaders/guest/fast/bloom_vertical.slang filter_linear6 = true -scale_type_x6 = viewport +scale_type_x6 = source scale_x6 = 1.0 -scale_type_y6 = source -scale_y6 = 0.5 +scale_type_y6 = absolute +scale_y6 = 480.0 float_framebuffer6 = true +alias6 = BloomPass shader7 = shaders/guest/fast/crt-guest-advanced-pass2.slang filter_linear7 = true +float_framebuffer7 = true scale_type7 = viewport scale_x7 = 1.0 scale_y7 = 1.0 diff --git a/crt/crt-guest-advanced-hires.slangp b/crt/crt-guest-advanced-hd.slangp similarity index 61% rename from crt/crt-guest-advanced-hires.slangp rename to crt/crt-guest-advanced-hd.slangp index d92ee91..13812ca 100644 --- a/crt/crt-guest-advanced-hires.slangp +++ b/crt/crt-guest-advanced-hd.slangp @@ -1,29 +1,5 @@ shaders = 10 -shader0 = ../stock.slang -filter_linear0 = false -scale_type0 = source -scale0 = 1.0 - -shader1 = ../stock.slang -filter_linear1 = false -scale_type1 = source -scale1 = 1.0 -alias1 = StockPass - -shader2 = shaders/guest/advanced/afterglow0.slang -filter_linear2 = false -scale_type2 = source -scale2 = 1.0 -alias2 = AfterglowPass - -shader3 = shaders/guest/advanced/pre-shaders-afterglow.slang -filter_linear3 = false -scale_type3 = source -scale3 = 1.0 -mipmap_input3 = true -alias3 = PrePass - textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4" SamplerLUT1 = shaders/guest/advanced/lut/trinitron-lut.png SamplerLUT1_linear = true @@ -32,23 +8,49 @@ SamplerLUT2_linear = true SamplerLUT3 = shaders/guest/advanced/lut/nec-lut.png SamplerLUT3_linear = true SamplerLUT4 = shaders/guest/advanced/lut/ntsc-lut.png -SamplerLUT4_linear = true +SamplerLUT4_linear = true -shader4 = shaders/guest/advanced/avg-lum-hires.slang +shader0 = shaders/guest/hd/stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 + +shader1 = shaders/guest/hd/stock.slang +filter_linear1 = false +scale_type1 = source +scale1 = 1.0 +alias1 = StockPass + +shader2 = shaders/guest/hd/afterglow0.slang +filter_linear2 = false +scale_type2 = source +scale2 = 1.0 +alias2 = AfterglowPass + +shader3 = shaders/guest/hd/pre-shaders-afterglow.slang +filter_linear3 = false +scale_type3 = source +mipmap_input3 = true +scale3 = 1.0 +alias3 = PrePass + +shader4 = shaders/guest/hd/linearize-hd.slang filter_linear4 = true scale_type4 = source scale4 = 1.0 -mipmap_input4 = true -alias4 = AvgLumPass +float_framebuffer4 = true +alias4 = LinearizePass -shader5 = shaders/guest/advanced/linearize-hires.slang +shader5 = shaders/guest/hd/crt-guest-advanced-hd-pass1.slang filter_linear5 = true -scale_type5 = source -scale5 = 1.0 -alias5 = LinearizePass +scale_type_x5 = viewport +scale_x5 = 1.0 +scale_type_y5 = source +scale_y5 = 1.0 float_framebuffer5 = true +alias5 = Pass1 -shader6 = shaders/guest/advanced/bloom_horizontal_ntsc.slang +shader6 = shaders/guest/hd/bloom_horizontal.slang filter_linear6 = true scale_type_x6 = absolute scale_x6 = 800.0 @@ -56,22 +58,23 @@ scale_type_y6 = source scale_y6 = 1.0 float_framebuffer6 = true -shader7 = shaders/guest/advanced/bloom_vertical_ntsc.slang +shader7 = shaders/guest/hd/bloom_vertical.slang filter_linear7 = true -scale_type_x7 = absolute -scale_x7 = 800.0 +scale_type_x7 = source +scale_x7 = 1.0 scale_type_y7 = absolute scale_y7 = 600.0 float_framebuffer7 = true -alias7 = GlowPass +alias7 = BloomPass -shader8 = shaders/guest/advanced/crt-guest-advanced-hires.slang +shader8 = shaders/guest/hd/crt-guest-advanced-hd-pass2.slang filter_linear8 = true +float_framebuffer8 = true scale_type8 = viewport scale_x8 = 1.0 scale_y8 = 1.0 -shader9 = shaders/guest/advanced/deconvergence.slang +shader9 = shaders/guest/hd/deconvergence-hd.slang filter_linear9 = true scale_type9 = viewport scale_x9 = 1.0 diff --git a/crt/crt-guest-advanced-ntsc.slangp b/crt/crt-guest-advanced-ntsc.slangp new file mode 100644 index 0000000..8387f42 --- /dev/null +++ b/crt/crt-guest-advanced-ntsc.slangp @@ -0,0 +1,147 @@ +shaders = 18 + + +shader0 = shaders/guest/advanced/stock.slang +filter_linear0 = false +scale_type0 = source +scale0 = 1.0 + +shader1 = shaders/guest/advanced/stock.slang +filter_linear1 = false +scale_type1 = source +scale1 = 1.0 +alias1 = StockPass + +shader2 = shaders/guest/advanced/afterglow0.slang +filter_linear2 = true +scale_type2 = source +scale2 = 1.0 +alias2 = AfterglowPass + +shader3 = shaders/guest/advanced/pre-shaders-afterglow.slang +filter_linear3 = true +scale_type3 = source +scale3 = 1.0 +alias3 = PrePass0 + +textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4" +SamplerLUT1 = shaders/guest/advanced/lut/trinitron-lut.png +SamplerLUT1_linear = true +SamplerLUT2 = shaders/guest/advanced/lut/inv-trinitron-lut.png +SamplerLUT2_linear = true +SamplerLUT3 = shaders/guest/advanced/lut/nec-lut.png +SamplerLUT3_linear = true +SamplerLUT4 = shaders/guest/advanced/lut/ntsc-lut.png +SamplerLUT4_linear = true + +# custom ntsc shaders + +shader4 = shaders/guest/advanced/ntsc/ntsc-pass1.slang +shader5 = shaders/guest/advanced/ntsc/ntsc-pass2.slang + +alias4 = NPass1 + +filter_linear4 = false +filter_linear5 = true + +scale_type_x4 = source +scale_type_y4 = source +scale_x4 = 4.0 +scale_y4 = 1.0 +float_framebuffer4 = true +float_framebuffer5 = true + +scale_type5 = source +scale_x5 = 0.5 +scale_y5 = 1.0 + +shader6 = shaders/guest/advanced/ntsc/ntsc-pass3.slang +filter_linear6 = true +scale_type6 = source +scale_x6 = 1.0 +scale_y6 = 1.0 + +shader7 = shaders/guest/advanced/custom-fast-sharpen.slang +filter_linear7 = true +scale_type7 = source +scale_x7 = 1.0 +scale_y7 = 1.0 +alias7 = NtscPass + +shader8 = shaders/guest/advanced/stock.slang +filter_linear8 = true +scale_type8 = source +scale_x8 = 1.0 +scale_y8 = 1.0 +alias8 = PrePass +mipmap_input8 = true + +shader9 = shaders/guest/advanced/avg-lum-ntsc.slang +filter_linear9 = true +scale_type9 = source +scale9 = 1.0 +mipmap_input9 = true +alias9 = AvgLumPass + +shader10 = shaders/guest/advanced/linearize-ntsc.slang +filter_linear10 = true +scale_type10 = source +scale10 = 1.0 +alias10 = LinearizePass +float_framebuffer10 = true + +shader11 = shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang +filter_linear11 = true +scale_type_x11 = viewport +scale_x11 = 1.0 +scale_type_y11 = source +scale_y11 = 1.0 +float_framebuffer11 = true +alias11 = Pass1 + +shader12 = shaders/guest/advanced/gaussian_horizontal.slang +filter_linear12 = true +scale_type_x12 = absolute +scale_x12 = 800.0 +scale_type_y12 = source +scale_y12 = 1.0 +float_framebuffer12 = true + +shader13 = shaders/guest/advanced/gaussian_vertical.slang +filter_linear13 = true +scale_type_x13 = absolute +scale_x13 = 800.0 +scale_type_y13 = absolute +scale_y13 = 600.0 +float_framebuffer13 = true +alias13 = GlowPass + +shader14 = shaders/guest/advanced/bloom_horizontal.slang +filter_linear14 = true +scale_type_x14 = absolute +scale_x14 = 800.0 +scale_type_y14 = absolute +scale_y14 = 600.0 +float_framebuffer14 = true + +shader15 = shaders/guest/advanced/bloom_vertical.slang +filter_linear15 = true +scale_type_x15 = absolute +scale_x15 = 800.0 +scale_type_y15 = absolute +scale_y15 = 600.0 +float_framebuffer15 = true +alias15 = BloomPass + +shader16 = shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang +filter_linear16 = true +float_framebuffer16 = true +scale_type16 = viewport +scale_x16 = 1.0 +scale_y16 = 1.0 + +shader17 = shaders/guest/advanced/deconvergence-ntsc.slang +filter_linear17 = true +scale_type17 = viewport +scale_x17 = 1.0 +scale_y17 = 1.0 diff --git a/crt/crt-guest-advanced.slangp b/crt/crt-guest-advanced.slangp index 49f4a92..551c1a8 100644 --- a/crt/crt-guest-advanced.slangp +++ b/crt/crt-guest-advanced.slangp @@ -1,11 +1,11 @@ shaders = 12 -shader0 = ../stock.slang +shader0 = shaders/guest/advanced/stock.slang filter_linear0 = false scale_type0 = source scale0 = 1.0 -shader1 = ../stock.slang +shader1 = shaders/guest/advanced/stock.slang filter_linear1 = false scale_type1 = source scale1 = 1.0 @@ -84,6 +84,7 @@ alias9 = BloomPass shader10 = shaders/guest/advanced/crt-guest-advanced.slang filter_linear10 = true +float_framebuffer10 = true scale_type10 = viewport scale_x10 = 1.0 scale_y10 = 1.0 diff --git a/crt/crt-guest-dr-venom-fast.slangp b/crt/crt-guest-dr-venom-fast.slangp deleted file mode 100644 index 2f2293e..0000000 --- a/crt/crt-guest-dr-venom-fast.slangp +++ /dev/null @@ -1,40 +0,0 @@ -shaders = 5 - -shader0 = shaders/guest/lut/lut.slang -filter_linear0 = false -scale_type0 = source -scale0 = 1.0 - -textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3" -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 - -shader1 = shaders/guest/fast/smoothing.slang -filter_linear1 = false -scale_type1 = source -scale1 = 1.0 -alias1 = SmoothPass - -shader2 = shaders/guest/fast/linearize-multipass.slang -filter_linear2 = false -scale_type2 = source -scale2 = 1.0 -float_framebuffer2 = true - -shader3 = shaders/guest/fast/crt-guest-dr-venom-pass1.slang -filter_linear3 = false -scale_type_x3 = viewport -scale_x3 = 1.0 -scale_type_y3 = source -scale_y3 = 1.0 -float_framebuffer3 = true - -shader4 = shaders/guest/fast/crt-guest-dr-venom-pass2.slang -filter_linear4 = false -scale_type4 = viewport -scale_x4 = 1.0 -scale_y4 = 1.0 diff --git a/crt/crt-guest-dr-venom.slangp b/crt/crt-guest-dr-venom.slangp deleted file mode 100644 index dcc215f..0000000 --- a/crt/crt-guest-dr-venom.slangp +++ /dev/null @@ -1,71 +0,0 @@ -shaders = 10 - -shader0 = shaders/guest/lut/lut.slang -filter_linear0 = false -scale_type0 = source -scale0 = 1.0 - -textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3" -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 - -shader1 = shaders/guest/color-profiles.slang -filter_linear1 = true -scale_type1 = source -scale1 = 1.0 - -shader2 = shaders/guest/d65-d50.slang -filter_linear2 = true -scale_type2 = source -scale2 = 1.0 -alias2 = WhitePointPass - -shader3 = shaders/guest/afterglow.slang -filter_linear3 = true -scale_type3 = source -scale3 = 1.0 -alias3 = AfterglowPass - -shader4 = shaders/guest/avg-lum.slang -filter_linear4 = true -scale_type4 = source -scale4 = 1.0 -mipmap_input4 = true -float_framebuffer4 = true -alias4 = AvgLumPass - -shader5 = shaders/guest/linearize.slang -filter_linear5 = true -scale_type5 = source -scale5 = 1.0 -float_framebuffer5 = true -alias5 = LinearizePass - -shader6 = shaders/guest/blur_horiz.slang -filter_linear6 = true -scale_type6 = source -scale6 = 1.0 -float_framebuffer6 = true - -shader7 = shaders/guest/blur_vert.slang -filter_linear7 = true -scale_type7 = source -scale7 = 1.0 -float_framebuffer7 = true -alias7 = GlowPass - -shader8 = shaders/guest/linearize_scanlines.slang -filter_linear8 = true -scale_type8 = source -scale8 = 1.0 -float_framebuffer8 = true - -shader9 = shaders/guest/crt-guest-dr-venom.slang -filter_linear9 = true -scale_type9 = viewport -scale_x9 = 1.0 -scale_y9 = 1.0 diff --git a/crt/crt-guest-sm.slangp b/crt/crt-guest-sm.slangp deleted file mode 100644 index 1bd2298..0000000 --- a/crt/crt-guest-sm.slangp +++ /dev/null @@ -1,46 +0,0 @@ -# This shader supports Vertical games. -# Auto rotation must be turned OFF in Core Options. -# Per-game presets can be saved, so it should work out OK. - -shaders = 6 - -shader0 = shaders/guest/crt-sm/d65-d50-sm.slang -filter_linear0 = true -scale_type0 = source -scale0 = 1.0 -alias0 = WpPass - -shader1 = shaders/guest/crt-sm/crt-guest-sm-rot0.slang -float_framebuffer1 = true -filter_linear1 = true -scale_type_x1 = source -scale_type_y1 = source -scale_x1 = 1.0 -scale_y1 = 3.0 -alias1 = RotPass - -shader2 = shaders/guest/crt-sm/linearize-sm.slang -float_framebuffer2 = true -filter_linear2 = true -scale_type2 = source -scale2 = 1.0 -alias2 = LinPass - -shader3 = shaders/guest/crt-sm/blur_horiz-sm.slang -float_framebuffer3 = true -filter_linear3 = true -scale_type3 = source -scale3 = 1.0 - -shader4 = shaders/guest/crt-sm/blur_vert-sm.slang -float_framebuffer4 = true -filter_linear4 = true -scale_type4 = source -scale4 = 1.0 - -shader5 = shaders/guest/crt-sm/crt-guest-sm.slang -filter_linear5 = true -scale_type_x5 = viewport -scale_type_y5 = viewport -scale_x5 = 1.0 -scale_y5 = 1.0 diff --git a/crt/shaders/guest/README b/crt/shaders/guest/advanced/README_old similarity index 100% rename from crt/shaders/guest/README rename to crt/shaders/guest/advanced/README_old diff --git a/crt/shaders/guest/advanced/afterglow0.slang b/crt/shaders/guest/advanced/afterglow0.slang index ad482a2..3d12b1b 100644 --- a/crt/shaders/guest/advanced/afterglow0.slang +++ b/crt/shaders/guest/advanced/afterglow0.slang @@ -27,9 +27,9 @@ layout(push_constant) uniform Push } params; #pragma parameter bogus_afterglow "[ AFTERGLOW SETTINGS ]:" 0.0 0.0 1.0 1.0 -#pragma parameter PR " Persistence Red" 0.25 0.0 0.50 0.01 -#pragma parameter PG " Persistence Green" 0.25 0.0 0.50 0.01 -#pragma parameter PB " Persistence Blue" 0.25 0.0 0.50 0.01 +#pragma parameter PR " Persistence Red" 0.32 0.0 0.50 0.01 +#pragma parameter PG " Persistence Green" 0.32 0.0 0.50 0.01 +#pragma parameter PB " Persistence Blue" 0.32 0.0 0.50 0.01 #define PR params.PR #define PG params.PG @@ -68,9 +68,9 @@ void main() vec3 accumulate = COMPAT_TEXTURE(AfterglowPassFeedback, TEX0.xy).rgb; float w = 1.0; - if ((color.r + color.g + color.b < 1.5/255.0)) { w = 0.0; } + if ((color.r + color.g + color.b < 5.0/255.0)) { w = 0.0; } - vec3 result = mix( max(mix(color, accumulate, 0.49 + vec3(PR, PG, PB))- 3.0/255.0, 0.0), color, w); + vec3 result = mix( max(mix(color, accumulate, 0.49 + vec3(PR, PG, PB))- 2.0/255.0, 0.0), color, w); FragColor = vec4(result, w); } \ No newline at end of file diff --git a/crt/shaders/guest/advanced/avg-lum-hires.slang b/crt/shaders/guest/advanced/avg-lum-hires.slang deleted file mode 100644 index e446989..0000000 --- a/crt/shaders/guest/advanced/avg-lum-hires.slang +++ /dev/null @@ -1,87 +0,0 @@ -#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.70 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 PrePass; -layout(set = 0, binding = 3) uniform sampler2D AvgLumPassFeedback; - - -void main() -{ - if (vTexCoord.x > 0.2 || vTexCoord.y > 0.2) discard; - - float m = max(log2(SourceSize.x), log2(SourceSize.y)); - m = floor(max(m, 1.0)); - - float ltotal = 0.0; - - ltotal+= length(textureLod(PrePass, vec2(0.25, 0.25), m).rgb); - ltotal+= length(textureLod(PrePass, vec2(0.25, 0.75), m).rgb); - ltotal+= length(textureLod(PrePass, vec2(0.75, 0.25), m).rgb); - ltotal+= length(textureLod(PrePass, vec2(0.75, 0.75), m).rgb); - - ltotal*=0.25; - - ltotal = pow(0.577350269 * ltotal, 0.65); - - 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/advanced/avg-lum-ntsc.slang b/crt/shaders/guest/advanced/avg-lum-ntsc.slang index 86da9b9..77b8f28 100644 --- a/crt/shaders/guest/advanced/avg-lum-ntsc.slang +++ b/crt/shaders/guest/advanced/avg-lum-ntsc.slang @@ -75,7 +75,7 @@ float dist(vec3 A, vec3 B) void main() { float m = max(log2(SourceSize.x), log2(SourceSize.y)); - m = floor(max(m, 1.0)); + m = floor(max(m, 1.0))-1.0; vec2 dx = vec2(1.0/SourceSize.x, 0.0); vec2 dy = vec2(0.0, 1.0/SourceSize.y); @@ -84,14 +84,14 @@ void main() float ltotal = 0.0; - ltotal+= length(textureLod(Source, vec2(0.25, 0.25), m).rgb); - ltotal+= length(textureLod(Source, vec2(0.25, 0.75), m).rgb); - ltotal+= length(textureLod(Source, vec2(0.75, 0.25), m).rgb); - ltotal+= length(textureLod(Source, vec2(0.75, 0.75), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.3, 0.3), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.3, 0.7), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.7, 0.3), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.7, 0.7), m).rgb); ltotal*=0.25; - ltotal = pow(0.577350269 * ltotal, 0.65); + ltotal = pow(0.577350269 * ltotal, 0.70); float lhistory = texture(AvgLumPassFeedback, vec2(0.5,0.5)).a; diff --git a/crt/shaders/guest/advanced/avg-lum.slang b/crt/shaders/guest/advanced/avg-lum.slang index 86da9b9..77b8f28 100644 --- a/crt/shaders/guest/advanced/avg-lum.slang +++ b/crt/shaders/guest/advanced/avg-lum.slang @@ -75,7 +75,7 @@ float dist(vec3 A, vec3 B) void main() { float m = max(log2(SourceSize.x), log2(SourceSize.y)); - m = floor(max(m, 1.0)); + m = floor(max(m, 1.0))-1.0; vec2 dx = vec2(1.0/SourceSize.x, 0.0); vec2 dy = vec2(0.0, 1.0/SourceSize.y); @@ -84,14 +84,14 @@ void main() float ltotal = 0.0; - ltotal+= length(textureLod(Source, vec2(0.25, 0.25), m).rgb); - ltotal+= length(textureLod(Source, vec2(0.25, 0.75), m).rgb); - ltotal+= length(textureLod(Source, vec2(0.75, 0.25), m).rgb); - ltotal+= length(textureLod(Source, vec2(0.75, 0.75), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.3, 0.3), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.3, 0.7), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.7, 0.3), m).rgb); + ltotal+= length(textureLod(Source, vec2(0.7, 0.7), m).rgb); ltotal*=0.25; - ltotal = pow(0.577350269 * ltotal, 0.65); + ltotal = pow(0.577350269 * ltotal, 0.70); float lhistory = texture(AvgLumPassFeedback, vec2(0.5,0.5)).a; diff --git a/crt/shaders/guest/advanced/bloom_horizontal.slang b/crt/shaders/guest/advanced/bloom_horizontal.slang index 72af8f4..a8d8454 100644 --- a/crt/shaders/guest/advanced/bloom_horizontal.slang +++ b/crt/shaders/guest/advanced/bloom_horizontal.slang @@ -33,10 +33,10 @@ layout(push_constant) uniform Push #pragma parameter bogus_bloom "[ BLOOM/HALATION/(GLOW) PASS SETTINGS ]:" 0.0 0.0 1.0 1.0 -#pragma parameter SIZEHB " Horizontal Bloom/Halation/(Glow) Radius" 4.0 1.0 50.0 1.0 +#pragma parameter SIZEHB " Horizontal Bloom/Halation/(Glow) Radius" 3.0 1.0 50.0 1.0 #define SIZEHB params.SIZEHB -#pragma parameter SIGMA_HB " Horizontal Bloom/Halation/(Glow) Sigma" 0.70 0.25 15.0 0.05 +#pragma parameter SIGMA_HB " Horizontal Bloom/Halation/(Glow) Sigma" 0.75 0.25 15.0 0.05 #define SIGMA_HB params.SIGMA_HB layout(std140, set = 0, binding = 0) uniform UBO diff --git a/crt/shaders/guest/advanced/bloom_horizontal_ntsc.slang b/crt/shaders/guest/advanced/bloom_horizontal_ntsc.slang deleted file mode 100644 index 687427f..0000000 --- a/crt/shaders/guest/advanced/bloom_horizontal_ntsc.slang +++ /dev/null @@ -1,101 +0,0 @@ -#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. - -*/ - -layout(push_constant) uniform Push -{ - vec4 LinearizePassSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float SIZEHB; - float SIGMA_HB; -} params; - -#pragma parameter bogus_bloom "[ BLOOM/HALATION/(GLOW) PASS SETTINGS ]:" 0.0 0.0 1.0 1.0 - -#pragma parameter SIZEHB " Horizontal Bloom/Halation/(Glow) Radius" 6.0 1.0 50.0 1.0 -#define SIZEHB params.SIZEHB - -#pragma parameter SIGMA_HB " Horizontal Bloom/Halation/(Glow) Sigma" 1.0 0.20 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; - vec4 color = vec4(0.0); - vec2 dx = vec2(SourceSize1.z, 0.0); - - float w; - float wsum = 0.0; - vec4 pixel; - float n = -SIZEHB; - - do - { - pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx); - w = gaussian(n+f); - pixel.a = max(max(pixel.r, pixel.g),pixel.b); - pixel.a*=pixel.a*pixel.a; - color = color + w * pixel; - wsum = wsum + w; - n = n + 1.0; - - } while (n <= SIZEHB); - - color = color / wsum; - - FragColor = vec4(color.rgb, pow(color.a, 0.333333)); -} \ No newline at end of file diff --git a/crt/shaders/guest/advanced/bloom_vertical.slang b/crt/shaders/guest/advanced/bloom_vertical.slang index 5dd8a3c..f587439 100644 --- a/crt/shaders/guest/advanced/bloom_vertical.slang +++ b/crt/shaders/guest/advanced/bloom_vertical.slang @@ -32,10 +32,10 @@ layout(push_constant) uniform Push } params; -#pragma parameter SIZEVB " Vertical Bloom/Halation/(Glow) Radius" 4.0 1.0 50.0 1.0 +#pragma parameter SIZEVB " Vertical Bloom/Halation/(Glow) Radius" 3.0 1.0 50.0 1.0 #define SIZEVB params.SIZEVB -#pragma parameter SIGMA_VB " Vertical Bloom/Halation/(Glow) Sigma" 0.70 0.25 15.0 0.05 +#pragma parameter SIGMA_VB " Vertical Bloom/Halation/(Glow) Sigma" 0.60 0.25 15.0 0.05 #define SIGMA_VB params.SIGMA_VB layout(std140, set = 0, binding = 0) uniform UBO diff --git a/crt/shaders/guest/advanced/bloom_vertical_ntsc.slang b/crt/shaders/guest/advanced/bloom_vertical_ntsc.slang deleted file mode 100644 index 91c6c75..0000000 --- a/crt/shaders/guest/advanced/bloom_vertical_ntsc.slang +++ /dev/null @@ -1,101 +0,0 @@ -#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. - -*/ - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float SIZEVB; - float SIGMA_VB; -} params; - - -#pragma parameter SIZEVB " Vertical Bloom/Halation/(Glow) Radius" 6.0 1.0 50.0 1.0 -#define SIZEVB params.SIZEVB - -#pragma parameter SIGMA_VB " Vertical Bloom/Halation/(Glow) Sigma" 1.0 0.20 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; - vec4 color = vec4(0.0); - vec2 dy = vec2(0.0, SourceSize1.w); - - float w; - float wsum = 0.0; - vec4 pixel; - float n = -SIZEVB; - - do - { - pixel = COMPAT_TEXTURE(Source, tex + n*dy); - w = gaussian(n+f); - pixel.a*=pixel.a*pixel.a; - color = color + w * pixel; - wsum = wsum + w; - n = n + 1.0; - - } while (n <= SIZEVB); - - color = color / wsum; - - FragColor = vec4(color.rgb, pow(color.a, 0.175)); -} \ No newline at end of file diff --git a/crt/shaders/guest/advanced/convert-ntsc.slang b/crt/shaders/guest/advanced/convert-ntsc.slang deleted file mode 100644 index 2778803..0000000 --- a/crt/shaders/guest/advanced/convert-ntsc.slang +++ /dev/null @@ -1,69 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float blendMode; -} params; - -#pragma parameter blendMode "NTSC Blend Mode (Main Mode Control)" 1.0 0.0 2.0 1.0 - -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 PrePass0; - -vec3 plant (vec3 tar, float r) -{ - float t = max(max(tar.r,tar.g),tar.b) + 0.00001; - return tar * r / t; -} - -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; -} - -void main() -{ - vec2 dx = vec2(params.SourceSize.z * 0.5, 0.0); - vec3 col1 = texture(Source, vTexCoord -dx).rgb; - vec3 col2 = texture(Source, vTexCoord +dx).rgb; - vec3 colc = max(col1, col2); - vec3 col = plant(sqrt(col1*col2), max(max(colc.r, colc.g),colc.b)); - - vec3 orig = texture(PrePass0, vTexCoord).rgb; - vec3 res = normalize(col + 0.00001) * min(length(col), length(orig)); - - float k2 = 1.0/(dot(col1 - res, col1 - res) + 0.0001); - float k3 = 1.0/(dot(col2 - res, col2 - res) + 0.0001); - - vec3 res1 = (k2 * col1 + k3 * col2) / (k2 + k3); - res1 = clamp(res1, min(col1,col2), max(col1, col2)); - - if ( params.blendMode == 1.0) res = res1; - - FragColor = vec4(res, 1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/advanced/crt-guest-advanced-hires.slang b/crt/shaders/guest/advanced/crt-guest-advanced-hires.slang deleted file mode 100644 index 7a3dda4..0000000 --- a/crt/shaders/guest/advanced/crt-guest-advanced-hires.slang +++ /dev/null @@ -1,775 +0,0 @@ -#version 450 - -/* - CRT - Guest - Advanced (Hi-Res version) - - Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com - - Incorporates many good ideas and suggestions from Dr. Venom. - I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. - - 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, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, spike, intres; -} params; - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float bloom; - float halation; - float mclip; - float scans; - float scansub; - float slotms; - float gamma_c; - float mask_gamma; - float gamma_out; - float overscanX; - float overscanY; - float c_shape; - float barspeed; - float barintensity; - float bardir; -} global; - -#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 - -#pragma parameter glow " Glow Strength" 0.08 -2.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 2.0 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.25 10.0 0.05 -#define brightboost params.brightboost // adjust brightness - -#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 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 -1.0 2.0 1.0 -#define gsl params.gsl // Alternate scanlines - -#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 0.0 20.0 0.5 -#define scanline1 params.scanline1 // scanline param, vertical sharpness - -#pragma parameter scanline2 " Scanline Beam Shape Edges" 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.25 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.10 -#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 Y: 224p/240p, 1.5...y-dowsample" 0.0 0.0 6.0 0.5 // Joint parameter with linearize 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.25 0.01 -#define csize params.csize // corner size - -#pragma parameter bsize " Border smoothness" 600.0 100.0 700.0 10.0 -#define bsize params.bsize // border smoothness - -#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 - -#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 - -#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 - -#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 -#define warpX params.warpX // Curvature X - -#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 -#define warpY params.warpY // Curvature Y - -#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 -#define c_shape global.c_shape // curvature shape - -#pragma parameter overscanX " Overscan X original pixels" 0.0 -50.0 50.0 1.0 -#define overscanX global.overscanX // OverscanX pixels - -#pragma parameter overscanY " Overscan Y original pixels" 0.0 -50.0 50.0 1.0 -#define overscanY global.overscanY // OverscanY pixels - -#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 mcut " Mask 5-7 Low Strength" 1.10 0.0 2.0 0.05 -#define mcut params.mcut // Mask 5-7 dark color strength - -#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 4.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 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 Bright Pixels" 0.0 0.0 1.0 0.05 -#define slotmask params.slotmask - -#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 -#define slotmask1 params.slotmask1 - -#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 4.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 - - -#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; -layout(set = 0, binding = 5) uniform sampler2D PrePass; - -#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; - ex = (gsl > -0.5) ? ex*ex : mix(ex*ex, ex*ex*ex, 0.4); - return exp2(-scanline*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-0.5*color)*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 + slotmask1) == 0.0) return 1.0; - else - { - 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; - } -} - -vec2 Warp(vec2 pos) -{ - pos = pos*2.0-1.0; - pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); - 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; -} - -float humbar(float pos) -{ - if (global.barintensity == 0.0) return 1.0; else - { - pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); - pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); - pos = (global.barintensity < 0.0) ? pos : (1.0-pos); - return (1.0-global.barintensity) + global.barintensity*pos; - } -} - -// Borrowed from maskstr's crt-geom, under GPL - -float corner(vec2 coord) -{ - coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); - vec2 cdist = vec2(max(csize/3.0, 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 == 0.5) sy = SourceY/224.0; else - 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); - - // 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); - texcoord = Overscan(texcoord, (SourceSize.x - overscanX)/SourceSize.x, (SourceSize.y - overscanY)/SourceSize.y); - 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; - - 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 = (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+=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 = (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.5) { 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 - { 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); - w3 = wf1+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(max(1.0-w3*w3, 2.5*f1), 1.0); - float ds2 = min(max(1.0-w3*w3, 2.5*f2), 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); -} - - if (interb) - { - color = gc(color1); - mcolor = mcolor1; - } - - float mx = max(max(mcolor.r,mcolor.g),mcolor.b); - mx = pow(mx, 1.20/gamma_in); - - // Apply Mask - - vec3 orig1 = color; - vec3 cmask = one; - - vec2 maskcoord = gl_FragCoord.yx * 1.000001; - if (notate) maskcoord = maskcoord.yx; - - float smask = SlotMask(maskcoord, mx); - cmask*= Mask(maskcoord, 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 = Glow; - float maxb = COMPAT_TEXTURE(GlowPass, pos ).a; - float vig = COMPAT_TEXTURE(PrePass, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; - - vec3 Bloom1 = min(Glow*(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)); - - if (halation > 0.025) { - Bloom = mix(0.5*(Bloom + Bloom*Bloom), Bloom*Bloom, colmx); - color = color + (1.1-0.25*colmx)*(0.75+maxb)*Bloom*(0.75 + 0.70*pow(colmx,0.33333))*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.35 + 0.4*maxb)*halation; } - - Glow = mix(Glow, 0.25*color, 0.7*colmx); - if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } - - color = min(color, 1.0); - - color = pow(color, vec3(1.0/gamma_out)); - - float corner0 = corner(pos0); - - FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir)), corner0); -} diff --git a/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang b/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang new file mode 100644 index 0000000..d829150 --- /dev/null +++ b/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass1.slang @@ -0,0 +1,165 @@ +#version 450 + +/* + CRT - Guest - NTSC - Pass1 + + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. + + 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 SIGMA_HOR; + float HSHARPNESS; + float S_SHARP; + float HARNG; + float HSHARP; + float prescalex; + float prescaley; + float spike; + float internal_res; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + + +#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter HSHARPNESS " Horizontal Filter Range" 1.5 1.0 8.0 0.125 +#define HSHARPNESS params.HSHARPNESS + +#pragma parameter SIGMA_HOR " Horizontal Blur Sigma" 0.90 0.1 7.0 0.05 +#define SIGMA_HOR params.SIGMA_HOR + +#pragma parameter S_SHARP " Substractive Sharpness" 0.9 0.0 2.0 0.10 +#define S_SHARP params.S_SHARP + +#pragma parameter HSHARP " Sharpness Definition" 1.2 0.0 2.0 0.10 +#define HSHARP params.HSHARP + +#pragma parameter HARNG " Substractive Sharpness Ringing" 0.4 0.0 4.0 0.10 +#define HARNG params.HARNG + +#pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 +#define spike params.spike + +#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 // Joint parameter with main pass, values must match +#define prescalex params.prescalex // prescale-x factor + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define TEX0 vTexCoord + +#define OutputSize params.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.0001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D LinearizePass; + +float invsqrsigma = 1.0/(2.0*SIGMA_HOR*SIGMA_HOR); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +void main() +{ + vec4 SourceSize = params.OriginalSize * vec4(2.0*prescalex, 1.0, 0.5/prescalex, 1.0); + + float f = fract(SourceSize.x * vTexCoord.x); + f = 0.5 - f; + vec2 tex = floor(SourceSize.xy * vTexCoord)*SourceSize.zw + 0.5*SourceSize.zw; + vec3 color = 0.0.xxx; + float scolor = 0.0; + vec2 dx = vec2(SourceSize.z, 0.0); + + float w = 0.0; + float swsum = 0.0; + float wsum = 0.0; + vec3 pixel; + + float hsharpness = HSHARPNESS; + vec3 cmax = 0.0.xxx; + vec3 cmin = 1.0.xxx; + float sharp = gaussian(hsharpness) * S_SHARP; + float maxsharp = 0.20; + float FPR = hsharpness; + float fpx = 0.0; + float sp = 0.0; + float sw = 0.0; + + float ts = 0.025; + vec3 luma = vec3(0.2126, 0.7152, 0.0722); + + float LOOPSIZE = ceil(2.0*FPR); + float CLAMPSIZE = round(2.0*LOOPSIZE/3.0); + + float n = -LOOPSIZE; + + do + { + pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx).rgb; + sp = max(max(pixel.r,pixel.g),pixel.b); + + w = gaussian(n+f) - sharp; + fpx = abs(n+f-sign(n)*FPR)/FPR; + if (abs(n) <= CLAMPSIZE) { cmax = max(cmax, pixel); cmin = min(cmin, pixel); } + if (w < 0.0) w = clamp(w, mix(-maxsharp, 0.0, pow(fpx, HSHARP)), 0.0); + + color = color + w * pixel; + wsum = wsum + w; + + sw = max(w, 0.0) * (dot(pixel,luma) + ts); + scolor = scolor + sw * sp; + swsum = swsum + sw; + + n = n + 1.0; + + } while (n <= LOOPSIZE); + + color = color / wsum; + scolor = scolor / swsum; + + color = clamp(mix(clamp(color, cmin, cmax), color, HARNG), 0.0, 1.0); + + scolor = clamp(mix(max(max(color.r, color.g),color.b), scolor, spike), 0.0, 1.0); + + FragColor = vec4(color, scolor); +} \ No newline at end of file diff --git a/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang b/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang new file mode 100644 index 0000000..8a6b5d5 --- /dev/null +++ b/crt/shaders/guest/advanced/crt-guest-advanced-ntsc-pass2.slang @@ -0,0 +1,399 @@ +#version 450 + +/* + CRT - Guest - Advanced - NTSC + + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. + + 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 IOS, OS, BLOOM, brightboost, brightboost1, gsl, scanline1, scanline2, beam_min, beam_max, beam_size, + h_sharp, s_sharp, warpX, warpY, glow, shadowMask, masksize, vertmask, ring; +} 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 gamma_c; + float gamma_out; + float overscanX; + float overscanY; + float intres; + float prescalex; + float c_shape; + float blendMode; + float scangamma; + float rolling_scan; + float sborder; + float scan_falloff; + float bloom_dist; +} global; + + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + +#pragma parameter halation " Halation Strength" 0.0 0.0 2.0 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.25 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 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 -1.0 2.0 1.0 +#define gsl params.gsl // Alternate scanlines + +#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 -20.0 20.0 0.5 +#define scanline1 params.scanline1 // scanline param, vertical sharpness + +#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 0.0 70.0 1.0 +#define scanline2 params.scanline2 // scanline param, vertical sharpness + +#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 5.0 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 3.5 0.025 +#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 / Mask Falloff" 0.60 0.0 2.5 0.05 +#define scans global.scans // scanline saturation + +#pragma parameter scan_falloff " Scanline Falloff" 1.0 0.25 2.0 0.05 +#define scan_falloff global.scan_falloff // scanline falloff + +#pragma parameter scangamma " Scanline Gamma" 2.40 0.5 5.0 0.05 +#define scangamma global.scangamma + +#pragma parameter rolling_scan " Rolling Scanlines" 0.0 -1.0 1.0 0.01 +#define rolling_scan global.rolling_scan // rolling scanlines + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter intres " Internal Resolution Y: 224p/240p, 1.5...y-dowsample" 0.0 0.0 6.0 0.5 // Joint parameter with linearize pass, values must match + +#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 warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 +#define c_shape global.c_shape // curvature shape + +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 +#define prescalex global.prescalex // prescale-x factor + + +#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.0001; +} + +#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 Pass1; + +#define eps 1e-8 + +float st(float x) +{ + return exp2(-10.0*x*x); +} + +float st1(float x) +{ + return exp2(-7.0*x*x); +} + +float sw0(float x, float color, float scanline) +{ + float tmp = mix(beam_min, beam_max, color); + float ex = x*tmp; + ex = (gsl > -0.5) ? ex*ex : mix(ex*ex, ex*ex*ex, 0.4); + return exp2(-scanline*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-0.5*color)*beam_min, beam_max, color); + tmp = mix(beam_max, tmp, pow(x, color+0.3)); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); + 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; +} + + +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(prescalex, 1.0, 1.0/prescalex, 1.0); + + SourceSize*= vec4(2.0, 1.0, 0.5, 1.0); + + float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.5,0.5)).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.5); + + float SourceY = SourceSize.y; + float sy = 1.0; + if (global.intres == 0.5) sy = SourceY/224.0; else + if (global.intres == 1.0) sy = SourceY/240.0; else + if (global.intres > 1.25) sy = global.intres; + SourceSize*=vec4(1.0, 1.0/sy, 1.0, sy); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + if (IOS > 0.0 && !interb){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = diff.y; + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = vec2(TEX0.x, texcoord.y); + } + + float factor = 1.00 + (1.0-0.5*OS)*BLOOM/100.0 - lum*BLOOM/100.0; + texcoord = Overscan(texcoord, factor, factor); + + texcoord = Overscan(texcoord, (SourceSize.x - overscanX)/SourceSize.x, (SourceSize.y - overscanY)/SourceSize.y); + + 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 + + float f = fp.y; + + vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps; + pC4.x = pos.x; + if (interb) pC4.y = pos.y; + + vec3 color1 = COMPAT_TEXTURE(Pass1, pC4 ).rgb; + vec3 scolor1 = COMPAT_TEXTURE(Pass1, pC4 ).aaa; + + if(!interb) color1 = pow(color1, vec3(scangamma/gamma_in)); + + pC4+=dy; + + vec3 color2 = COMPAT_TEXTURE(Pass1, pC4 ).rgb; + vec3 scolor2 = COMPAT_TEXTURE(Pass1, pC4 ).aaa; + + if(!interb) color2 = pow(color2, vec3(scangamma/gamma_in)); + + vec3 ctmp = color1; float w3 = 1.0; vec3 color = color1; + vec3 one = vec3(1.0); + +if (!interb) +{ + // calculating scanlines + + 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; + + ctmp = color00/(wt1+wt2); + vec3 sctmp = scolor0/(wt1+wt2); + + float wf1, wf2; + + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = pow(max(max(cref1.r,cref1.g),cref1.b), scan_falloff); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = pow(max(max(cref2.r,cref2.g),cref2.b), scan_falloff); + + float f1 = f; + float f2 = 1.0-f; + + float scanpix = SourceSize.x/OutputSize.x; + + f1 = fract(f1 + rolling_scan*float(global.FrameCount)*scanpix); + f2 = 1.0 - f1; + + if (gsl < 0.5) { 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 + { 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 saturation application + + vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); + w3 = wf1+wf2; + + float mc1 = max(max(color1.r,color1.g),color1.b) + eps; + float mc2 = max(max(color2.r,color2.g),color2.b) + eps; + + cref1 = color1 / mc1; cref1=cref1*cref1; cref1*=cref1; + cref2 = color2 / mc2; cref2=cref2*cref2; cref2*=cref2; + + w1 = max( mix(w1*mix(one, cref1, scans), w1, wf1*min((1.0+0.15*scans), 1.2)), 0.0); w1 = min(w1*color1, mc1)/(color1 + eps); + w2 = max( mix(w2*mix(one, cref2, scans), w2, wf2*min((1.0+0.15*scans), 1.2)), 0.0); w2 = min(w2*color2, mc2)/(color2 + eps); + + // Scanline Deconvergence + + 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(max(1.0-w3*w3, 2.5*f1), 1.0); + float ds2 = min(max(1.0-w3*w3, 2.5*f2), 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; + + if (abs(rolling_scan) > 0.005) + { + wt1 = st1(f); + wt2 = st1(1.0-f); + color00 = (color1*wt1 + color2*wt2)/(wt1+wt2); + color = gc(color00) * mix(w1+w2, w3.xxx, max(wf1,wf2)); + } + + color = min(color, 1.0); +} + + if (interb) + { + color = gc(color1); + } + + float colmx = pow(max(max(ctmp.r,ctmp.g),ctmp.b), 1.40/gamma_in); + + if(!interb) color = pow( color, vec3(gamma_in/scangamma) ); + + FragColor = vec4(color, colmx); +} diff --git a/crt/shaders/guest/advanced/crt-guest-advanced-ntsc.slang b/crt/shaders/guest/advanced/crt-guest-advanced-ntsc.slang deleted file mode 100644 index d9c8086..0000000 --- a/crt/shaders/guest/advanced/crt-guest-advanced-ntsc.slang +++ /dev/null @@ -1,904 +0,0 @@ -#version 450 - -/* - CRT - Guest - Advanced - - Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com - - Incorporates many good ideas and suggestions from Dr. Venom. - I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. - - 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, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, spike; -} 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; - float overscanY; - float intres; - float prescalex; - float c_shape; - float barspeed; - float barintensity; - float bardir; - float blendMode; - float scangamma; -} global; - - -#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 - -#pragma parameter blendMode "NTSC Blend Mode (Main Mode Control)" 1.0 0.0 2.0 1.0 - -#pragma parameter glow " Glow Strength" 0.08 -2.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 2.0 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.25 0.25 10.0 0.05 -#define brightboost params.brightboost // adjust brightness - -#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 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 -1.0 2.0 1.0 -#define gsl params.gsl // Alternate scanlines - -#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 0.0 20.0 0.5 -#define scanline1 params.scanline1 // scanline param, vertical sharpness - -#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 0.0 40.0 1.0 -#define scanline2 params.scanline2 // scanline param, vertical sharpness - -#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 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.65 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" 0.7 0.0 2.0 0.10 -#define spike params.spike - -#pragma parameter scangamma " Scanline Gamma" 2.40 0.5 5.0 0.05 -#define scangamma global.scangamma - -#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 - -#pragma parameter h_sharp " Horizontal sharpness" 3.70 0.10 15.0 0.10 -#define h_sharp params.h_sharp // pixel sharpness - -#pragma parameter s_sharp " Substractive sharpness (1.0 recommended)" 0.20 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 intres " Internal Resolution Y: 224p/240p, 1.5...y-dowsample" 0.0 0.0 6.0 0.5 // Joint parameter with linearize pass, values must match - -#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.25 0.01 -#define csize params.csize // corner size - -#pragma parameter bsize " Border smoothness" 600.0 100.0 700.0 10.0 -#define bsize params.bsize // border smoothness - -#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 - -#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 - -#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 - -#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 -#define warpX params.warpX // Curvature X - -#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 -#define warpY params.warpY // Curvature Y - -#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 -#define c_shape global.c_shape // curvature shape - -#pragma parameter overscanY " Overscan Y original pixels" 0.0 -50.0 50.0 1.0 -#define overscanY global.overscanY // OverscanY pixels - -#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 -#define prescalex global.prescalex // prescale-x factor - -#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 mcut " Mask 5-7 Low Strength" 1.10 0.0 2.0 0.05 -#define mcut params.mcut // Mask 5-7 dark color strength - -#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 4.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 mask_gamma " Mask gamma" 2.40 1.0 5.0 0.025 -#define mask_gamma global.mask_gamma // Mask application gamma - -#pragma parameter slotmask " Slot Mask Strength Bright Pixels" 0.0 0.0 1.0 0.05 -#define slotmask params.slotmask - -#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 -#define slotmask1 params.slotmask1 - -#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 4.0 1.0 -#define slotms global.slotms // Slot Mask Size - -#pragma parameter mclip " Keep Mask effect with clipping" 0.50 0.0 1.0 0.05 -#define mclip global.mclip // Slot Mask Size - -#pragma parameter gamma_out "Gamma out" 2.0 1.0 5.0 0.05 -#define gamma_out global.gamma_out // output gamma - -#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.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; -layout(set = 0, binding = 6) uniform sampler2D PrePass0; -layout(set = 0, binding = 7) uniform sampler2D NtscPass; - -#define eps 1e-8 - -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; - ex = (gsl > -0.5) ? ex*ex : mix(ex*ex, ex*ex*ex, 0.4); - return exp2(-scanline*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-0.5*color)*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; - else - { - 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; - } -} - -vec2 Warp(vec2 pos) -{ - pos = pos*2.0-1.0; - pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); - 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; -} - -float humbar(float pos) -{ - if (global.barintensity == 0.0) return 1.0; else - { - pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); - pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); - pos = (global.barintensity < 0.0) ? pos : (1.0-pos); - return (1.0-global.barintensity) + global.barintensity*pos; - } -} - -// Borrowed from cgwg's crt-geom, under GPL - -float corner(vec2 coord) -{ - coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); - vec2 cdist = vec2(max(csize/3.0, 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); -} - -vec3 plant (vec3 tar, float r) -{ - float t = max(max(tar.r,tar.g),tar.b) + 0.00001; - return tar * r / t; -} - -void main() -{ - vec4 SourceSize = global.OriginalSize * vec4(prescalex, 1.0, 1.0/prescalex, 1.0); - - bool nmode1 = (global.blendMode == 1.0); - bool nmode2 = (global.blendMode > 1.5); - - if (nmode2) SourceSize*= vec4(2.0, 1.0, 0.5, 1.0); - - float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.5,0.5)).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 (global.intres == 0.5) sy = SourceY/224.0; else - if (global.intres == 1.0) sy = SourceY/240.0; else - if (global.intres > 1.25) sy = global.intres; - if (notate) SourceSize*=vec4(1.0, 1.0/sy, 1.0, sy); else SourceSize*=vec4(1.0/sy, 1.0, sy, 1.0); - - // 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); - - texcoord = Overscan(texcoord, 1.0, (SourceSize.y - overscanY)/SourceSize.y); - - vec2 pos = Warp(texcoord); - vec2 pos0 = Warp(TEX0.xy); - - 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; - - vec2 pC5 = mix(vec2(pos.x, pC4.y), vec2(pC4.x, pos.y), float(!notate)); - - if (interb) pC4.y = pos.y; - - float zero = exp2(-h_sharp); - float sharp1 = s_sharp * zero; - - float fdivider = min(prescalex + 0.325*float(nmode2), 2.0); - - float wl3 = (2.0 + fpx)/fdivider; - float wl2 = (1.0 + fpx)/fdivider; - float wl1 = ( fpx)/fdivider; - float wr1 = (1.0 - fpx)/fdivider; - float wr2 = (2.0 - fpx)/fdivider; - float wr3 = (3.0 - fpx)/fdivider; - - 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 twl3 = max(wl3 - sharp1, 0.0); - float twl2 = max(wl2 - sharp1, mix(-0.12, 0.0, 1.0-fp1*fp1)); - 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 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); - } - - vec3 cc, l3, l2, l1, r1, r2, r3, sl2, sl1, sr1, sr2, color1, color2, colmin, colmax; - -if (nmode2) -{ - l3 = pow(COMPAT_TEXTURE(NtscPass, pC4 -off2).rgb, gamma_in.xxx); - l2 = pow(COMPAT_TEXTURE(NtscPass, pC4 -offx).rgb, gamma_in.xxx); - l1 = pow(COMPAT_TEXTURE(NtscPass, pC4 ).rgb, gamma_in.xxx); - r1 = pow(COMPAT_TEXTURE(NtscPass, pC4 +offx).rgb, gamma_in.xxx); - r2 = pow(COMPAT_TEXTURE(NtscPass, pC4 +off2).rgb, gamma_in.xxx); - r3 = pow(COMPAT_TEXTURE(NtscPass, pC4 +offx+off2).rgb, gamma_in.xxx); -} -else -{ - 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; - cc = pow(COMPAT_TEXTURE(NtscPass, pC5).rgb, gamma_in.xxx); -} - 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(abs(rwl1), pc); twr1 = pow(abs(rwr1), pc); - twl2 = pow(abs(rwl2), pl); twr2 = pow(abs(rwr2), pr); - float wmax = max(twl1, twr1); - float sharp_ei = s_sharp*pow(zero, pc)/wmax; - twl2 = max(twl2/wmax - sharp_ei, mix(-0.12, 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.12, 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); - if (nmode1 && !interb) color1 = min(normalize (color1+eps) * length(sqrt(color1 * cc)) ,1.0); - - color1 = pow(color1, vec3(scangamma/gamma_in)); - - float ts = 0.033; - - bool ntscbm = (global.blendMode == 0.0); - -if (ntscbm) -{ - l2 = COMPAT_TEXTURE(PrePass0, pC4 -offx).rgb; - l1 = COMPAT_TEXTURE(PrePass0, pC4 ).rgb; - r1 = COMPAT_TEXTURE(PrePass0, pC4 +offx).rgb; - r2 = COMPAT_TEXTURE(PrePass0, pC4 +off2).rgb; -} - - float lm2 = max(max(l2.r,l2.g),l2.b); - float lm1 = max(max(l1.r,l1.g),l1.b); - float rm1 = max(max(r1.r,r1.g),r1.b); - float rm2 = max(max(r2.r,r2.g),r2.b); - -if (ntscbm) -{ - lm2 = pow(lm2, gamma_in); - lm1 = pow(lm1, gamma_in); - rm1 = pow(rm1, gamma_in); - rm2 = pow(rm2, gamma_in); -} - - float swl2 = max(twl2, eps) * (lm2+ts); - float swl1 = twl1 * (lm1+ts); - float swr1 = twr1 * (rm1+ts); - float swr2 = max(twr2, eps) * (rm2+ts); - - float fscolor1 = (lm2*swl2 + lm1*swl1 + rm1*swr1 + rm2*swr2)/(swl2+swl1+swr1+swr2); - vec3 mcolor1 = vec3(fscolor1); - vec3 scolor1 = vec3(clamp(mix(max(max(color1.r,color1.g),color1.b), fscolor1, spike), 0.0, 1.0)); - - vec3 scolor2, mcolor2; - - if (!interb) -{ - pC4+=offy; - pC5+=offy; - -if (nmode2) -{ - l3 = pow(COMPAT_TEXTURE(NtscPass, pC4 -off2).rgb, gamma_in.xxx); - l2 = pow(COMPAT_TEXTURE(NtscPass, pC4 -offx).rgb, gamma_in.xxx); - l1 = pow(COMPAT_TEXTURE(NtscPass, pC4 ).rgb, gamma_in.xxx); - r1 = pow(COMPAT_TEXTURE(NtscPass, pC4 +offx).rgb, gamma_in.xxx); - r2 = pow(COMPAT_TEXTURE(NtscPass, pC4 +off2).rgb, gamma_in.xxx); - r3 = pow(COMPAT_TEXTURE(NtscPass, pC4 +offx+off2).rgb, gamma_in.xxx); -} -else -{ - 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; - cc = pow(COMPAT_TEXTURE(NtscPass, pC5).rgb, gamma_in.xxx); -} - - 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(abs(rwl1), pc); twr1 = pow(abs(rwr1), pc); - twl2 = pow(abs(rwl2), pl); twr2 = pow(abs(rwr2), pr); - float wmax = max(twl1, twr1); - float sharp_ei = s_sharp*pow(zero, pc)/wmax; - twl2 = max(twl2/wmax - sharp_ei, mix(-0.12, 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.12, 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); - if (nmode1 && !interb) color2 = min(normalize (color2+eps) * length(sqrt(color2 * cc)) ,1.0); - - color2 = pow(color2, vec3(scangamma/gamma_in)); - -if (ntscbm) -{ - l2 = COMPAT_TEXTURE(PrePass0, pC4 -offx).rgb; - l1 = COMPAT_TEXTURE(PrePass0, pC4 ).rgb; - r1 = COMPAT_TEXTURE(PrePass0, pC4 +offx).rgb; - r2 = COMPAT_TEXTURE(PrePass0, pC4 +off2).rgb; -} - - lm2 = max(max(l2.r,l2.g),l2.b); - lm1 = max(max(l1.r,l1.g),l1.b); - rm1 = max(max(r1.r,r1.g),r1.b); - rm2 = max(max(r2.r,r2.g),r2.b); - -if (ntscbm) -{ - lm2 = pow(lm2, gamma_in); - lm1 = pow(lm1, gamma_in); - rm1 = pow(rm1, gamma_in); - rm2 = pow(rm2, gamma_in); -} - - swl2 = max(twl2, 0.0) * (lm2+ts); - swl1 = twl1 * (lm1+ts); - swr1 = twr1 * (rm1+ts); - swr2 = max(twr2, 0.0) * (rm2+ts); - - float fscolor2 = (lm2*swl2 + lm1*swl1 + rm1*swr1 + rm2*swr2)/(swl2+swl1+swr1+swr2); - mcolor2 = vec3(fscolor2); - scolor2 = vec3(clamp(mix(max(max(color2.r,color2.g),color2.b), fscolor2, spike), 0.0, 1.0)); -} - - vec3 ctmp; vec3 mcolor; float w3; vec3 color; - vec3 one = vec3(1.0); - -if (!interb) -{ - // calculating scanlines - - 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); - mcolor = clamp(mix(ctmp, mcolor, 1.5), 0.0, 1.0); - - 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.5) { 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 - { 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); - w3 = wf1+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(max(1.0-w3*w3, 2.5*f1), 1.0); - float ds2 = min(max(1.0-w3*w3, 2.5*f2), 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); -} - - if (interb) - { - color = gc(color1); - mcolor = clamp(mix(color1, mcolor1, 1.25), 0.0, 1.0); - } - - float mx = max(max(mcolor.r,mcolor.g),mcolor.b); - mx = pow(mx, 1.20/gamma_in); - - // Apply Mask - - vec3 orig1 = color; - vec3 cmask = one; - - vec2 maskcoord = gl_FragCoord.yx * 1.000001; - if (notate) maskcoord = maskcoord.yx; - - float smask = SlotMask(maskcoord, mx); - cmask*= Mask(maskcoord, 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; - float maxb = COMPAT_TEXTURE(BloomPass, pos).a; - float vig = COMPAT_TEXTURE(PrePass0, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; - - 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.60)); - - if (halation > 0.025) { - Bloom = mix(0.5*(Bloom + Bloom*Bloom), Bloom*Bloom, colmx); - color = color + (1.1-0.25*colmx)*(0.75+maxb)*Bloom*(0.75 + 0.70*pow(colmx,0.33333))*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.35 + 0.4*maxb)*halation; } - - color = pow(color, vec3(gamma_in/scangamma)); - - Glow = mix(Glow, 0.25*color, 0.7*colmx); - if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } - - color = min(color, 1.0); - - color = pow(color, vec3(1.0/gamma_out)); - - FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir)), corner(pos0)); -} diff --git a/crt/shaders/guest/advanced/crt-guest-advanced.slang b/crt/shaders/guest/advanced/crt-guest-advanced.slang index ed08394..a11dae3 100644 --- a/crt/shaders/guest/advanced/crt-guest-advanced.slang +++ b/crt/shaders/guest/advanced/crt-guest-advanced.slang @@ -3,7 +3,7 @@ /* CRT - Guest - Advanced - Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com Incorporates many good ideas and suggestions from Dr. Venom. I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. @@ -27,8 +27,7 @@ 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, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, spike; + h_sharp, s_sharp, csize, bsize1, warpX, warpY, glow, vertmask, spike, ring; } params; layout(std140, set = 0, binding = 0) uniform UBO @@ -41,22 +40,19 @@ layout(std140, set = 0, binding = 0) uniform UBO 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; + float overscanX; float overscanY; + float c_shape; float intres; float prescalex; - float c_shape; - float barspeed; - float barintensity; - float bardir; + float scan_falloff; + float rolling_scan; + float bloom_dist; + float scangamma; } global; @@ -65,9 +61,15 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 #define glow params.glow // Glow Strength -#pragma parameter bloom " Bloom Strength" 0.0 0.0 2.0 0.05 +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 #define bloom global.bloom // bloom effect +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + #pragma parameter halation " Halation Strength" 0.0 0.0 2.0 0.025 #define halation global.halation // halation effect @@ -85,16 +87,16 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter gsl " Scanline Type" 0.0 -1.0 2.0 1.0 #define gsl params.gsl // Alternate scanlines -#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 0.0 20.0 0.5 +#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 -20.0 20.0 0.5 #define scanline1 params.scanline1 // scanline param, vertical sharpness -#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 0.0 40.0 1.0 +#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 0.0 70.0 1.0 #define scanline2 params.scanline2 // scanline param, vertical sharpness -#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 3.5 0.05 +#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 5.0 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 +#pragma parameter beam_max " Scanline Shape Bright Pixels" 1.00 0.4 3.5 0.025 #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 @@ -103,17 +105,21 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 +#pragma parameter scans " Scanline Saturation / Mask Falloff" 0.60 0.0 2.5 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 scan_falloff " Scanline Falloff" 1.0 0.25 2.0 0.05 +#define scan_falloff global.scan_falloff // scanline falloff #pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 #define spike params.spike +#pragma parameter rolling_scan " Rolling Scanlines" 0.0 -1.0 1.0 0.01 +#define rolling_scan global.rolling_scan // rolling scanlines + +#pragma parameter scangamma " Scanline Gamma" 2.40 0.5 5.0 0.05 +#define scangamma global.scangamma + #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.10 @@ -122,13 +128,16 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 +#pragma parameter ring " Substractive sharpness Ringing" 0.0 0.0 3.0 0.05 +#define ring params.ring // substractive sharpness ringing + +#pragma parameter smart_ei " Smart Edges Effect Strength" 0.0 0.0 0.75 0.01 #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 +#pragma parameter ei_limit " Smart Edges Effect Strength Limit" 0.25 0.0 0.75 0.01 #define ei_limit global.ei_limit // smart edge handling -#pragma parameter sth " Smart Edges Smoothing Threshold" 0.20 0.0 1.0 0.01 +#pragma parameter sth " Smart Edges Smoothing Threshold" 0.23 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 @@ -147,18 +156,6 @@ layout(std140, set = 0, binding = 0) uniform UBO #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.25 0.01 -#define csize params.csize // corner size - -#pragma parameter bsize " Border smoothness" 600.0 100.0 700.0 10.0 -#define bsize params.bsize // border smoothness - -#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 - -#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 - -#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 - #pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 #define warpX params.warpX // Curvature X @@ -168,55 +165,15 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 #define c_shape global.c_shape // curvature shape -#pragma parameter overscanY " Overscan Y original pixels" 0.0 -50.0 50.0 1.0 +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 #define overscanY global.overscanY // OverscanY pixels #pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 #define prescalex global.prescalex // prescale-x factor -#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 mcut " Mask 5-7 Low Strength" 1.10 0.0 2.0 0.05 -#define mcut params.mcut // Mask 5-7 dark color strength - -#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 4.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 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 Bright Pixels" 0.0 0.0 1.0 0.05 -#define slotmask params.slotmask - -#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 -#define slotmask1 params.slotmask1 - -#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 4.0 1.0 -#define slotms global.slotms // Slot Mask Size - -#pragma parameter mclip " Keep Mask effect with clipping" 0.50 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 #define COMPAT_TEXTURE(c,d) texture(c,d) #define TEX0 vTexCoord @@ -232,7 +189,7 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + vTexCoord = TexCoord * 1.0001; } #pragma stage fragment @@ -240,9 +197,7 @@ 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; -layout(set = 0, binding = 6) uniform sampler2D PrePass; +layout(set = 0, binding = 4) uniform sampler2D PrePass; #define eps 1e-10 @@ -250,6 +205,11 @@ float st(float x) { return exp2(-10.0*x*x); } + +float st1(float x) +{ + return exp2(-7.0*x*x); +} float sw0(float x, float color, float scanline) { @@ -275,147 +235,6 @@ float sw2(float x, float color, float scanline) 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 + slotmask1) == 0.0) return 1.0; - else - { - 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; - } -} vec2 Warp(vec2 pos) { @@ -430,34 +249,6 @@ vec2 Overscan(vec2 pos, float dx, float dy){ return pos*0.5+0.5; } -float humbar(float pos) -{ - if (global.barintensity == 0.0) return 1.0; else - { - pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); - pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); - pos = (global.barintensity < 0.0) ? pos : (1.0-pos); - return (1.0-global.barintensity) + global.barintensity*pos; - } -} - -// Borrowed from cgwg's crt-geom, under GPL - -float corner(vec2 coord) -{ - coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); - vec2 cdist = vec2(max(csize/3.0, 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) { @@ -466,21 +257,16 @@ vec3 gc(vec3 c) return c * mg/(mc + eps); } -vec3 plant (vec3 tar, float r) -{ - float t = max(max(tar.r,tar.g),tar.b) + 0.00001; - return tar * r / t; -} void main() { - vec4 SourceSize = global.OriginalSize * vec4(prescalex, 1.0, 1.0/prescalex, 1.0); + vec4 SourceSize = global.OriginalSize * mix( vec4(prescalex, 1.0, 1.0/prescalex, 1.0), vec4(1.0, prescalex, 1.0, 1.0/prescalex), TATE); float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.5,0.5)).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 interb = (intera < 0.5); bool notate = (TATE < 0.5); float SourceY = mix(SourceSize.y, SourceSize.x, TATE); @@ -493,7 +279,7 @@ void main() // Calculating texel coordinates vec2 texcoord = TEX0.xy; - if (IOS > 0.0){ + if (IOS > 0.0 && !interb){ vec2 ofactor = OutputSize.xy/SourceSize.xy; vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); vec2 diff = ofactor/intfactor; @@ -505,12 +291,11 @@ void main() float factor = 1.00 + (1.0-0.5*OS)*BLOOM/100.0 - lum*BLOOM/100.0; texcoord = Overscan(texcoord, factor, factor); - texcoord = Overscan(texcoord, 1.0, (SourceSize.y - overscanY)/SourceSize.y); + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); vec2 pos = Warp(texcoord); - vec2 pos0 = Warp(TEX0.xy); - bool smarte = (smart_ei > 0.0 && notate); // smart edge interpolation on / off + bool smarte = (smart_ei > 0.01 && notate); // smart edge interpolation on / off vec2 coffset = vec2(0.5, 0.5); @@ -564,32 +349,23 @@ void main() float fp1 = 1.-fpx; float twl3 = max(wl3 - sharp1, 0.0); - float twl2 = max(wl2 - sharp1, mix(-0.12, 0.0, 1.0-fp1*fp1)); float scl2 = max(twl2, 0.0); + float twl2 = max(wl2 - sharp1, mix(-0.12, 0.0, 1.0-fp1*fp1)); 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 scr2 = max(twr2, 0.0); + float twr2 = max(wr2 - sharp1, mix(-0.12, 0.0, 1.0-fpx*fpx)); 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); + c1 = COMPAT_TEXTURE(AvgLumPass, pC4 ).xyz; + c2 = COMPAT_TEXTURE(AvgLumPass, pC4 + offy).xyz; + c1 = max(c1 - sth, 0.0); + c2 = max(c2 - sth, 0.0); } vec3 l3, l2, l1, r1, r2, r3, sl2, sl1, sr1, sr2, color1, color2, colmin, colmax; @@ -606,38 +382,35 @@ void main() 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(abs(rwl1), pc); twr1 = pow(abs(rwr1), pc); - twl2 = pow(abs(rwl2), pl); twr2 = pow(abs(rwr2), pr); - float wmax = max(twl1, twr1); - float sharp_ei = s_sharp*pow(zero, pc)/wmax; - twl2 = max(twl2/wmax - sharp_ei, mix(-0.12, 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.12, 0.0, 1.0-fpx*fpx)); + float pc = min(smart_ei*c1.y, ei_limit); + float pl = min(smart_ei*max(c1.y,c1.x), ei_limit); + float pr = min(smart_ei*max(c1.y,c1.z), ei_limit); + twl1 = max(wl1-pc, 0.01*wl1); twr1 = max(wr1-pc, 0.01*wr1); + twl2 = max(wl2-pl, 0.01*wl2); twr2 = max(wr2-pr, 0.01*wr2); } + 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); - float ts = 0.033; + if (sharp) color1 = clamp(mix(clamp(color1, colmin, colmax), color1, ring), 0.0, 1.0); + float ts = 0.025; + vec3 luma = vec3(0.2126, 0.7152, 0.0722); float lm2 = max(max(l2.r,l2.g),l2.b); float lm1 = max(max(l1.r,l1.g),l1.b); float rm1 = max(max(r1.r,r1.g),r1.b); float rm2 = max(max(r2.r,r2.g),r2.b); - float swl2 = max(twl2, 0.0) * (lm2+ts); - float swl1 = twl1 * (lm1+ts); - float swr1 = twr1 * (rm1+ts); - float swr2 = max(twr2, 0.0) * (rm2+ts); + float swl2 = max(twl2, 0.0) * (dot(l2,luma) + ts); + float swl1 = twl1 * (dot(l1,luma) + ts); + float swr1 = twr1 * (dot(r1,luma) + ts); + float swr2 = max(twr2, 0.0) * (dot(r2,luma) + ts); float fscolor1 = (lm2*swl2 + lm1*swl1 + rm1*swr1 + rm2*swr2)/(swl2+swl1+swr1+swr2); - vec3 mcolor1 = vec3(fscolor1); vec3 scolor1 = vec3(clamp(mix(max(max(color1.r,color1.g),color1.b), fscolor1, spike), 0.0, 1.0)); + + if(!interb) color1 = pow(color1, vec3(scangamma/gamma_in)); - vec3 scolor2, mcolor2; + vec3 scolor2; if (!interb) { @@ -655,38 +428,35 @@ void main() 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(abs(rwl1), pc); twr1 = pow(abs(rwr1), pc); - twl2 = pow(abs(rwl2), pl); twr2 = pow(abs(rwr2), pr); - float wmax = max(twl1, twr1); - float sharp_ei = s_sharp*pow(zero, pc)/wmax; - twl2 = max(twl2/wmax - sharp_ei, mix(-0.12, 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.12, 0.0, 1.0-fpx*fpx)); - } + float pc = min(smart_ei*c2.y, ei_limit); + float pl = min(smart_ei*max(c2.y,c2.x), ei_limit); + float pr = min(smart_ei*max(c2.y,c2.z), ei_limit); + twl1 = max(wl1-pc, 0.01*wl1); twr1 = max(wr1-pc, 0.01*wr1); + twl2 = max(wl2-pl, 0.01*wl2); twr2 = max(wr2-pr, 0.01*wr2); + } + 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); + if (sharp) color2 = clamp(mix(clamp(color2, colmin, colmax), color2, ring), 0.0, 1.0); lm2 = max(max(l2.r,l2.g),l2.b); lm1 = max(max(l1.r,l1.g),l1.b); rm1 = max(max(r1.r,r1.g),r1.b); rm2 = max(max(r2.r,r2.g),r2.b); - swl2 = max(twl2, 0.0) * (lm2+ts); - swl1 = twl1 * (lm1+ts); - swr1 = twr1 * (rm1+ts); - swr2 = max(twr2, 0.0) * (rm2+ts); + swl2 = max(twl2, 0.0) * (dot(l2,luma) + ts); + swl1 = twl1 * (dot(l1,luma) + ts); + swr1 = twr1 * (dot(r1,luma) + ts); + swr2 = max(twr2, 0.0) * (dot(r2,luma) + ts); float fscolor2 = (lm2*swl2 + lm1*swl1 + rm1*swr1 + rm2*swr2)/(swl2+swl1+swr1+swr2); - mcolor2 = vec3(fscolor2); scolor2 = vec3(clamp(mix(max(max(color2.r,color2.g),color2.b), fscolor2, spike), 0.0, 1.0)); + + color2 = pow(color2, vec3(scangamma/gamma_in)); + } - vec3 ctmp; vec3 mcolor; float w3; vec3 color; + vec3 ctmp = color1; float w3 = 1.0; vec3 color = color1; vec3 one = vec3(1.0); if (!interb) @@ -701,41 +471,44 @@ if (!interb) 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); - mcolor = clamp(mix(ctmp, mcolor, 1.25), 0.0, 1.0); 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); + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = pow(max(max(cref1.r,cref1.g),cref1.b), scan_falloff); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = pow(max(max(cref2.r,cref2.g),cref2.b), scan_falloff); float f1 = f; float f2 = 1.0-f; - + + float scanpix = mix(SourceSize.x/OutputSize.x, SourceSize.y/OutputSize.y, float(notate)); + + f1 = fract(f1 + rolling_scan*float(global.FrameCount)*scanpix); + f2 = 1.0 - f1; + if (gsl < 0.5) { 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 { 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); - w3 = wf1+wf2; + w3 = wf1+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); + float mc1 = max(max(color1.r,color1.g),color1.b) + eps; + float mc2 = max(max(color2.r,color2.g),color2.b) + eps; + + cref1 = color1 / mc1; cref1=cref1*cref1; cref1*=cref1; + cref2 = color2 / mc2; cref2=cref2*cref2; cref2*=cref2; - w1 = mix(w1*mix(one, cref1*cref1*cref1, scans), w1, wf1); - w2 = mix(w2*mix(one, cref2*cref2*cref2, scans), w2, wf2); + w1 = max( mix(w1*mix(one, cref1, scans), w1, wf1*min((1.0+0.15*scans), 1.2)), 0.0); w1 = min(w1*color1, mc1)/(color1 + eps); + w2 = max( mix(w2*mix(one, cref2, scans), w2, wf2*min((1.0+0.15*scans), 1.2)), 0.0); w2 = min(w2*color2, mc2)/(color2 + eps); + + // Scanline Deconvergence vec3 cd1 = one; vec3 cd2 = one; float vm = sqrt(abs(vertmask)); @@ -758,6 +531,14 @@ if (!interb) } color = gc(color1)*w1*cd1 + gc(color2)*w2*cd2; + + if (abs(rolling_scan) > 0.005) + { + wt1 = st1(f); + wt2 = st1(1.0-f); + color00 = (color1*wt1 + color2*wt2)/(wt1+wt2); + color = gc(color00) * mix(w1+w2, w3.xxx, max(wf1,wf2)); + } color = min(color, 1.0); } @@ -765,58 +546,11 @@ if (!interb) if (interb) { color = gc(color1); - mcolor = clamp(mix(color1, mcolor1, 1.25), 0.0, 1.0); } - float mx = max(max(mcolor.r,mcolor.g),mcolor.b); - mx = pow(mx, 1.20/gamma_in); + float colmx = pow(max(max(ctmp.r,ctmp.g),ctmp.b), 1.40/gamma_in); + + if(!interb) color = pow( color, vec3(gamma_in/scangamma) ); - // Apply Mask - - vec3 orig1 = color; - vec3 cmask = one; - - vec2 maskcoord = gl_FragCoord.yx * 1.000001; - if (notate) maskcoord = maskcoord.yx; - - float smask = SlotMask(maskcoord, mx); - cmask*= Mask(maskcoord, 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; - float maxb = COMPAT_TEXTURE(BloomPass, pos).a; - float vig = COMPAT_TEXTURE(PrePass, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; - - 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.60)); - - if (halation > 0.025) { - Bloom = mix(0.5*(Bloom + Bloom*Bloom), Bloom*Bloom, colmx); - color = color + (1.1-0.25*colmx)*(0.75+maxb)*Bloom*(0.75 + 0.70*pow(colmx,0.33333))*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.35 + 0.4*maxb)*halation; } - - Glow = mix(Glow, 0.25*color, 0.7*colmx); - if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } - - color = min(color, 1.0); - - color = pow(color, vec3(1.0/gamma_out)); - - FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir)), corner(pos0)); + FragColor = vec4(color, colmx); } diff --git a/crt/shaders/guest/advanced/custom-fast-sharpen.slang b/crt/shaders/guest/advanced/custom-fast-sharpen.slang index 0fa6dbe..b5e9e0a 100644 --- a/crt/shaders/guest/advanced/custom-fast-sharpen.slang +++ b/crt/shaders/guest/advanced/custom-fast-sharpen.slang @@ -3,7 +3,7 @@ /* Fast Sharpen Shader (Custom) - Copyright (C) 2005 - 2019 guest(r) - guest.r@gmail.com + Copyright (C) 2005 - 2022 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 @@ -26,16 +26,16 @@ layout(push_constant) uniform Push vec4 OriginalSize; vec4 OutputSize; uint FrameCount; - float SHARPEN, CONTR, DETAILS; + float CSHARPEN, CCONTR, CDETAILS; } params; -#pragma parameter SHARPEN "Sharpen strength" 0.00 0.0 4.00 0.10 -#pragma parameter CONTR "Ammount of sharpening" 0.05 0.0 0.25 0.01 -#pragma parameter DETAILS "Details sharpened " 1.00 0.0 1.00 0.05 +#pragma parameter CSHARPEN "Sharpen strength" 0.00 0.0 5.00 0.10 +#pragma parameter CCONTR "Ammount of sharpening" 0.05 0.0 0.25 0.01 +#pragma parameter CDETAILS "Details sharpened " 1.00 0.0 1.00 0.05 -#define SHARPEN params.SHARPEN -#define CONTR params.CONTR -#define DETAILS params.DETAILS +#define CSHARPEN params.CSHARPEN +#define CCONTR params.CCONTR +#define CDETAILS params.CDETAILS layout(std140, set = 0, binding = 0) uniform UBO { @@ -71,13 +71,13 @@ void main() vec3 b11 = 0.5*(c01+c21); float contrast = max(max(c11.r,c11.g),c11.b); - contrast = mix(2.0*CONTR, CONTR, contrast); + contrast = mix(2.0*CCONTR, CCONTR, contrast); vec3 mn1 = min(c01,c21); mn1 = min(mn1,c11*(1.0-contrast)); vec3 mx1 = max(c01,c21); mx1 = max(mx1,c11*(1.0+contrast)); vec3 dif = pow(mx1-mn1+0.0001, vec3(0.75,0.75,0.75)); - vec3 sharpen = mix(vec3(SHARPEN*DETAILS), vec3(SHARPEN), dif); + vec3 sharpen = mix(vec3(CSHARPEN*CDETAILS), vec3(CSHARPEN), dif); c11 = clamp(mix(c11,b11,-sharpen), mn1,mx1); diff --git a/crt/shaders/guest/advanced/deconvergence-ntsc.slang b/crt/shaders/guest/advanced/deconvergence-ntsc.slang new file mode 100644 index 0000000..e2d2487 --- /dev/null +++ b/crt/shaders/guest/advanced/deconvergence-ntsc.slang @@ -0,0 +1,697 @@ +#version 450 + +/* + CRT - Guest - Advanced + + Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. + + 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 IOS, OS, BLOOM, brightboost, brightboost1, csize, bsize1, warpX, warpY, glow, shadowMask, masksize, + slotmask, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, mshift, mask_layout, mask_bloom; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float bloom; + float halation; + float slotms; + float mclip; + float mask_gamma; + float gamma_out; + float overscanX; + float overscanY; + float intres; + float prescalex; + float c_shape; + float barspeed; + float barintensity; + float bardir; + float sborder; + float bloom_dist; + float deconr; + float decons; + float addnoised; + float noiseresd; + float noisetype; + float deconrr; + float deconrg; + float deconrb; + float deconrry; + float deconrgy; + float deconrby; + float dctypex; + float dctypey; + float post_br; +} global; + + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + +#pragma parameter halation " Halation Strength" 0.0 0.0 2.0 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.25 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#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.25 0.005 +#define csize params.csize // corner size + +#pragma parameter bsize1 " Border Size" 0.01 0.0 3.0 0.01 +#define bsize1 params.bsize1 // border Size + +#pragma parameter sborder " Border Intensity" 0.75 0.25 2.0 0.05 +#define sborder global.sborder // border intensity + +#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 + +#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 + +#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 +#define c_shape global.c_shape // curvature shape + +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 +#define prescalex global.prescalex // prescale-x factor + +#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-12:'Trinitron'" 0.0 -1.0 12.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-12)" 0.3 -0.5 1.0 0.025 +#define maskstr params.maskstr // Mask Strength + +#pragma parameter mcut " Mask 5-12 Low Strength" 1.10 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-12 dark color strength + +#pragma parameter masksize " CRT Mask Size" 1.0 1.0 4.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 mshift " Mask Shift/Stagger" 0.0 -8.0 8.0 1.0 +#define mshift params.mshift // mask 'line' shift/stagger + +#pragma parameter mask_layout " Mask Layout: RGB or BGR (check LCD panel) " 0.0 0.0 1.0 1.0 +#define mask_layout params.mask_layout // mask layout: RGB or BGR + +#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 Bright Pixels" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask + +#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 +#define slotmask1 params.slotmask1 + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 8.0 1.0 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1..." 1.0 1.0 4.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 4.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.50 0.0 1.0 0.05 +#define mclip global.mclip // + +#pragma parameter gamma_out "Gamma out" 1.95 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + + +#pragma parameter bogus_deconvergence11 "[ HORIZONTAL/VERTICAL DECONVERGENCE ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter dctypex " Deconvergence type X : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 + +#pragma parameter dctypey " Deconvergence type Y : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 + +#pragma parameter deconrr " Horizontal Deconvergence Red Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrg " Horizontal Deconvergence Green Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrb " Horizontal Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrry " Vertical Deconvergence Red Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrgy " Vertical Deconvergence Green Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrby " Vertical Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter decons " Deconvergence Strength" 1.0 0.0 3.0 0.10 + +#pragma parameter addnoised " Add Noise" 0.0 -1.0 1.0 0.02 + +#pragma parameter noiseresd " Noise Resolution" 2.0 1.0 10.0 1.0 + +#pragma parameter noisetype " Noise Type: Colored, Luma" 0.0 0.0 1.0 1.0 + +#pragma parameter post_br " Post Brightness" 1.0 0.25 5.0 0.01 + + +#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.0001; +} + +#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; +layout(set = 0, binding = 6) uniform sampler2D PrePass0; +layout(set = 0, binding = 7) uniform sampler2D Source; + +#define eps 1e-10 + +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + vec2 pos0 = pos; + pos.y = floor(pos.y/masksize); + float next_line = float(fract(pos.y*0.5) > 0.25); + pos0.x = (mshift > -0.25) ? (pos0.x + next_line * mshift) : (pos0.x + pos.y * mshift); + pos = floor(pos0/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.4, 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.49) { 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.49) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.49) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.49) + { 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.3) mask.r = 1.0; + else if (pos.x < 0.6) 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) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.49) + { mask = 0.0.xxx; + } + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 8 + else if (shadowMask == 8.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask = 1.0.xxx; + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Magenta - Green - Black mask + else if (shadowMask == 9.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask.rb = 1.0.xx; + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RGBX + else if (shadowMask == 10.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask = 0.0.xxx; + else if (pos.x < 0.4) mask.r = 1.0; + else if (pos.x < 0.7) 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; + } + + // 4k mask + else if (shadowMask == 11.0) + { + mask = vec3(0.0); + 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; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RRGGBBX mask + else + { + mask = vec3(0.0); + pos.x = floor(mod(pos.x,7.0)); + if (pos.x < 1.0) mask = 0.0.xxx; + else if (pos.x < 3.0) mask.r = 1.0; + else if (pos.x < 5.0) 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; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if ((slotmask + slotmask1) == 0.0) return 1.0; + else + { + 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; + } +} + +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); + 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; +} + +float humbar(float pos) +{ + if (global.barintensity == 0.0) return 1.0; else + { + pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); + pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); + pos = (global.barintensity < 0.0) ? pos : (1.0-pos); + return (1.0-global.barintensity) + global.barintensity*pos; + } +} + + +float corner(vec2 pos) { + vec2 b = vec2(bsize1, bsize1) * vec2(1.0, OutputSize.x/OutputSize.y) * 0.05; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + float csize1 = mix(400.0, 7.0, pow(4.0*csize, 0.10)); + float crn = dot(pow(pos, csize1.xx), vec2(1.0, OutputSize.y/OutputSize.x)); + crn = (csize == 0.0) ? max(pos.x, pos.y) : pow(crn, 1.0/csize1); + pos = max(pos, crn); + vec2 res = (bsize1 == 0.0) ? 1.0.xx : mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, sborder.xx); + return sqrt(res.x*res.y); +} + + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +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; +} + +// noise function: +// Dedicated to the public domain. +// If you want a real license, you may consider this MIT/BSD/CC0/WTFPL-licensed (take your pick). +// Adapted from ChuckNorris - shadertoy: https://www.shadertoy.com/view/XtK3Dz + +vec3 noise(vec3 v){ + if (global.addnoised < 0.0) v.z = -global.addnoised; else v.z = mod(v.z,6001.0)/1753.0; + // ensure reasonable range + v = fract(v) + fract(v*1e4) + fract(v*1e-4); + // seed + v += vec3(0.12345, 0.6789, 0.314159); + // more iterations => more random + v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); + return v; +} + +void fetch_pixel (inout vec3 c, inout vec3 b, vec2 coord, vec2 bcoord) +{ + float stepx = OutputSize.z; + float stepy = OutputSize.w; + + float ds = global.decons; + + vec2 dx = vec2(stepx, 0.0); + vec2 dy = vec2(0.0, stepy); + + float posx = 2.0*coord.x - 1.0; + float posy = 2.0*coord.y - 1.0; + + if (global.dctypex > 0.025) + { + posx = sign(posx)*pow(abs(posx), 1.05-global.dctypex); + dx = posx * dx; + } + + if (global.dctypey > 0.025) + { + + posy = sign(posy)*pow(abs(posy), 1.05-global.dctypey); + dy = posy * dy; + } + + // if (global.dctypex > 0.025 || global.dctypey > 0.025) ds *= sqrt(posx*posx*sign(global.dctypex) + posy*posy*sign(global.dctypey)); + + vec2 rc = global.deconrr * dx + global.deconrry*dy; + vec2 gc = global.deconrg * dx + global.deconrgy*dy; + vec2 bc = global.deconrb * dx + global.deconrby*dy; + + float r1 = COMPAT_TEXTURE(Source, coord + rc).r; + float g1 = COMPAT_TEXTURE(Source, coord + gc).g; + float b1 = COMPAT_TEXTURE(Source, coord + bc).b; + + vec3 d = vec3(r1, g1, b1); + c = clamp(mix(c, d, ds), 0.0, 1.0); + + r1 = COMPAT_TEXTURE(BloomPass, bcoord + rc).r; + g1 = COMPAT_TEXTURE(BloomPass, bcoord + gc).g; + b1 = COMPAT_TEXTURE(BloomPass, bcoord + bc).b; + + d = vec3(r1, g1, b1); + b = clamp(mix(b, d, ds), 0.0, 1.0); +} + + +void main() +{ + vec4 SourceSize = global.OriginalSize * vec4(prescalex, 1.0, 1.0/prescalex, 1.0); + + float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.5,0.5)).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.5); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + if (IOS > 0.0 && !interb){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = diff.y; + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = vec2(TEX0.x, texcoord.y); + } + + float factor = 1.00 + (1.0-0.5*OS)*BLOOM/100.0 - lum*BLOOM/100.0; + texcoord = Overscan(texcoord, factor, factor); + + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); + + vec2 pos1 = TEX0.xy; + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + vec3 color0 = COMPAT_TEXTURE(Source,pos1).rgb; + float c0 = max(max(color0.r, color0.g),color0.b); + + // color and bloom fetching + vec3 color = COMPAT_TEXTURE(Source,pos1).rgb; + vec3 Bloom = COMPAT_TEXTURE(BloomPass, pos).rgb; + fetch_pixel(color, Bloom, pos1, pos); + + float cm = max(max(color.r,color.g),color.b); + float mx1 = COMPAT_TEXTURE(Source, pos1 ).a; + float colmx = max(mx1, cm); + float w3 = min((c0 + 0.0005) / (pow(colmx, gamma_in/1.4) + 0.0005), 1.0); + + vec2 dx = vec2(0.001, 0.0); + float mx0 = COMPAT_TEXTURE(Source, pos1 - dx).a; + float mx2 = COMPAT_TEXTURE(Source, pos1 + dx).a; + float mx = max(max(mx0,mx1),max(mx2,cm)); + + vec3 one = vec3(1.0); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + vec2 maskcoord = gl_FragCoord.xy * 1.000001; + + float smask = SlotMask(maskcoord, mx); + cmask*= Mask(maskcoord, mx); + + if (mask_layout > 0.5) cmask = cmask.rbg; + + vec3 cmask1 = cmask; + float smask1 = smask; + + if (mask_bloom > 0.025) + { + float maxb = max(max(Bloom.r,Bloom.g),Bloom.b); + maxb = pow(sqrt(maxb*mix(maxb, colmx, 0.75)),0.275); + vec3 mBloom = 0.5*(1.5*Bloom+0.5*maxb) * mix(1.0, 2.0-colmx, (bloom_dist + 0.5)); + float maskmx = 1.0; if (shadowMask > 0.5 || shadowMask < 4.5) maskmx = maskLight; else if (shadowMask > 6.5 && shadowMask < 10.5) maskmx = 1.0; else maskmx = max(max(cmask.r,cmask.g),cmask.b); + cmask = min(cmask + maxb*mBloom*mask_bloom, maskmx); + smask = min(smask + 0.9*maxb*max(max(mBloom.r,mBloom.g),mBloom.b)*mask_bloom, 1.0); + } + + 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); + cmask1 = min(cmask1*smask1, 1.0); + + 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 Ref = COMPAT_TEXTURE(LinearizePass, pos).rgb; + float maxb = COMPAT_TEXTURE(BloomPass, pos).a; + float vig = COMPAT_TEXTURE(PrePass0, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; + + vec3 Bloom1 = Bloom; + + if (bloom < -0.01) Bloom1 = plant(Bloom, maxb); + + Bloom1 = min(Bloom1*(orig1+color), max(0.5*(colmx + orig1 - color),0.001*Bloom1)); + Bloom1 = 0.5*(Bloom1 + mix(Bloom1, mix(colmx*orig1, Bloom1, 0.5), 1.0-color)); + + Bloom1 = Bloom1 * mix(1.0, 2.0-colmx, bloom_dist); + + color = color + abs(bloom) * Bloom1; + + color = min(color, mix(one, cmask1, mclip)); + + if (!interb) color = declip(color, mix(1.0, w3, 0.6)); else w3 = 1.0; + + if (halation > 0.01) { + Bloom = mix(0.5*(Bloom + Bloom*Bloom), 0.75*Bloom*Bloom, colmx); + color = color + 2.0*max((2.0*mix(maxb*maxb, maxb, colmx)-0.5*max(max(Ref.r,Ref.g),Ref.b)),0.25)*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.6)*Bloom*halation; } + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } + + color = min(color, 1.0); + + color = pow(color, vec3(1.0/gamma_out)); + + float rc = 0.6*sqrt(max(max(color.r, color.g), color.b))+0.4; + + if (abs(global.addnoised) > 0.01) + { + vec3 noise0 = noise(vec3(floor(OutputSize.xy * vTexCoord / global.noiseresd), float(global.FrameCount))); + if (global.noisetype < 0.5) color = mix(color, noise0, 0.25*abs(global.addnoised) * rc); + else color = min(color * mix(1.0, 1.5*noise0.x, 0.5*abs(global.addnoised)), 1.0); + } + + FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir))*global.post_br*corner(pos0), 1.0); +} diff --git a/crt/shaders/guest/advanced/deconvergence.slang b/crt/shaders/guest/advanced/deconvergence.slang index 2ce761f..33a47df 100644 --- a/crt/shaders/guest/advanced/deconvergence.slang +++ b/crt/shaders/guest/advanced/deconvergence.slang @@ -1,9 +1,12 @@ #version 450 /* - CRT - Guest - Advanced - Deconvergence pass + noise + CRT - Guest - Advanced - Copyright (C) 2021 guest(r) - guest.r@gmail.com + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,63 +26,208 @@ layout(push_constant) uniform Push { + float TATE, IOS, OS, BLOOM, brightboost, brightboost1, csize, bsize1, warpX, warpY, glow, shadowMask, masksize, + slotmask, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, mshift, mask_layout, mask_bloom; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; vec4 OutputSize; uint FrameCount; + float bloom; + float halation; + float slotms; + float mclip; + float mask_gamma; + float gamma_out; + float overscanX; + float overscanY; + float intres; + float prescalex; + float c_shape; + float barspeed; + float barintensity; + float bardir; + float sborder; + float bloom_dist; float deconr; - float TATE; float decons; float addnoised; + float noisetype; float noiseresd; - float shadowMask; - float masksize; float deconrr; float deconrg; float deconrb; float deconrry; float deconrgy; float deconrby; - float deconsmooth; float dctypex; - float dctypey; -} params; + float dctypey; + float post_br; +} global; + + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + +#pragma parameter halation " Halation Strength" 0.0 0.0 2.0 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.25 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#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.25 0.005 +#define csize params.csize // corner size + +#pragma parameter bsize1 " Border Size" 0.01 0.0 3.0 0.01 +#define bsize1 params.bsize1 // border Size + +#pragma parameter sborder " Border Intensity" 0.75 0.25 2.0 0.05 +#define sborder global.sborder // border intensity + +#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 + +#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 + +#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 +#define c_shape global.c_shape // curvature shape + +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 +#define prescalex global.prescalex // prescale-x factor + +#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-12:'Trinitron'" 0.0 -1.0 12.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-12)" 0.3 -0.5 1.0 0.025 +#define maskstr params.maskstr // Mask Strength + +#pragma parameter mcut " Mask 5-12 Low Strength" 1.10 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-12 dark color strength + +#pragma parameter masksize " CRT Mask Size" 1.0 1.0 4.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 mshift " Mask Shift/Stagger" 0.0 -8.0 8.0 1.0 +#define mshift params.mshift // mask 'line' shift/stagger + +#pragma parameter mask_layout " Mask Layout: RGB or BGR (check LCD panel) " 0.0 0.0 1.0 1.0 +#define mask_layout params.mask_layout // mask layout: RGB or BGR + +#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 Bright Pixels" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask + +#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 +#define slotmask1 params.slotmask1 + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 8.0 1.0 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1..." 1.0 1.0 4.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 4.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.50 0.0 1.0 0.05 +#define mclip global.mclip // + +#pragma parameter gamma_out "Gamma out" 2.4 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + + #pragma parameter bogus_deconvergence11 "[ HORIZONTAL/VERTICAL DECONVERGENCE ]: " 0.0 0.0 1.0 1.0 -#pragma parameter dctypex " Deconvergence type X : 0.0 - static, other - dynamic" 0.0 0.0 1.0 0.05 +#pragma parameter dctypex " Deconvergence type X : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 -#pragma parameter dctypey " Deconvergence type Y : 0.0 - static, other - dynamic" 0.0 0.0 1.0 0.05 +#pragma parameter dctypey " Deconvergence type Y : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 -#pragma parameter deconrr " Horizontal Deconvergence Red Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrr " Horizontal Deconvergence Red Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrg " Horizontal Deconvergence Green Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrg " Horizontal Deconvergence Green Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrb " Horizontal Deconvergence Blue Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrb " Horizontal Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrry " Vertical Deconvergence Red Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrry " Vertical Deconvergence Red Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrgy " Vertical Deconvergence Green Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrgy " Vertical Deconvergence Green Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrby " Vertical Deconvergence Blue Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrby " Vertical Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 -#pragma parameter decons " Deconvergence Strength (and Type)" 0.5 -4.0 4.0 0.10 -#define decons params.decons // Horizontal deconvergence colors strength - -#pragma parameter deconsmooth " Deconvergence Smoothing" 0.0 0.0 1.0 0.10 +#pragma parameter decons " Deconvergence Strength" 1.0 0.0 3.0 0.10 #pragma parameter addnoised " Add Noise" 0.0 -1.0 1.0 0.02 -#define addnoised params.addnoised // add noise -#pragma parameter noiseresd " Noise Resolution" 2.0 0.0 10.0 1.0 -#define noiseresd params.noiseresd // add noise +#pragma parameter noiseresd " Noise Resolution" 2.0 1.0 10.0 1.0 + +#pragma parameter noisetype " Noise Type: Colored, Luma" 0.0 0.0 1.0 1.0 + +#pragma parameter post_br " Post Brightness" 1.0 0.25 5.0 0.01 -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; -} global; +#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; @@ -89,16 +237,269 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord; + 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 Source; +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; +layout(set = 0, binding = 6) uniform sampler2D PrePass; +layout(set = 0, binding = 7) uniform sampler2D Source; -#define COMPAT_TEXTURE(c,d) texture(c,d) +#define eps 1e-10 +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + vec2 pos0 = pos; + pos.y = floor(pos.y/masksize); + float next_line = float(fract(pos.y*0.5) > 0.25); + pos0.x = (mshift > -0.25) ? (pos0.x + next_line * mshift) : (pos0.x + pos.y * mshift); + pos = floor(pos0/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.4, 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.49) { 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.49) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.49) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.49) + { 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.3) mask.r = 1.0; + else if (pos.x < 0.6) 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) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.49) + { mask = 0.0.xxx; + } + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 8 + else if (shadowMask == 8.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask = 1.0.xxx; + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Magenta - Green - Black mask + else if (shadowMask == 9.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask.rb = 1.0.xx; + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RGBX + else if (shadowMask == 10.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask = 0.0.xxx; + else if (pos.x < 0.4) mask.r = 1.0; + else if (pos.x < 0.7) 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; + } + + // 4k mask + else if (shadowMask == 11.0) + { + mask = vec3(0.0); + 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; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RRGGBBX mask + else + { + mask = vec3(0.0); + pos.x = floor(mod(pos.x,7.0)); + if (pos.x < 1.0) mask = 0.0.xxx; + else if (pos.x < 3.0) mask.r = 1.0; + else if (pos.x < 5.0) 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; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if ((slotmask + slotmask1) == 0.0) return 1.0; + else + { + 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; + } +} + +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); + 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; +} + +float humbar(float pos) +{ + if (global.barintensity == 0.0) return 1.0; else + { + pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); + pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); + pos = (global.barintensity < 0.0) ? pos : (1.0-pos); + return (1.0-global.barintensity) + global.barintensity*pos; + } +} + + +float corner(vec2 pos) { + vec2 b = vec2(bsize1, bsize1) * vec2(1.0, OutputSize.x/OutputSize.y) * 0.05; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + float csize1 = mix(400.0, 7.0, pow(4.0*csize, 0.10)); + float crn = dot(pow(pos, csize1.xx), vec2(1.0, OutputSize.y/OutputSize.x)); + crn = (csize == 0.0) ? max(pos.x, pos.y) : pow(crn, 1.0/csize1); + pos = max(pos, crn); + vec2 res = (bsize1 == 0.0) ? 1.0.xx : mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, sborder.xx); + return sqrt(res.x*res.y); +} + + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +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; +} // noise function: // Dedicated to the public domain. @@ -106,7 +507,7 @@ layout(set = 0, binding = 2) uniform sampler2D Source; // Adapted from ChuckNorris - shadertoy: https://www.shadertoy.com/view/XtK3Dz vec3 noise(vec3 v){ - if (addnoised < 0.0) v.z = -addnoised; else v.z = v.z/6000.0; + if (global.addnoised < 0.0) v.z = -global.addnoised; else v.z = mod(v.z,6001.0)/1753.0; // ensure reasonable range v = fract(v) + fract(v*1e4) + fract(v*1e-4); // seed @@ -114,85 +515,190 @@ vec3 noise(vec3 v){ // more iterations => more random v = fract(v*dot(v, v)*123.456); v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); v = fract(v*dot(v, v)*123.456); return v; -} +} -void main() +void fetch_pixel (inout vec3 c, inout vec3 b, vec2 coord, vec2 bcoord) { - - vec3 color = COMPAT_TEXTURE(Source, vTexCoord).rgb; - vec3 result = color; - - if ((abs(params.deconrr) + abs(params.deconrg) + abs(params.deconrb) + abs(params.deconrry) + abs(params.deconrgy) + abs(params.deconrby)) > 0.20) - { - float step = 1.0; - float dstep = step; - step*= (TATE < 0.5) ? (params.OutputSize.z) : (params.OutputSize.w); - float stepy = (TATE < 0.5) ? (params.OutputSize.w) : (params.OutputSize.z); - float stepx = (TATE < 0.5) ? (params.OutputSize.z) : (params.OutputSize.w); + float stepx = OutputSize.z; + float stepy = OutputSize.w; - vec2 sx = mix(vec2(stepx, 0.0), vec2(0.0, stepx), TATE); - - float ds = decons; + float ds = global.decons; - vec2 dx = (TATE < 0.5) ? vec2(step, 0.0) : vec2(0.0, step); - vec2 dy = (TATE > 0.5) ? vec2(stepy, 0.0) : vec2(0.0, stepy); + vec2 dx = vec2(stepx, 0.0); + vec2 dy = vec2(0.0, stepy); - float posx = 2.0*vTexCoord.x - 1.0; - float posy = 2.0*vTexCoord.y - 1.0; + float posx = 2.0*coord.x - 1.0; + float posy = 2.0*coord.y - 1.0; - if (params.dctypex > 0.025) + if (global.dctypex > 0.025) { - posx = sign(posx)*pow(abs(posx), 1.05-params.dctypex); + posx = sign(posx)*pow(abs(posx), 1.05-global.dctypex); dx = posx * dx; } - if (params.dctypey > 0.025) + if (global.dctypey > 0.025) { - posy = sign(posy)*pow(abs(posy), 1.05-params.dctypey); + posy = sign(posy)*pow(abs(posy), 1.05-global.dctypey); dy = posy * dy; } - if (params.dctypex > 0.025 || params.dctypey > 0.025) ds *= sqrt(posx*posx*sign(params.dctypex) + posy*posy*sign(params.dctypey)); + // if (global.dctypex > 0.025 || global.dctypey > 0.025) ds *= sqrt(posx*posx*sign(global.dctypex) + posy*posy*sign(global.dctypey)); - vec2 rc = params.deconrr * dx + params.deconrry*dy; - vec2 gc = params.deconrg * dx + params.deconrgy*dy; - vec2 bc = params.deconrb * dx + params.deconrby*dy; + vec2 rc = global.deconrr * dx + global.deconrry*dy; + vec2 gc = global.deconrg * dx + global.deconrgy*dy; + vec2 bc = global.deconrb * dx + global.deconrby*dy; - dx = (dx+dy) * params.deconsmooth; + float r1 = COMPAT_TEXTURE(Source, coord + rc).r; + float g1 = COMPAT_TEXTURE(Source, coord + gc).g; + float b1 = COMPAT_TEXTURE(Source, coord + bc).b; + + vec3 d = vec3(r1, g1, b1); + c = clamp(mix(c, d, ds), 0.0, 1.0); - float r1 = COMPAT_TEXTURE(Source, vTexCoord + rc ).r; - float g1 = COMPAT_TEXTURE(Source, vTexCoord + gc ).g; - float b1 = COMPAT_TEXTURE(Source, vTexCoord + bc ).b; + r1 = COMPAT_TEXTURE(BloomPass, bcoord + rc).r; + g1 = COMPAT_TEXTURE(BloomPass, bcoord + gc).g; + b1 = COMPAT_TEXTURE(BloomPass, bcoord + 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; + d = vec3(r1, g1, b1); + b = clamp(mix(b, d, ds), 0.0, 1.0); +} - 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); - result = (result1+result2+result3)/3.0; - - vec3 dcolor = max(max(COMPAT_TEXTURE(Source, vTexCoord + sx).rgb, COMPAT_TEXTURE(Source, vTexCoord - sx).rgb), color); - - float mc = max(max(dcolor.r, dcolor.g), dcolor.b); - if (decons < 0.0) mc = 0.9; +void main() +{ + vec4 SourceSize = global.OriginalSize * vec4(prescalex, 1.0, 1.0/prescalex, 1.0); - result = clamp(mix(color, sqrt(mix(result*result, color*result, sqrt(mc))), abs(ds)), min(result,color), max(result, color)); + float lum = COMPAT_TEXTURE(AvgLumPass, vec2(0.5,0.5)).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.5); + bool notate = (TATE < 0.5); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + if (IOS > 0.0 && !interb){ + 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); + + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); + + vec2 pos1 = TEX0.xy; + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + vec3 color0 = COMPAT_TEXTURE(Source,pos1).rgb; + float c0 = max(max(color0.r, color0.g),color0.b); + + // color and bloom fetching + vec3 color = COMPAT_TEXTURE(Source,pos1).rgb; + vec3 Bloom = COMPAT_TEXTURE(BloomPass, pos).rgb; + + fetch_pixel(color, Bloom, pos1, pos); // deconvergence + + float cm = max(max(color.r,color.g),color.b); + float mx1 = COMPAT_TEXTURE(Source, pos1 ).a; + float colmx = max(mx1, cm); + float w3 = min((c0 + 0.0005) / (pow(colmx, gamma_in/1.4) + 0.0005), 1.0); + + vec2 dx = mix(vec2(0.001, 0.0), vec2(0.0, 0.001), TATE); + float mx0 = COMPAT_TEXTURE(Source, pos1 - dx).a; + float mx2 = COMPAT_TEXTURE(Source, pos1 + dx).a; + float mx = max(max(mx0,mx1),max(mx2,cm)); + + vec3 one = vec3(1.0); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + vec2 maskcoord = gl_FragCoord.yx * 1.000001; + if (notate) maskcoord = maskcoord.yx; + + float smask = SlotMask(maskcoord, mx); + cmask*= Mask(maskcoord, mx); + + if (mask_layout > 0.5) cmask = cmask.rbg; + + vec3 cmask1 = cmask; + float smask1 = smask; + + if (mask_bloom > 0.025) + { + float maxb = max(max(Bloom.r,Bloom.g),Bloom.b); + maxb = pow(sqrt(maxb*mix(maxb, colmx, 0.75)),0.275); + vec3 mBloom = 0.5*(1.5*Bloom+0.5*maxb) * mix(1.0, 2.0-colmx, (bloom_dist + 0.5)); + float maskmx = 1.0; if (shadowMask > 0.5 || shadowMask < 4.5) maskmx = maskLight; else if (shadowMask > 6.5 && shadowMask < 10.5) maskmx = 1.0; else maskmx = max(max(cmask.r,cmask.g),cmask.b); + cmask = min(cmask + maxb*mBloom*mask_bloom, maskmx); + smask = min(smask + 0.9*maxb*max(max(mBloom.r,mBloom.g),mBloom.b)*mask_bloom, 1.0); } - float rc = 0.6*sqrt(max(max(result.r, result.g), result.b))+0.4; + 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); + cmask1 = min(cmask1*smask1, 1.0); + + 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 Ref = COMPAT_TEXTURE(LinearizePass, pos).rgb; + float maxb = COMPAT_TEXTURE(BloomPass, pos).a; + float vig = COMPAT_TEXTURE(PrePass, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; + + vec3 Bloom1 = Bloom; + + if (bloom < -0.01) Bloom1 = plant(Bloom, maxb); + + Bloom1 = min(Bloom1*(orig1+color), max(0.5*(colmx + orig1 - color),0.001*Bloom1)); + Bloom1 = 0.5*(Bloom1 + mix(Bloom1, mix(colmx*orig1, Bloom1, 0.5), 1.0-color)); + + Bloom1 = Bloom1 * mix(1.0, 2.0-colmx, bloom_dist); + + color = color + abs(bloom) * Bloom1; + + color = min(color, mix(one, cmask1, mclip)); + + if (!interb) color = declip(color, mix(1.0, w3, 0.6)); else w3 = 1.0; + + if (halation > 0.01) { + Bloom = mix(0.5*(Bloom + Bloom*Bloom), 0.75*Bloom*Bloom, colmx); + color = color + 2.0*max((2.0*mix(maxb*maxb, maxb, colmx)-0.5*max(max(Ref.r,Ref.g),Ref.b)),0.25)*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.6)*Bloom*halation; } + + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } + + color = min(color, 1.0); - if (abs(addnoised) > 0.01) result = mix(result, noise(vec3(floor(params.OutputSize.xy * vTexCoord / noiseresd), float(params.FrameCount))), 0.25*abs(addnoised) * rc); + color = pow(color, vec3(1.0/gamma_out)); + + float rc = 0.6*sqrt(max(max(color.r, color.g), color.b))+0.4; - float corner = COMPAT_TEXTURE(Source, vTexCoord).a; + if (abs(global.addnoised) > 0.01) + { + vec3 noise0 = noise(vec3(floor(OutputSize.xy * vTexCoord / global.noiseresd), float(global.FrameCount))); + if (global.noisetype < 0.5) color = mix(color, noise0, 0.25*abs(global.addnoised) * rc); + else color = min(color * mix(1.0, 1.5*noise0.x, 0.5*abs(global.addnoised)), 1.0); + } - FragColor = vec4(result*corner, 1.0); -} \ No newline at end of file + FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir))*global.post_br*corner(pos0), 1.0); +} diff --git a/crt/shaders/guest/advanced/grade/pre-shaders-afterglow-grade.slang b/crt/shaders/guest/advanced/grade/pre-shaders-afterglow-grade.slang new file mode 100644 index 0000000..46bbdfd --- /dev/null +++ b/crt/shaders/guest/advanced/grade/pre-shaders-afterglow-grade.slang @@ -0,0 +1,929 @@ +#version 450 + +layout(push_constant) uniform Push +{ + float g_gamma_in; + float g_gamma_out; + float g_signal_type; + float g_crtgamut; + float g_space_out; + float g_hue_degrees; + float g_I_SHIFT; + float g_Q_SHIFT; + float g_I_MUL; + float g_Q_MUL; + float g_lum_fix; + float g_vignette; + float g_vstr; + float g_vpower; + float g_sat; + float g_vibr; + float g_lum; + float g_cntrst; + float g_mid; + float g_lift; + float blr; + float blg; + float blb; + float wlr; + float wlg; + float wlb; + float rg; + float rb; + float gr; + float gb; + float br; + float bg; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float wp_temperature; + float g_satr; + float g_satg; + float g_satb; + float LUT_Size1; + float LUT1_toggle; + float LUT_Size2; + float LUT2_toggle; + float AS, asat; +} global; + +/* + Grade + > Ubershader grouping some monolithic color related shaders: + ::color-mangler (hunterk), ntsc color tuning knobs (Doriphor), white_point (hunterk, Dogway), RA Reshade LUT. + > and the addition of: + ::analogue color emulation, phosphor gamut, color space + TRC support, vibrance, HUE vs SAT, vignette (shared by Syh), black level, rolled gain and sigmoidal contrast. + + Author: Dogway + License: Public domain + + **Thanks to those that helped me out keep motivated by continuous feedback and bug reports: + **Syh, Nesguy, hunterk, and the libretro forum members. + + + ######################################...PRESETS...####################################### + ########################################################################################## + ### ### + ### PAL ### + ### Phosphor: EBU (#3) (or an EBU T3213 based CRT phosphor gamut) ### + ### WP: D65 (6504K) (in practice more like ~7500K) ### + ### TRC: 2.8 SMPTE-C Gamma ### + ### Saturation: -0.02 ### + ### ### + ### NTSC-U ### + ### Phosphor: P22/SMPTE-C (#1 #-1)(or a SMPTE-C based CRT phosphor gamut) ### + ### WP: D65 (6504K) (in practice more like ~7500K) ### + ### TRC: 2.22 SMPTE-C Gamma (in practice more like 2.35-2.55) ### + ### ### + ### NTSC-J (Default) ### + ### Phosphor: NTSC-J (#2) (or a NTSC-J based CRT phosphor gamut) ### + ### WP: 9300K+27MPCD (8942K) (CCT from x:0.281 y:0.311) ### + ### TRC: 2.22 SMPTE-C Gamma (in practice more like 2.35-2.55) ### + ### ### + ### *Despite the standard of 2.22, a more faithful approximation to CRT... ### + ### ...is to use a gamma (SMPTE-C type) with a value of 2.35-2.55. ### + ### ### + ### ### + ########################################################################################## + ########################################################################################## +*/ + +#pragma parameter AS " Afterglow Strength" 0.20 0.0 0.60 0.01 +#define AS global.AS +#pragma parameter asat " Afterglow saturation" 0.33 0.0 1.0 0.01 +#define asat global.asat + + +#pragma parameter g_gamma_in "Game Embedded Gamma" 2.222 1.80 3.0 0.05 +#pragma parameter g_gamma_out "CRT Electron Gun Gamma" 2.50 1.80 3.0 0.05 +#pragma parameter g_signal_type "Signal Type (0:RGB 1:Composite)" 1.0 0.0 1.0 1.0 +#pragma parameter g_crtgamut "Phosphor (1:NTSC-U 2:NTSC-J 3:PAL)" 2.0 -4.0 3.0 1.0 +#pragma parameter g_space_out "Diplay Color Space (-1:709 0:sRGB 1:DCI 2:2020 3:Adobe)" 0.0 -1.0 3.0 1.0 + +#pragma parameter g_hue_degrees "Hue" 0.0 -360.0 360.0 1.0 +#pragma parameter g_I_SHIFT "I/U Shift" 0.0 -0.2 0.2 0.01 +#pragma parameter g_Q_SHIFT "Q/V Shift" 0.0 -0.2 0.2 0.01 +#pragma parameter g_I_MUL "I/U Multiplier" 1.0 0.0 2.0 0.01 +#pragma parameter g_Q_MUL "Q/V Multiplier" 1.0 0.0 2.0 0.01 +#pragma parameter g_lum_fix "Sega Luma Fix" 0.0 0.0 1.0 1.0 +#pragma parameter g_vignette "Vignette Toggle" 1.0 0.0 1.0 1.0 +#pragma parameter g_vstr "Vignette Strength" 40.0 0.0 50.0 1.0 +#pragma parameter g_vpower "Vignette Power" 0.20 0.0 0.5 0.01 +#pragma parameter g_lum "Brightness" 0.0 -0.5 1.0 0.01 +#pragma parameter g_cntrst "Contrast" 0.0 -1.0 1.0 0.05 +#pragma parameter g_mid "Contrast Pivot" 0.5 0.0 1.0 0.01 +#pragma parameter wp_temperature "White Point" 6504.0 5004.0 12004.0 100.0 +#pragma parameter g_sat "Saturation" 0.0 -1.0 2.0 0.01 +#pragma parameter g_vibr "Dullness/Vibrance" 0.0 -1.0 1.0 0.05 +#pragma parameter g_satr "Hue vs Sat Red" 0.0 -1.0 1.0 0.01 +#pragma parameter g_satg "Hue vs Sat Green" 0.0 -1.0 1.0 0.01 +#pragma parameter g_satb "Hue vs Sat Blue" 0.0 -1.0 1.0 0.01 +#pragma parameter g_lift "Black Level" 0.0 -0.5 0.5 0.01 +#pragma parameter blr "Black-Red Tint" 0.0 0.0 1.0 0.01 +#pragma parameter blg "Black-Green Tint" 0.0 0.0 1.0 0.01 +#pragma parameter blb "Black-Blue Tint" 0.0 0.0 1.0 0.01 +#pragma parameter wlr "White-Red Tint" 1.0 0.0 2.0 0.01 +#pragma parameter wlg "White-Green Tint" 1.0 0.0 2.0 0.01 +#pragma parameter wlb "White-Blue Tint" 1.0 0.0 2.0 0.01 +#pragma parameter rg "Red-Green Tint" 0.0 -1.0 1.0 0.005 +#pragma parameter rb "Red-Blue Tint" 0.0 -1.0 1.0 0.005 +#pragma parameter gr "Green-Red Tint" 0.0 -1.0 1.0 0.005 +#pragma parameter gb "Green-Blue Tint" 0.0 -1.0 1.0 0.005 +#pragma parameter br "Blue-Red Tint" 0.0 -1.0 1.0 0.005 +#pragma parameter bg "Blue-Green Tint" 0.0 -1.0 1.0 0.005 +#pragma parameter LUT_Size1 "LUT Size 1" 16.0 8.0 64.0 16.0 +#pragma parameter LUT1_toggle "LUT 1 Toggle" 0.0 0.0 1.0 1.0 +#pragma parameter LUT_Size2 "LUT Size 2" 64.0 0.0 64.0 16.0 +#pragma parameter LUT2_toggle "LUT 2 Toggle" 0.0 0.0 1.0 1.0 + +#define M_PI 3.1415926535897932384626433832795 +#define gamma_in params.g_gamma_in +#define gamma_out params.g_gamma_out +#define signal params.g_signal_type +#define crtgamut params.g_crtgamut +#define SPC params.g_space_out +#define hue_degrees params.g_hue_degrees +#define I_SHIFT params.g_I_SHIFT +#define Q_SHIFT params.g_Q_SHIFT +#define I_MUL params.g_I_MUL +#define Q_MUL params.g_Q_MUL +#define lum_fix params.g_lum_fix +#define vignette params.g_vignette +#define vstr params.g_vstr +#define vpower params.g_vpower +#define g_sat params.g_sat +#define vibr params.g_vibr +#define satr global.g_satr +#define satg global.g_satg +#define satb global.g_satb +#define lum params.g_lum +#define cntrst params.g_cntrst +#define mid params.g_mid +#define lift params.g_lift +#define blr params.blr +#define blg params.blg +#define blb params.blb +#define wlr params.wlr +#define wlg params.wlg +#define wlb params.wlb +#define rg params.rg +#define rb params.rb +#define gr params.gr +#define gb params.gb +#define br params.br +#define bg params.bg + +#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 StockPass; +layout(set = 0, binding = 4) uniform sampler2D AfterglowPass; +layout(set = 0, binding = 5) uniform sampler2D SamplerLUT1; +layout(set = 0, binding = 6) uniform sampler2D SamplerLUT2; + + +///////////////////////// Color Space Transformations ////////////////////////// + + + +vec3 XYZ_to_RGB(vec3 XYZ, float CSPC){ + + // to sRGB + const mat3x3 sRGB = mat3x3( + 3.24081254005432130, -0.969243049621582000, 0.055638398975133896, + -1.53730857372283940, 1.875966310501098600, -0.204007431864738460, + -0.49858659505844116, 0.041555050760507584, 1.057129383087158200); + + // to DCI-P3 -D65- + const mat3x3 DCIP3 = mat3x3( + 2.49339652061462400, -0.82948720455169680, 0.035850685089826584, + -0.93134605884552000, 1.76266026496887200, -0.076182708144187930, + -0.40269458293914795, 0.023624641820788383, 0.957014024257659900); + + // to Rec.2020 + const mat3x3 rec2020 = mat3x3( + 1.71660947799682620, -0.66668272018432620, 0.017642205581068993, + -0.35566213726997375, 1.61647748947143550, -0.042776308953762054, + -0.25336012244224550, 0.01576850563287735, 0.942228555679321300); + + // to AdobeRGB + const mat3x3 Adobe = mat3x3( + 2.0415899753570557, -0.96924000978469850, 0.013439999893307686, + -0.5650100111961365, 1.87597000598907470, -0.118359997868537900, + -0.3447299897670746, 0.04156000167131424, 1.015169978141784700); + + return (CSPC == 3.0) ? Adobe * XYZ : (CSPC == 2.0) ? rec2020 * XYZ : (CSPC == 1.0) ? DCIP3 * XYZ : sRGB * XYZ; +} + +vec3 RGB_to_XYZ(vec3 RGB, float CSPC){ + + // from sRGB + const mat3x3 sRGB = mat3x3( + 0.41241079568862915, 0.21264933049678802, 0.019331756979227066, + 0.35758456587791443, 0.71516913175582890, 0.119194857776165010, + 0.18045382201671600, 0.07218152284622192, 0.950390160083770800); + + // from DCI-P3 -D65- + const mat3x3 DCIP3 = mat3x3( + 0.48659050464630127, 0.22898375988006592, 0.00000000000000000, + 0.26566821336746216, 0.69173991680145260, 0.04511347413063049, + 0.19819043576717377, 0.07927616685628891, 1.04380297660827640); + + // from Rec.2020 + const mat3x3 rec2020 = mat3x3( + 0.63697350025177000, 0.24840137362480164, 0.00000000000000000, + 0.15294560790061950, 0.67799961566925050, 0.04253686964511871, + 0.11785808950662613, 0.03913172334432602, 1.06084382534027100); + + // from AdobeRGB + const mat3x3 Adobe = mat3x3( + 0.57666999101638790, 0.2973400056362152, 0.02703000046312809, + 0.18556000292301178, 0.6273599863052368, 0.07068999856710434, + 0.18822999298572540, 0.0752900019288063, 0.9913399815559387); + + return (CSPC == 3.0) ? Adobe * RGB : (CSPC == 2.0) ? rec2020 * RGB : (CSPC == 1.0) ? DCIP3 * RGB : sRGB * RGB; +} + + +vec3 XYZtoYxy(vec3 XYZ){ + + float XYZrgb = XYZ.r+XYZ.g+XYZ.b; + float Yxyg = (XYZrgb <= 0.0) ? 0.3805 : XYZ.r / XYZrgb; + float Yxyb = (XYZrgb <= 0.0) ? 0.3769 : XYZ.g / XYZrgb; + return vec3(XYZ.g, Yxyg, Yxyb); +} + +vec3 YxytoXYZ(vec3 Yxy){ + + float Xs = Yxy.r * (Yxy.g/Yxy.b); + float Xsz = (Yxy.r <= 0.0) ? 0.0 : 1.0; + vec3 XYZ = vec3(Xsz,Xsz,Xsz) * vec3(Xs, Yxy.r, (Xs/Yxy.g)-Xs-Yxy.r); + return XYZ; +} + +///////////////////////// White Point Mapping ///////////////////////// +// +// +// PAL: D65 NTSC-U: D65 NTSC-J: CCT NTSC-J NTSC-FCC: C +// PAL: 6504K NTSC-U: 6504K NTSC-J: 8942K NTSC-FCC: 6780K +// 0.3127 0.3290 0.3127 0.3290 0.281 0.311 0.310 0.316 + +vec3 wp_adjust(float temperature, vec3 color){ + + float temp3 = pow(10.,3.) / temperature; + float temp6 = pow(10.,6.) / pow(temperature, 2.); + float temp9 = pow(10.,9.) / pow(temperature, 3.); + + vec3 wp = vec3(1.); + + wp.x = (temperature <= 7000.) ? 0.244063 + 0.09911 * temp3 + 2.9678 * temp6 - 4.6070 * temp9 : \ + 0.237040 + 0.24748 * temp3 + 1.9018 * temp6 - 2.0064 * temp9 ; + + wp.y = -3.000 * pow(wp.x,2.) + 2.870 * wp.x - 0.275; + wp.z = 1. - wp.x - wp.y; + + const mat3x3 CAT02 = mat3x3( + 0.7328, 0.4296, -0.1624, + -0.70360, 1.6975, 0.0061, + 0.003, -0.0136, 0.9834); + + vec3 fw_trans = (vec3(wp.x/wp.y,1.,wp.z/wp.y) * CAT02) / (vec3(0.95045,1.,1.088917) * CAT02) ; + + return color.xyz * fw_trans.xyz ; + +} + +//////////////////////////////////////////////////////////////////////////////// + + +// Monitor Curve Functions: https://github.com/ampas/aces-dev +//---------------------------------------------------------------------- + + +float moncurve_f( float color, float gamma, float offs) +{ + // Forward monitor curve + color = clamp(color, 0.0, 1.0); + float fs = (( gamma - 1.0) / offs) * pow( offs * gamma / ( ( gamma - 1.0) * ( 1.0 + offs)), gamma); + float xb = offs / ( gamma - 1.0); + + color = ( color > xb) ? pow( ( color + offs) / ( 1.0 + offs), gamma) : color * fs; + return color; +} + + +vec3 moncurve_f_f3( vec3 color, float gamma, float offs) +{ + color.r = moncurve_f( color.r, gamma, offs); + color.g = moncurve_f( color.g, gamma, offs); + color.b = moncurve_f( color.b, gamma, offs); + return color.rgb; +} + + +float moncurve_r( float color, float gamma, float offs) +{ + // Reverse monitor curve + color = clamp(color, 0.0, 1.0); + float yb = pow( offs * gamma / ( ( gamma - 1.0) * ( 1.0 + offs)), gamma); + float rs = pow( ( gamma - 1.0) / offs, gamma - 1.0) * pow( ( 1.0 + offs) / gamma, gamma); + + color = ( color > yb) ? ( 1.0 + offs) * pow( color, 1.0 / gamma) - offs : color * rs; + return color; +} + + +vec3 moncurve_r_f3( vec3 color, float gamma, float offs) +{ + color.r = moncurve_r( color.r, gamma, offs); + color.g = moncurve_r( color.g, gamma, offs); + color.b = moncurve_r( color.b, gamma, offs); + return color.rgb; +} + + +//-------------------------- Luma Functions ---------------------------- + + +// Performs better in gamma encoded space +float contrast_sigmoid(float color, float cont, float pivot){ + + cont = pow(cont + 1., 3.); + + float knee = 1. / (1. + exp(cont * pivot)); + float shldr = 1. / (1. + exp(cont * (pivot - 1.))); + + color = (1. / (1. + exp(cont * (pivot - color))) - knee) / (shldr - knee); + + return color; +} + + +// Performs better in gamma encoded space +float contrast_sigmoid_inv(float color, float cont, float pivot){ + + cont = pow(cont - 1., 3.); + + float knee = 1. / (1. + exp (cont * pivot)); + float shldr = 1. / (1. + exp (cont * (pivot - 1.))); + + color = pivot - log(1. / (color * (shldr - knee) + knee) - 1.) / cont; + + return color; +} + + +float rolled_gain(float color, float gain){ + + float gx = abs(gain) + 0.001; + float anch = (gain > 0.0) ? 0.5 / (gx / 2.0) : 0.5 / gx; + color = (gain > 0.0) ? color * ((color - anch) / (1 - anch)) : color * ((1 - anch) / (color - anch)) * (1 - gain); + + return color; +} + + +vec4 rolled_gain_v4(vec4 color, float gain){ + + color.r = rolled_gain(color.r, gain); + color.g = rolled_gain(color.g, gain); + color.b = rolled_gain(color.b, gain); + + return vec4(color.rgb, 1.0); +} + + +float SatMask(float color_r, float color_g, float color_b) +{ + float max_rgb = max(color_r, max(color_g, color_b)); + float min_rgb = min(color_r, min(color_g, color_b)); + float msk = clamp((max_rgb - min_rgb) / (max_rgb + min_rgb), 0.0, 1.0); + return msk; +} + + +// This shouldn't be necessary but it seems some undefined values can +// creep in and each GPU vendor handles that differently. This keeps +// all values within a safe range +vec3 mixfix(vec3 a, vec3 b, float c) +{ + return (a.z < 1.0) ? mix(a, b, c) : a; +} + + +vec4 mixfix_v4(vec4 a, vec4 b, float c) +{ + return (a.z < 1.0) ? mix(a, b, c) : a; +} + + +//---------------------- Range Expansion/Compression ------------------- + + +// to Studio Swing/Broadcast Safe/SMPTE legal/Limited Range +vec3 PCtoTV(vec3 col, float luma_swing, float Umax, float Vmax, float max_swing, bool rgb_in) +{ + col *= 255.; + Umax = (max_swing == 1.0) ? Umax * 224. : Umax * 239.; + Vmax = (max_swing == 1.0) ? Vmax * 224. : Vmax * 239.; + + col.x = (luma_swing == 1.0) ? ((col.x * 219.) / 255.) + 16. : col.x; + col.y = (rgb_in == true) ? ((col.y * 219.) / 255.) + 16. : (((col.y - 128.) * (Umax * 2.)) / 255.) + Umax; + col.z = (rgb_in == true) ? ((col.z * 219.) / 255.) + 16. : (((col.z - 128.) * (Vmax * 2.)) / 255.) + Vmax; + return col.xyz / 255.; +} + + +// to Full Swing/Full Range +vec3 TVtoPC(vec3 col, float luma_swing, float Umax, float Vmax, float max_swing, bool rgb_in) +{ + col *= 255.; + Umax = (max_swing == 1.0) ? Umax * 224. : Umax * 239.; + Vmax = (max_swing == 1.0) ? Vmax * 224. : Vmax * 239.; + + float colx = (luma_swing == 1.0) ? ((col.x - 16.) / 219.) * 255. : col.x; + float coly = (rgb_in == true) ? ((col.y - 16.) / 219.) * 255. : (((col.y - Umax) / (Umax * 2.)) * 255.) + 128.; + float colz = (rgb_in == true) ? ((col.z - 16.) / 219.) * 255. : (((col.z - Vmax) / (Vmax * 2.)) * 255.) + 128.; + return vec3(colx,coly,colz) / 255.; +} + + +//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/ + + +//--------------------- ITU-R BT.470/601 (M) (1953) -------------------- + + +// FCC (Sanctioned) YIQ matrix +vec3 RGB_FCC(vec3 col) + { + const mat3 conv_mat = mat3( + 0.299996928307425, 0.590001575542717, 0.110001496149858, + 0.599002392519453, -0.277301256521204, -0.321701135998249, + 0.213001700342824, -0.525101205289350, 0.312099504946526); + + return col.rgb * conv_mat; + } + +// FCC (Sanctioned) YIQ matrix (inverse) +vec3 FCC_RGB(vec3 col) + { + const mat3 conv_mat = mat3( + 1.0000000, 0.946882217090069, 0.623556581986143, + 1.0000000, -0.274787646298978, -0.635691079187380, + 1.0000000, -1.108545034642030, 1.709006928406470); + + return col.rgb * conv_mat; + } + + +//--------------------- SMPTE RP 145 (C), 170M (1987) ------------------ + + +vec3 RGB_YIQ(vec3 col) + { + const mat3 conv_mat = mat3( + 0.2990, 0.5870, 0.1140, + 0.5959, -0.2746, -0.3213, + 0.2115, -0.5227, 0.3112); + + return col.rgb * conv_mat; + } + +vec3 YIQ_RGB(vec3 col) + { + const mat3 conv_mat = mat3( + 1.0000000, 0.956, 0.619, + 1.0000000, -0.272, -0.647, + 1.0000000, -1.106, 1.703); + + return col.rgb * conv_mat; + } + +//----------------------- ITU-R BT.470/601 (B/G) ----------------------- + + +vec3 r601_YUV(vec3 RGB) + { + const mat3 conv_mat = mat3( + 0.299000, 0.587000, 0.114000, + -0.147407, -0.289391, 0.436798, + 0.614777, -0.514799, -0.099978); + + return RGB.rgb * conv_mat; + } + +vec3 YUV_r601(vec3 RGB) + { + const mat3 conv_mat = mat3( + 1.0000000, 0.00000000000000000, 1.14025080204010000, + 1.0000000, -0.39393067359924316, -0.58080917596817020, + 1.0000000, 2.02839756011962900, -0.00000029356581166); + + return RGB.rgb * conv_mat; + } + +// Custom - not Standard +vec3 YUV_r709(vec3 YUV) + { + const mat3 conv_mat = mat3( + 1.0000000, 0.0000000000000000, 1.14025092124938960, + 1.0000000, -0.2047683298587799, -0.33895039558410645, + 1.0000001, 2.0283975601196290, 0.00000024094399364); + + return YUV.rgb * conv_mat; + } + +// Custom - not Standard +vec3 r709_YUV(vec3 RGB) + { + const mat3 conv_mat = mat3( + 0.2126000, 0.715200, 0.0722000, + -0.1048118, -0.3525936, 0.4574054, + 0.6905498, -0.6272304, -0.0633194); + + return RGB.rgb * conv_mat; + } + + +//------------------------- SMPTE-240M Y’PbPr -------------------------- + + +// Umax 0.886 +// Vmax 0.700 +// RGB to YPbPr -full to limited range- with Rec.601 primaries +vec3 r601_YCC(vec3 RGB) + { + const mat3 conv_mat = mat3( + 0.299, 0.587, 0.114, + -0.16873589164785553047, -0.33126410835214446953, 0.500, + 0.500, -0.41868758915834522111, -0.08131241084165477889); + + return RGB.rgb * conv_mat; + } + +// YPbPr to RGB -limited to full range- with Rec.601 primaries +vec3 YCC_r601(vec3 YUV) + { + const mat3 conv_mat = mat3( + 1.0000000, 0.000, 1.402, + 1.0000000, -0.34413628620102214651, -0.71413628620102214651, + 1.0000000, 1.772, 0.000); + + return YUV.rgb * conv_mat; + } + +// Umax 0.53890924768269023496443198965294 +// Vmax 0.63500127000254000508001016002032 +// RGB to YPbPr -full range in-gamut- with Rec.709 primaries +vec3 r709_YCC(vec3 RGB) + { + const mat3 conv_mat = mat3( + 0.2126, 0.7152, 0.0722, + -0.11457210605733994395, -0.38542789394266005605, 0.5000, + 0.5000, -0.45415290830581661163, -0.04584709169418338837); + + return RGB.rgb * conv_mat; + } + +// YPbPr to RGB -full range in-gamut- with Rec.709 primaries +vec3 YCC_r709(vec3 YUV) + { + const mat3 conv_mat = mat3( + 1.0000000, 0.00000000000000000000, 1.5748, + 1.0000000, -0.18732427293064876957, -0.46812427293064876957, + 1.0000000, 1.8556, 0.00000000000000000000); + + return YUV.rgb * conv_mat; + } + + +//------------------------- IPT -------------------------- + + +const mat3 LMS = +mat3( + 0.4002, 0.7076, -0.0808, +-0.2263, 1.1653, 0.0457, + 0.0, 0.0, 0.9182); + +const mat3 IPT = +mat3( + 0.4000, 0.4000, 0.2000, + 4.4550, -4.8510, 0.3960, + 0.8056, 0.3572, -1.1628); + + + +//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/ + + +// ITU-R BT.470/601 (M) (proof of concept, actually never used) +// SMPTE 170M-1999 +// NTSC-FCC 1953 Standard Phosphor (use with temperature C: 6780K) +const mat3 NTSC_FCC_transform = +mat3( + 0.60699284076690670, 0.2989666163921356, 0.00000000000000000, + 0.17344850301742554, 0.5864211320877075, 0.06607561558485031, + 0.20057128369808197, 0.1146121546626091, 1.11746847629547120); + +// ITU-R BT.470/601 (M) +// Conrac 7211N19 CRT Phosphor +const mat3 Conrac_transform = +mat3( + 0.55842006206512450, 0.28580552339553833, 0.03517606481909752, + 0.20613566040992737, 0.63714659214019780, 0.09369802474975586, + 0.18589359521865845, 0.07704800367355347, 0.96004259586334230); + +// NTSC-J (use with D93 white point) +// Sony Trinitron KV-20M20 +const mat3 Sony20_20_transform = +mat3( + 0.33989441394805910, 0.18490256369113922, 0.019034087657928467, + 0.33497872948646545, 0.71182984113693240, 0.149544075131416320, + 0.22866378724575043, 0.10326752066612244, 1.143318891525268600); + +// SMPTE-C - Measured Average Phosphor (1979-1994) +const mat3 P22_transform = +mat3( + 0.4665636420249939, 0.25661000609397890, 0.005832045804709196, + 0.3039233088493347, 0.66820019483566280, 0.105618737637996670, + 0.1799621731042862, 0.07518967241048813, 0.977465748786926300); + +// SMPTE RP 145-1994 (SMPTE-C), 170M-1999 +// SMPTE-C - Standard Phosphor (Rec.601 NTSC) +const mat3 SMPTE_transform = +mat3( + 0.39354196190834045, 0.21238772571086884, 0.01874009333550930, + 0.36525884270668030, 0.70106136798858640, 0.11193416267633438, + 0.19164848327636720, 0.08655092865228653, 0.95824241638183590); + +// SMPTE RP 145-1994 (SMPTE-C), 170M-1999 +// NTSC-J - Standard Phosphor (https://web.archive.org/web/20130413104152/http://arib.or.jp/english/html/overview/doc/4-TR-B09v1_0.pdf) +const mat3 NTSC_J_transform = +mat3( + 0.39603787660598755, 0.22429330646991730, 0.02050681784749031, + 0.31201449036598206, 0.67417418956756590, 0.12814880907535553, + 0.24496731162071228, 0.10153251141309738, 1.26512730121612550); + +// ITU-R BT.470/601 (B/G) +// EBU Tech.3213-E PAL - Standard Phosphor for Studio Monitors +const mat3 EBU_transform = +mat3( + 0.43194326758384705, 0.22272075712680817, 0.020247340202331543, + 0.34123489260673523, 0.70600330829620360, 0.129433929920196530, + 0.17818950116634370, 0.07127580046653748, 0.938464701175689700); + + + + +//*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/ + + + + + +void main() +{ + + vec4 imgColor = texture(StockPass, vTexCoord.xy); + vec4 aftglow = 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(asat)))*l; + + +// Retro Sega Systems: Genesis, 32x, CD and Saturn 2D had color palettes designed in TV levels to save on transformations. + float lum_exp = (lum_fix == 1.0) ? (255./239.) : 1.; + + // vec3 src = texture(Source, vTexCoord.xy).rgb * lum_exp; + vec3 src = imgColor.rgb * lum_exp; + +// Assumes framebuffer in Rec.601 with baked gamma +// make a YUV * NTSC Phosphor option too and a FCC * NTSC phosphor + vec3 col = (crtgamut == 3.0) ? r601_YUV(src) : \ + (crtgamut == 2.0) ? RGB_YIQ(src) : \ + (crtgamut == -3.0) ? RGB_FCC(src) : \ + (crtgamut == -4.0) ? RGB_FCC(src) : \ + RGB_YIQ(src) ; + + +// Clipping Logic / Gamut Limiting + vec2 UVmax = (crtgamut == 3.0) ? vec2(0.436798, 0.614777) : \ + (crtgamut == -4.0) ? vec2(0.599002392519453, 0.52510120528935) : \ + (crtgamut == -3.0) ? vec2(0.599002392519453, 0.52510120528935) : \ + vec2(0.5959, 0.5227) ; + + col = clamp(col.xyz, vec3(0.0, -UVmax.x, -UVmax.y), vec3(1.0, UVmax.x, UVmax.y)); + + + col = (crtgamut == 3.0) ? col : \ + (crtgamut == 2.0) ? col : \ + (crtgamut == -3.0) ? PCtoTV(col, 1.0, UVmax.x, UVmax.y, 1.0, false) : \ + (crtgamut == -4.0) ? PCtoTV(col, 1.0, UVmax.x, UVmax.y, 1.0, false) : \ + PCtoTV(col, 1.0, UVmax.x, UVmax.y, 1.0, false) ; + + +// YIQ/YUV Analogue Color Controls (HUE + Color Shift + Color Burst) + float hue_radians = hue_degrees * (M_PI / 180.0); + float hue = atan(col.z, col.y) + hue_radians; + float chroma = sqrt(col.z * col.z + col.y * col.y); + col = vec3(col.x, chroma * cos(hue), chroma * sin(hue)); + + col.y = (mod((col.y + 1.0) + I_SHIFT, 2.0) - 1.0) * I_MUL; + col.z = (mod((col.z + 1.0) + Q_SHIFT, 2.0) - 1.0) * Q_MUL; + + +// Back to RGB + col = (crtgamut == 3.0) ? col : \ + (crtgamut == 2.0) ? col : \ + (crtgamut == -3.0) ? TVtoPC(col, 1.0, UVmax.x, UVmax.y, 1.0, false) : \ + (crtgamut == -4.0) ? TVtoPC(col, 1.0, UVmax.x, UVmax.y, 1.0, false) : \ + TVtoPC(col, 1.0, UVmax.x, UVmax.y, 1.0, false) ; + + col = (crtgamut == 3.0) ? YUV_r601(col) : \ + (crtgamut == 2.0) ? YIQ_RGB(col) : \ + (crtgamut == -3.0) ? FCC_RGB(col) : \ + (crtgamut == -4.0) ? FCC_RGB(col) : \ + YIQ_RGB(col) ; + +// Gamut Limiting + col = r601_YCC(clamp(col, 0., 1.)); + col = (signal == 0.0) ? src : YCC_r601(clamp(col, vec3(0.0, -.886,-.700), vec3(1.0, .886,.700))); + + +// Developer baked CRT gamma (2.20 - 2.25) + col = moncurve_f_f3(col, gamma_in, 0.099); + +// CRT Phosphor Gamut + mat3 m_in; + + if (crtgamut == -4.0) { m_in = NTSC_FCC_transform; } else + if (crtgamut == -3.0) { m_in = Conrac_transform; } else + if (crtgamut == -2.0) { m_in = Sony20_20_transform; } else + if (crtgamut == -1.0) { m_in = SMPTE_transform; } else + if (crtgamut == 1.0) { m_in = P22_transform; } else + if (crtgamut == 2.0) { m_in = NTSC_J_transform; } else + if (crtgamut == 3.0) { m_in = EBU_transform; } + + vec3 gamut = m_in*col; + +// White Point Mapping + vec3 wp = (crtgamut == -4.0) ? wp_adjust(global.wp_temperature - (6404. - 6504.), gamut) : \ + (crtgamut == -3.0) ? wp_adjust(global.wp_temperature - (6504. - 6504.), gamut) : \ + (crtgamut == -2.0) ? wp_adjust(global.wp_temperature - (7600. - 6504.), gamut) : \ + (crtgamut == -1.0) ? wp_adjust(global.wp_temperature - (6504. - 6504.), gamut) : \ + (crtgamut == 1.0) ? wp_adjust(global.wp_temperature - (6504. - 6504.), gamut) : \ + (crtgamut == 2.0) ? wp_adjust(global.wp_temperature - (7400. - 6504.), gamut) : \ + (crtgamut == 3.0) ? wp_adjust(global.wp_temperature - (6504. - 6504.), gamut) : \ + wp_adjust(global.wp_temperature, gamut) ; + + vec3 adj = clamp(XYZ_to_RGB(wp, SPC), 0., 1.); + + +// Guest Emulated CRT Electron Gun gamma (2.35 - 2.50) (phosphor gamma brings it up back to ~2.222) + adj = moncurve_r_f3(crtgamut == 0.0 ? col : adj, pow(gamma_in, 2.) / gamma_out, 0.099); + + +// Look LUT - (in SPC space) + float red = (adj.r * (global.LUT_Size1 - 1.0) + 0.4999) / (global.LUT_Size1 * global.LUT_Size1); + float green = (adj.g * (global.LUT_Size1 - 1.0) + 0.4999) / global.LUT_Size1; + float blue1 = (floor(adj.b * (global.LUT_Size1 - 1.0)) / global.LUT_Size1) + red; + float blue2 = (ceil(adj.b * (global.LUT_Size1 - 1.0)) / global.LUT_Size1) + red; + float mixer = clamp(max((adj.b - blue1) / (blue2 - blue1), 0.0), 0.0, 32.0); + vec3 color1 = texture(SamplerLUT1, vec2(blue1, green)).rgb; + vec3 color2 = texture(SamplerLUT1, vec2(blue2, green)).rgb; + vec3 vcolor = (global.LUT1_toggle == 0.0) ? adj : mixfix(color1, color2, mixer); + + + +// OETF - Opto-Electronic Transfer Function (Rec.709 does a Dim to Dark Surround adaptation) + vcolor = (SPC == 3.0) ? clamp(pow(vcolor, vec3(563./256.)), 0., 1.) : \ + (SPC == 2.0) ? moncurve_f_f3(vcolor, 2.20 + 0.022222, 0.0993) : \ + (SPC == 1.0) ? clamp(pow(vcolor, vec3(2.20 + 0.40)), 0., 1.) : \ + (SPC == 0.0) ? moncurve_f_f3(vcolor, 2.20 + 0.20, 0.0550) : \ + clamp(pow(pow(vcolor, vec3(1./1.019264)), vec3(2.20 + 0.20)), 0., 1.) ; + + + vcolor = RGB_to_XYZ(vcolor, SPC); + + +// Sigmoidal Contrast + vec3 Yxy = XYZtoYxy(vcolor); + float toGamma = clamp(moncurve_r(Yxy.r, 2.40, 0.055), 0., 1.); + toGamma = (Yxy.r > 0.5) ? contrast_sigmoid_inv(toGamma, 2.3, 0.5) : toGamma; + float sigmoid = (cntrst > 0.0) ? contrast_sigmoid(toGamma, cntrst, mid) : contrast_sigmoid_inv(toGamma, cntrst, mid); + vec3 contrast = vec3(moncurve_f(sigmoid, 2.40, 0.055), Yxy.g, Yxy.b); + vec3 XYZsrgb = clamp(XYZ_to_RGB(YxytoXYZ(contrast), SPC), 0., 1.); + contrast = (cntrst == 0.0) ? XYZ_to_RGB(vcolor, SPC) : XYZsrgb; + + +// Vignetting & Black Level + vec2 vpos = vTexCoord*(global.OriginalSize.xy/global.SourceSize.xy); + + vpos *= 1.0 - vpos.xy; + float vig = vpos.x * vpos.y * vstr; + vig = min(pow(vig, vpower), 1.0); + contrast *= (vignette == 1.0) ? vig : 1.0; + + contrast += (lift / 20.0) * (1.0 - contrast); + + +// RGB Related Transforms + vec4 screen = vec4(max(contrast, 0.0), 1.0); + float sat = g_sat + 1.0; + + // r g b alpha ; alpha does nothing for our purposes + mat4 color = mat4(wlr, rg, rb, 0.0, //red tint + gr, wlg, gb, 0.0, //green tint + br, bg, wlb, 0.0, //blue tint + blr/20., blg/20., blb/20., 0.0); //black tint + + + vec3 coeff = (SPC == 3.0) ? vec3(0.29734000563621520, 0.62735998630523680, 0.07529000192880630) : \ + (SPC == 2.0) ? vec3(0.24840137362480164, 0.67799961566925050, 0.03913172334432602) : \ + (SPC == 1.0) ? vec3(0.22898375988006592, 0.69173991680145260, 0.07927616685628891) : \ + vec3(0.21264933049678802, 0.71516913175582890, 0.07218152284622192) ; + + + mat3 adjust = mat3((1.0 - sat) * coeff.x + sat, (1.0 - sat) * coeff.x, (1.0 - sat) * coeff.x, + (1.0 - sat) * coeff.y, (1.0 - sat) * coeff.y + sat, (1.0 - sat) * coeff.y, + (1.0 - sat) * coeff.z, (1.0 - sat) * coeff.z, (1.0 - sat) * coeff.z + sat); + + + screen = clamp(rolled_gain_v4(screen, clamp(lum, -0.49, 0.99)), 0., 1.); + screen = color * screen; + +// HUE vs SAT + vec3 src_h = RGB_to_XYZ(screen.rgb, SPC) * LMS; + src_h.x = src_h.x >= 0.0 ? pow(src_h.x, 0.43) : -pow(-src_h.x, 0.43); + src_h.y = src_h.y >= 0.0 ? pow(src_h.y, 0.43) : -pow(-src_h.y, 0.43); + src_h.z = src_h.z >= 0.0 ? pow(src_h.z, 0.43) : -pow(-src_h.z, 0.43); + + src_h.xyz *= IPT; + + float hue_at = atan(src_h.z, src_h.y); + chroma = sqrt(src_h.z * src_h.z + src_h.y * src_h.y); + + float hue_radians_r = -40.0 * (M_PI / 180.0); + float hue_r = chroma * cos(hue_at + hue_radians_r) * 2.; + + float hue_radians_g = 230.0 * (M_PI / 180.0); + float hue_g = chroma * cos(hue_at + hue_radians_g) * 2.; + + float hue_radians_b = 100.0 * (M_PI / 180.0); + float hue_b = chroma * cos(hue_at + hue_radians_b) * 2.; + + float msk = dot(clamp(vec3(hue_r, hue_g, hue_b), 0., 1.), vec3(satr, satg, satb)*(-1.)); + src_h = mixfix(screen.rgb, vec3(dot(coeff, screen.rgb)), msk); + + float sat_msk = (vibr < 0.0) ? 1.0 - abs(SatMask(src_h.x, src_h.y, src_h.z) - 1.0) * abs(vibr) : \ + 1.0 - (SatMask(src_h.x, src_h.y, src_h.z) * vibr) ; + + src_h = mixfix(src_h, clamp(adjust * src_h, 0., 1.), clamp(sat_msk, 0., 1.)); + + +// EOTF - Electro-Optical Transfer Function (Rec.709 does a Dim to Dark Surround adaptation) + vec3 TRC = (SPC == 3.0) ? clamp(pow(src_h, vec3(1./(563./256.))), 0., 1.) : \ + (SPC == 2.0) ? moncurve_r_f3(src_h, 2.20 + 0.022222, 0.0993) : \ + (SPC == 1.0) ? clamp(pow(src_h, vec3(1./(2.20 + 0.40))), 0., 1.) : \ + (SPC == 0.0) ? moncurve_r_f3(src_h, 2.20 + 0.20, 0.0550) : \ + clamp(pow(pow(src_h, vec3(1.019264)), vec3(1./(2.20 + 0.20))), 0., 1.) ; + + +// Technical LUT - (in SPC space) + float red_2 = (TRC.r * (global.LUT_Size2 - 1.0) + 0.4999) / (global.LUT_Size2 * global.LUT_Size2); + float green_2 = (TRC.g * (global.LUT_Size2 - 1.0) + 0.4999) / global.LUT_Size2; + float blue1_2 = (floor(TRC.b * (global.LUT_Size2 - 1.0)) / global.LUT_Size2) + red_2; + float blue2_2 = (ceil(TRC.b * (global.LUT_Size2 - 1.0)) / global.LUT_Size2) + red_2; + float mixer_2 = clamp(max((TRC.b - blue1_2) / (blue2_2 - blue1_2), 0.0), 0.0, 32.0); + vec3 color1_2 = texture(SamplerLUT2, vec2(blue1_2, green_2)).rgb; + vec3 color2_2 = texture(SamplerLUT2, vec2(blue2_2, green_2)).rgb; + vec3 LUT2_output = mixfix(color1_2, color2_2, mixer_2); + + LUT2_output = (global.LUT2_toggle == 0.0) ? TRC : LUT2_output; + + + FragColor = vec4(LUT2_output + aftglow.rgb, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/advanced/linearize-ntsc.slang b/crt/shaders/guest/advanced/linearize-ntsc.slang index 1466472..79fee3b 100644 --- a/crt/shaders/guest/advanced/linearize-ntsc.slang +++ b/crt/shaders/guest/advanced/linearize-ntsc.slang @@ -72,7 +72,7 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 // Joint parameter with main pass, values must match #define prescalex params.prescalex // prescale-x factor -#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.40 0.0 1.0 0.05 +#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.25 0.0 1.0 0.05 #define iscans params.iscans // interlace saturation #pragma stage vertex @@ -83,13 +83,13 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + 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 PrePass; +layout(set = 0, binding = 2) uniform sampler2D NtscPass; #define COMPAT_TEXTURE(c,d) texture(c,d) @@ -109,15 +109,15 @@ vec3 fetch_pixel(vec2 coord) vec2 d2 = dx - dy; float sum = 15.0; - vec3 result = 3.0*COMPAT_TEXTURE(PrePass, coord ).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord + dx).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord - dx).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord + dy).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord - dy).rgb + - COMPAT_TEXTURE(PrePass, coord + d1).rgb + - COMPAT_TEXTURE(PrePass, coord - d1).rgb + - COMPAT_TEXTURE(PrePass, coord + d2).rgb + - COMPAT_TEXTURE(PrePass, coord - d2).rgb; + vec3 result = 3.0*COMPAT_TEXTURE(NtscPass, coord ).rgb + + 2.0*COMPAT_TEXTURE(NtscPass, coord + dx).rgb + + 2.0*COMPAT_TEXTURE(NtscPass, coord - dx).rgb + + 2.0*COMPAT_TEXTURE(NtscPass, coord + dy).rgb + + 2.0*COMPAT_TEXTURE(NtscPass, coord - dy).rgb + + COMPAT_TEXTURE(NtscPass, coord + d1).rgb + + COMPAT_TEXTURE(NtscPass, coord - d1).rgb + + COMPAT_TEXTURE(NtscPass, coord + d2).rgb + + COMPAT_TEXTURE(NtscPass, coord - d2).rgb; return result/sum; } @@ -125,8 +125,8 @@ vec3 fetch_pixel(vec2 coord) void main() { - vec3 c1 = COMPAT_TEXTURE(PrePass, vTexCoord).rgb; - vec3 c2 = COMPAT_TEXTURE(PrePass, vTexCoord + vec2(0.0, params.OriginalSize.w)).rgb; + vec3 c1 = COMPAT_TEXTURE(NtscPass, vTexCoord).rgb; + vec3 c2 = COMPAT_TEXTURE(NtscPass, vTexCoord + vec2(0.0, params.OriginalSize.w)).rgb; if ((downsample_levelx + downsample_levely) > 0.025) { @@ -152,7 +152,7 @@ void main() if (inter <= params.OriginalSize.y/yres_div && interm > 0.5 && intres != 1.0 && intres != 0.5) { - intera = 0.5; + intera = 0.25; float line_no = clamp(floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)), 0.0, 1.0); float frame_no = clamp(floor(mod(float(params.FrameCount),2.0)), 0.0, 1.0); float ii = abs(line_no-frame_no); @@ -163,10 +163,9 @@ void main() r = clamp(max(m1*ii, (1.0-iscan)*min(m1,m2)), 0.0, 1.0); 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 == 4.0) c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); - if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b));} + if (interm == 4.0) { c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } + if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } } c = pow(c, vec3(gamma_in)); diff --git a/crt/shaders/guest/advanced/linearize.slang b/crt/shaders/guest/advanced/linearize.slang index 14b0472..5a596dc 100644 --- a/crt/shaders/guest/advanced/linearize.slang +++ b/crt/shaders/guest/advanced/linearize.slang @@ -72,7 +72,7 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 // Joint parameter with main pass, values must match #define prescalex params.prescalex // prescale-x factor -#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.40 0.0 1.0 0.05 +#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.25 0.0 1.0 0.05 #define iscans params.iscans // interlace saturation #pragma stage vertex @@ -83,7 +83,7 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + vTexCoord = TexCoord * 1.0001; } #pragma stage fragment @@ -152,7 +152,7 @@ void main() if (inter <= params.OriginalSize.y/yres_div && interm > 0.5 && intres != 1.0 && intres != 0.5) { - intera = 0.5; + intera = 0.25; float line_no = clamp(floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)), 0.0, 1.0); float frame_no = clamp(floor(mod(float(params.FrameCount),2.0)), 0.0, 1.0); float ii = abs(line_no-frame_no); @@ -163,10 +163,9 @@ void main() r = clamp(max(m1*ii, (1.0-iscan)*min(m1,m2)), 0.0, 1.0); 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 == 4.0) c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); - if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b));} + if (interm == 4.0) { c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } + if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } } c = pow(c, vec3(gamma_in)); diff --git a/crt/shaders/guest/advanced/lut/inv-trinitron-lut.png b/crt/shaders/guest/advanced/lut/inv-trinitron-lut.png index b4fa5dc42cc0bc49310fc6f8a4dd659fc1038357..c9033bc58184d465c958fd0f1600da6ae5ddb8a7 100644 GIT binary patch literal 15627 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 diff --git a/crt/shaders/guest/advanced/lut/nec-lut.png b/crt/shaders/guest/advanced/lut/nec-lut.png index d9e04bd6e8d87bc7ccf53210fdf4ba9dbdcd9fd4..604dc1988777a1037f101bb5c99922a8a9cc2bdc 100644 GIT binary patch literal 15243 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 literal 13681 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 Da;uw+ literal 20394 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

iybQTFJ7dYJ|YO~1%Y$5eOt-2A#hY#9?9E3#Uo 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 300.0) ? 2.0 : 3.0) : ((global.ntsc_phase > 2.5) ? 3.0 : 2.0); + + CHROMA_MOD_FREQ = (phase < 2.5) ? (4.0 * PI / 15.0) : (PI / 3.0); + ARTIFACTING = (global.quality > -0.5) ? global.quality : global.cust_artifacting; + FRINGING = (global.quality > -0.5) ? global.quality : global.cust_fringing; + SATURATION = global.ntsc_sat; + BRIGHTNESS = global.ntsc_bright; + + MERGE = (int(global.quality) == 2 || phase < 2.5) ? 0.0 : 1.0; + MERGE = (int(global.quality) == -1) ? global.ntsc_fields : MERGE; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec2 pix_no; +layout(location = 2) in float phase; +layout(location = 3) in float BRIGHTNESS; +layout(location = 4) in float SATURATION; +layout(location = 5) in float FRINGING; +layout(location = 6) in float ARTIFACTING; +layout(location = 7) in float CHROMA_MOD_FREQ; +layout(location = 8) in float MERGE; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +#define mix_mat mat3(BRIGHTNESS, FRINGING, FRINGING, ARTIFACTING, 2.0 * SATURATION, 0.0, ARTIFACTING, 0.0, 2.0 * SATURATION) + +const mat3 yiq2rgb_mat = mat3( + 1.0, 0.956, 0.6210, + 1.0, -0.2720, -0.6474, + 1.0, -1.1060, 1.7046); + +vec3 yiq2rgb(vec3 yiq) +{ + return yiq * yiq2rgb_mat; +} + +const mat3 yiq_mat = mat3( + 0.2989, 0.5870, 0.1140, + 0.5959, -0.2744, -0.3216, + 0.2115, -0.5229, 0.3114 +); + +vec3 rgb2yiq(vec3 col) +{ + return col * yiq_mat; +} + +void main() +{ + float res = global.ntsc_scale; + vec3 col = texture(Source, vTexCoord).rgb; + vec3 yiq = rgb2yiq(col); + float lum = yiq.x; + + vec3 yiq2 = yiq; + vec3 yiqs = yiq; + vec3 yiqz = yiq; + + float mod1 = 2.0; + float mod2 = 3.0; + +if (MERGE > 0.5) +{ + float chroma_phase2 = (phase < 2.5) ? PI * (mod(pix_no.y, mod1) + mod(global.FrameCount+1, 2.)) : 0.6667 * PI * (mod(pix_no.y, mod2) + mod(global.FrameCount+1, 2.)); + float mod_phase2 = chroma_phase2 + pix_no.x * CHROMA_MOD_FREQ; + float i_mod2 = cos(mod_phase2); + float q_mod2 = sin(mod_phase2); + yiq2.yz *= vec2(i_mod2, q_mod2); // Modulate. + yiq2 *= mix_mat; // Cross-talk. + yiq2.yz *= vec2(i_mod2, q_mod2); // Demodulate. + + if (res > 1.025) + { + mod_phase2 = chroma_phase2 + pix_no.x * CHROMA_MOD_FREQ * res; + i_mod2 = cos(mod_phase2); + q_mod2 = sin(mod_phase2); + yiqs.yz *= vec2(i_mod2, q_mod2); // Modulate. + yiq2.x = dot(yiqs, mix_mat[0]); // Cross-talk. + } +} + + float chroma_phase = (phase < 2.5) ? PI * (mod(pix_no.y, mod1) + mod(global.FrameCount, 2.)) : 0.6667 * PI * (mod(pix_no.y, mod2) + mod(global.FrameCount, 2.)); + float mod_phase = chroma_phase + pix_no.x * CHROMA_MOD_FREQ; + + float i_mod = cos(mod_phase); + float q_mod = sin(mod_phase); + + yiq.yz *= vec2(i_mod, q_mod); // Modulate. + yiq *= mix_mat; // Cross-talk. + yiq.yz *= vec2(i_mod, q_mod); // Demodulate. + + if (res > 1.025) + { + mod_phase = chroma_phase + pix_no.x * CHROMA_MOD_FREQ * res; + i_mod = cos(mod_phase); + q_mod = sin(mod_phase); + yiqz.yz *= vec2(i_mod, q_mod); // Modulate. + yiq.x = dot(yiqz, mix_mat[0]); // Cross-talk. + } + + yiq = (MERGE < 0.5) ? yiq : 0.5*(yiq+yiq2); + + FragColor = vec4(yiq, lum); +} diff --git a/crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang b/crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang new file mode 100644 index 0000000..8b5d0bc --- /dev/null +++ b/crt/shaders/guest/advanced/ntsc/ntsc-pass2.slang @@ -0,0 +1,299 @@ +#version 450 + +// NTSC-Adaptive +// based on Themaister's NTSC shader + + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; + float ntsc_scale; + float ntsc_phase; + float auto_res; + float ntsc_ring; +} global; + +#pragma parameter ntsc_scale "NTSC Resolution Scaling" 1.0 0.20 2.5 0.01 +#pragma parameter ntsc_phase "NTSC Phase: Auto | 2 phase | 3 phase" 1.0 1.0 3.0 1.0 +#pragma parameter ntsc_ring "NTSC Anti-Ringing" 0.0 0.0 1.0 1.0 + +#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 - vec2(0.5 / global.SourceSize.x, 0.0); // Compensate for decimate-by-2. +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +vec3 fetch_offset(float offset, float one_x) +{ + return texture(Source, vTexCoord + vec2((offset) * (one_x), 0.0)).xyz; +} + +const mat3 yiq2rgb_mat = mat3( + 1.0, 0.956, 0.6210, + 1.0, -0.2720, -0.6474, + 1.0, -1.1060, 1.7046); + +vec3 yiq2rgb(vec3 yiq) +{ + return yiq * yiq2rgb_mat; +} + +const mat3 yiq_mat = mat3( + 0.2989, 0.5870, 0.1140, + 0.5959, -0.2744, -0.3216, + 0.2115, -0.5229, 0.3114 +); + +vec3 rgb2yiq(vec3 col) +{ + return col * yiq_mat; +} + +const int TAPS_2_phase = 32; +const float luma_filter_2_phase[33] = float[33]( + -0.000174844, + -0.000205844, + -0.000149453, + -0.000051693, + 0.000000000, + -0.000066171, + -0.000245058, + -0.000432928, + -0.000472644, + -0.000252236, + 0.000198929, + 0.000687058, + 0.000944112, + 0.000803467, + 0.000363199, + 0.000013422, + 0.000253402, + 0.001339461, + 0.002932972, + 0.003983485, + 0.003026683, + -0.001102056, + -0.008373026, + -0.016897700, + -0.022914480, + -0.021642347, + -0.008863273, + 0.017271957, + 0.054921920, + 0.098342579, + 0.139044281, + 0.168055832, + 0.178571429); + +const float chroma_filter_2_phase[33] = float[33]( + 0.001384762, + 0.001678312, + 0.002021715, + 0.002420562, + 0.002880460, + 0.003406879, + 0.004004985, + 0.004679445, + 0.005434218, + 0.006272332, + 0.007195654, + 0.008204665, + 0.009298238, + 0.010473450, + 0.011725413, + 0.013047155, + 0.014429548, + 0.015861306, + 0.017329037, + 0.018817382, + 0.020309220, + 0.021785952, + 0.023227857, + 0.024614500, + 0.025925203, + 0.027139546, + 0.028237893, + 0.029201910, + 0.030015081, + 0.030663170, + 0.031134640, + 0.031420995, + 0.031517031); + +const int TAPS_3_phase = 24; +const float luma_filter_3_phase[25] = float[25]( + -0.000012020, + -0.000022146, + -0.000013155, + -0.000012020, + -0.000049979, + -0.000113940, + -0.000122150, + -0.000005612, + 0.000170516, + 0.000237199, + 0.000169640, + 0.000285688, + 0.000984574, + 0.002018683, + 0.002002275, + -0.000909882, + -0.007049081, + -0.013222860, + -0.012606931, + 0.002460860, + 0.035868225, + 0.084016453, + 0.135563500, + 0.175261268, + 0.190176552); + +const float chroma_filter_3_phase[25] = float[25]( + -0.000118847, + -0.000271306, + -0.000502642, + -0.000930833, + -0.001451013, + -0.002064744, + -0.002700432, + -0.003241276, + -0.003524948, + -0.003350284, + -0.002491729, + -0.000721149, + 0.002164659, + 0.006313635, + 0.011789103, + 0.018545660, + 0.026414396, + 0.035100710, + 0.044196567, + 0.053207202, + 0.061590275, + 0.068803602, + 0.074356193, + 0.077856564, + 0.079052396); + +void main() +{ + float res = global.ntsc_scale; + float OriginalSize = global.OriginalSize.x; + float one_x = global.SourceSize.z / res; + vec3 signal = vec3(0.0); + float phase = (global.ntsc_phase < 1.5) ? ((OriginalSize > 300.0) ? 2.0 : 3.0) : ((global.ntsc_phase > 2.5) ? 3.0 : 2.0); + + if(phase < 2.5) + { + vec3 sums = fetch_offset(0.0 - 32.0, one_x) + fetch_offset(32.0 - 0.0, one_x); + signal += sums * vec3(luma_filter_2_phase[0], chroma_filter_2_phase[0], chroma_filter_2_phase[0]); + sums = fetch_offset(1.0 - 32.0, one_x) + fetch_offset(32.0 - 1.0, one_x); + signal += sums * vec3(luma_filter_2_phase[1], chroma_filter_2_phase[1], chroma_filter_2_phase[1]); + sums = fetch_offset(2.0 - 32.0, one_x) + fetch_offset(32.0 - 2.0, one_x); + signal += sums * vec3(luma_filter_2_phase[2], chroma_filter_2_phase[2], chroma_filter_2_phase[2]); + sums = fetch_offset(3.0 - 32.0, one_x) + fetch_offset(32.0 - 3.0, one_x); + signal += sums * vec3(luma_filter_2_phase[3], chroma_filter_2_phase[3], chroma_filter_2_phase[3]); + sums = fetch_offset(4.0 - 32.0, one_x) + fetch_offset(32.0 - 4.0, one_x); + signal += sums * vec3(luma_filter_2_phase[4], chroma_filter_2_phase[4], chroma_filter_2_phase[4]); + sums = fetch_offset(5.0 - 32.0, one_x) + fetch_offset(32.0 - 5.0, one_x); + signal += sums * vec3(luma_filter_2_phase[5], chroma_filter_2_phase[5], chroma_filter_2_phase[5]); + sums = fetch_offset(6.0 - 32.0, one_x) + fetch_offset(32.0 - 6.0, one_x); + signal += sums * vec3(luma_filter_2_phase[6], chroma_filter_2_phase[6], chroma_filter_2_phase[6]); + sums = fetch_offset(7.0 - 32.0, one_x) + fetch_offset(32.0 - 7.0, one_x); + signal += sums * vec3(luma_filter_2_phase[7], chroma_filter_2_phase[7], chroma_filter_2_phase[7]); + sums = fetch_offset(8.0 - 32.0, one_x) + fetch_offset(32.0 - 8.0, one_x); + signal += sums * vec3(luma_filter_2_phase[8], chroma_filter_2_phase[8], chroma_filter_2_phase[8]); + sums = fetch_offset(9.0 - 32.0, one_x) + fetch_offset(32.0 - 9.0, one_x); + signal += sums * vec3(luma_filter_2_phase[9], chroma_filter_2_phase[9], chroma_filter_2_phase[9]); + sums = fetch_offset(10.0 - 32.0, one_x) + fetch_offset(32.0 - 10.0, one_x); + signal += sums * vec3(luma_filter_2_phase[10], chroma_filter_2_phase[10], chroma_filter_2_phase[10]); + sums = fetch_offset(11.0 - 32.0, one_x) + fetch_offset(32.0 - 11.0, one_x); + signal += sums * vec3(luma_filter_2_phase[11], chroma_filter_2_phase[11], chroma_filter_2_phase[11]); + sums = fetch_offset(12.0 - 32.0, one_x) + fetch_offset(32.0 - 12.0, one_x); + signal += sums * vec3(luma_filter_2_phase[12], chroma_filter_2_phase[12], chroma_filter_2_phase[12]); + sums = fetch_offset(13.0 - 32.0, one_x) + fetch_offset(32.0 - 13.0, one_x); + signal += sums * vec3(luma_filter_2_phase[13], chroma_filter_2_phase[13], chroma_filter_2_phase[13]); + sums = fetch_offset(14.0 - 32.0, one_x) + fetch_offset(32.0 - 14.0, one_x); + signal += sums * vec3(luma_filter_2_phase[14], chroma_filter_2_phase[14], chroma_filter_2_phase[14]); + sums = fetch_offset(15.0 - 32.0, one_x) + fetch_offset(32.0 - 15.0, one_x); + signal += sums * vec3(luma_filter_2_phase[15], chroma_filter_2_phase[15], chroma_filter_2_phase[15]); + sums = fetch_offset(16.0 - 32.0, one_x) + fetch_offset(32.0 - 16.0, one_x); + signal += sums * vec3(luma_filter_2_phase[16], chroma_filter_2_phase[16], chroma_filter_2_phase[16]); + sums = fetch_offset(17.0 - 32.0, one_x) + fetch_offset(32.0 - 17.0, one_x); + signal += sums * vec3(luma_filter_2_phase[17], chroma_filter_2_phase[17], chroma_filter_2_phase[17]); + sums = fetch_offset(18.0 - 32.0, one_x) + fetch_offset(32.0 - 18.0, one_x); + signal += sums * vec3(luma_filter_2_phase[18], chroma_filter_2_phase[18], chroma_filter_2_phase[18]); + sums = fetch_offset(19.0 - 32.0, one_x) + fetch_offset(32.0 - 19.0, one_x); + signal += sums * vec3(luma_filter_2_phase[19], chroma_filter_2_phase[19], chroma_filter_2_phase[19]); + sums = fetch_offset(20.0 - 32.0, one_x) + fetch_offset(32.0 - 20.0, one_x); + signal += sums * vec3(luma_filter_2_phase[20], chroma_filter_2_phase[20], chroma_filter_2_phase[20]); + sums = fetch_offset(21.0 - 32.0, one_x) + fetch_offset(32.0 - 21.0, one_x); + signal += sums * vec3(luma_filter_2_phase[21], chroma_filter_2_phase[21], chroma_filter_2_phase[21]); + sums = fetch_offset(22.0 - 32.0, one_x) + fetch_offset(32.0 - 22.0, one_x); + signal += sums * vec3(luma_filter_2_phase[22], chroma_filter_2_phase[22], chroma_filter_2_phase[22]); + sums = fetch_offset(23.0 - 32.0, one_x) + fetch_offset(32.0 - 23.0, one_x); + signal += sums * vec3(luma_filter_2_phase[23], chroma_filter_2_phase[23], chroma_filter_2_phase[23]); + sums = fetch_offset(24.0 - 32.0, one_x) + fetch_offset(32.0 - 24.0, one_x); + signal += sums * vec3(luma_filter_2_phase[24], chroma_filter_2_phase[24], chroma_filter_2_phase[24]); + sums = fetch_offset(25.0 - 32.0, one_x) + fetch_offset(32.0 - 25.0, one_x); + signal += sums * vec3(luma_filter_2_phase[25], chroma_filter_2_phase[25], chroma_filter_2_phase[25]); + sums = fetch_offset(26.0 - 32.0, one_x) + fetch_offset(32.0 - 26.0, one_x); + signal += sums * vec3(luma_filter_2_phase[26], chroma_filter_2_phase[26], chroma_filter_2_phase[26]); + sums = fetch_offset(27.0 - 32.0, one_x) + fetch_offset(32.0 - 27.0, one_x); + signal += sums * vec3(luma_filter_2_phase[27], chroma_filter_2_phase[27], chroma_filter_2_phase[27]); + sums = fetch_offset(28.0 - 32.0, one_x) + fetch_offset(32.0 - 28.0, one_x); + signal += sums * vec3(luma_filter_2_phase[28], chroma_filter_2_phase[28], chroma_filter_2_phase[28]); + sums = fetch_offset(29.0 - 32.0, one_x) + fetch_offset(32.0 - 29.0, one_x); + signal += sums * vec3(luma_filter_2_phase[29], chroma_filter_2_phase[29], chroma_filter_2_phase[29]); + sums = fetch_offset(30.0 - 32.0, one_x) + fetch_offset(32.0 - 30.0, one_x); + signal += sums * vec3(luma_filter_2_phase[30], chroma_filter_2_phase[30], chroma_filter_2_phase[30]); + sums = fetch_offset(31.0 - 32.0, one_x) + fetch_offset(32.0 - 31.0, one_x); + signal += sums * vec3(luma_filter_2_phase[31], chroma_filter_2_phase[31], chroma_filter_2_phase[31]); + + signal += texture(Source, vTexCoord).xyz * + vec3(luma_filter_2_phase[TAPS_2_phase], chroma_filter_2_phase[TAPS_2_phase], chroma_filter_2_phase[TAPS_2_phase]); + } + else if(phase > 2.5) + { + for (int i = 0; i < TAPS_3_phase; i++) + { + float offset = float(i); + + vec3 sums = fetch_offset(offset - float(TAPS_3_phase), one_x) + + fetch_offset(float(TAPS_3_phase) - offset, one_x); + signal += sums * vec3(luma_filter_3_phase[i], chroma_filter_3_phase[i], chroma_filter_3_phase[i]); + } + signal += texture(Source, vTexCoord).xyz * + vec3(luma_filter_3_phase[TAPS_3_phase], chroma_filter_3_phase[TAPS_3_phase], chroma_filter_3_phase[TAPS_3_phase]); + } + + + if (global.ntsc_ring > 0.5) + { + vec2 dx = vec2(global.OriginalSize.z / min(res, 1.0), 0.0); + float a = texture(Source, vTexCoord - 1.5*dx).a; + float b = texture(Source, vTexCoord - 0.5*dx).a; + float c = texture(Source, vTexCoord + 1.5*dx).a; + float d = texture(Source, vTexCoord + 0.5*dx).a; + float e = texture(Source, vTexCoord ).a; + signal.x = clamp(signal.x, min(min(min(a,b),min(c,d)),e), max(max(max(a,b),max(c,d)),e)); + } + + signal.x = clamp(signal.x, -1.0, 1.0); + vec3 rgb = signal; + + FragColor = vec4(rgb, 1.0); +} diff --git a/crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang b/crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang new file mode 100644 index 0000000..30e41ae --- /dev/null +++ b/crt/shaders/guest/advanced/ntsc/ntsc-pass3.slang @@ -0,0 +1,122 @@ +#version 450 + +// NTSC-Adaptive +// based on Themaister's NTSC shader + + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; + float auto_res; + float ntsc_sharp; + float ntsc_shape; + float blendMode; +} global; + +#pragma parameter ntsc_sharp "NTSC Sharpness (negative: Adaptive)" 0.0 -10.0 10.0 0.50 +#pragma parameter ntsc_shape "NTSC Sharpness Shape" 0.75 0.5 1.0 0.05 +#pragma parameter blendMode "NTSC Blend Mode (Main Mode Control)" 1.0 0.0 1.0 1.0 + +#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 - vec2(0.25 / global.SourceSize.x, 0.0); // Compensate for decimate-by-2. +} + +#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 NPass1; +layout(set = 0, binding = 4) uniform sampler2D PrePass0; + +float fetch_offset(float offset, float one_x) +{ + return texture(NPass1, vTexCoord + vec2((offset) * (one_x), 0.0)).x; +} + +const mat3 yiq2rgb_mat = mat3( + 1.0, 0.956, 0.6210, + 1.0, -0.2720, -0.6474, + 1.0, -1.1060, 1.7046); + +vec3 yiq2rgb(vec3 yiq) +{ + return yiq * yiq2rgb_mat; +} + +const mat3 yiq_mat = mat3( + 0.2989, 0.5870, 0.1140, + 0.5959, -0.2744, -0.3216, + 0.2115, -0.5229, 0.3114 +); + +vec3 rgb2yiq(vec3 col) +{ + return col * yiq_mat; +} + + +void main() +{ + vec2 offsetx = vec2(0.5 * global.OriginalSize.z, 0.0); + vec2 texcoord = vTexCoord + vec2(0.25 * global.SourceSize.z, 0.0); + + vec3 l1 = texture(Source, texcoord + offsetx).xyz; + vec3 l2 = texture(Source, texcoord - offsetx).xyz; + vec3 l3 = texture(Source, texcoord + 0.50*offsetx).xyz; + vec3 l4 = texture(Source, texcoord - 0.50*offsetx).xyz; + vec3 ref = texture(Source, texcoord).xyz; + + float lum1 = texture(NPass1, vTexCoord).a; + float lum2 = max(ref.x, 0.0); + + float dif = max(max(abs(l1.x-l2.x), abs(l1.y-l2.y)), max(abs(l1.z-l2.z), abs(l1.x*l1.x-l2.x*l2.x))); + float dff = max(max(abs(l3.x-l4.x), abs(l3.y-l4.y)), max(abs(l3.z-l4.z), abs(l3.x*l3.x-l4.x*l4.x))); + + float lc = (1.0-smoothstep(0.10, 0.20, abs(lum2-lum1)))*pow(dff, 0.125); + + float sweight = smoothstep(0.05-0.03*lc, 0.45 - 0.40*lc, dif); + + vec3 signal = ref; + + if (abs(global.ntsc_sharp) > -0.1) + { + float lummix = mix(lum2, lum1, 0.1*abs(global.ntsc_sharp)); + float lm1 = mix(lum2*lum2, lum1*lum1, 0.1*abs(global.ntsc_sharp)); lm1 = sqrt(lm1); + float lm2 = mix(sqrt(lum2), sqrt(lum1), 0.1*abs(global.ntsc_sharp)); lm2 = lm2*lm2; + + float k1 = abs(lummix - lm1) + 0.00001; + float k2 = abs(lummix - lm2) + 0.00001; + + lummix = min((k2*lm1 + k1*lm2)/(k1+k2), 1.0); + + signal.x = mix(lum2, lummix, smoothstep(0.25, 0.4, pow(dff, 0.125))); + signal.x = min(signal.x, max(global.ntsc_shape*signal.x, lum2)); + } + else signal.x = clamp(signal.x, -1.0, 1.0); + + vec3 rgb = signal; + if (global.ntsc_sharp < -0.1) + { + rgb.x = mix(ref.x, rgb.x, sweight); + } + + rgb = clamp(yiq2rgb(rgb), 0.0, 1.0); + + if (global.blendMode < 0.5) + { + vec3 orig = texture(PrePass0, vTexCoord).rgb; + rgb = normalize(rgb + 0.00001) * min(length(rgb), length(orig)); + } + + FragColor = vec4(rgb, 1.0); +} diff --git a/crt/shaders/guest/advanced/pre-shaders-afterglow.slang b/crt/shaders/guest/advanced/pre-shaders-afterglow.slang index 622716e..caa24f5 100644 --- a/crt/shaders/guest/advanced/pre-shaders-afterglow.slang +++ b/crt/shaders/guest/advanced/pre-shaders-afterglow.slang @@ -3,7 +3,7 @@ /* CRT Advanced Afterglow, color altering - Copyright (C) 2019-2021 guest(r) and Dr. Venom + Copyright (C) 2019-2022 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 @@ -39,12 +39,13 @@ layout(push_constant) uniform Push float vigdef; float sega_fix; float pre_bb; + float contr; } params; #pragma parameter AS " Afterglow Strength" 0.20 0.0 0.60 0.01 #define AS params.AS -#pragma parameter sat " Afterglow saturation" 0.20 0.0 1.0 0.01 +#pragma parameter sat " Afterglow saturation" 0.50 0.0 1.0 0.01 #define sat params.sat #pragma parameter bogus_color "[ COLOR TWEAKS ]:" 0.0 0.0 1.0 1.0 @@ -56,7 +57,7 @@ layout(push_constant) uniform Push #define CP params.CP #define CS params.CS -#pragma parameter TNTC " LUT Colors: Trin. | invTrin. | Nec Mult. | NTSC" 0.0 0.0 4.0 1.0 +#pragma parameter TNTC " LUT Colors: Trin.1 | Trin.2 | Nec Mult. | NTSC" 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 @@ -72,13 +73,15 @@ layout(push_constant) uniform Push #pragma parameter pre_bb " Brightness Adjustment" 1.0 0.0 2.0 0.01 +#pragma parameter contr " Contrast Adjustment" 0.0 -1.0 1.0 0.05 + #pragma parameter sega_fix " Sega Brightness Fix" 0.0 0.0 1.0 1.0 -#pragma parameter BP " Raise Black Level" 0.0 0.0 25.0 1.0 +#pragma parameter BP " Raise Black Level" 0.0 -100.0 25.0 1.0 -#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.025 +#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.05 -#pragma parameter vigdef " Vignette Definition" 7.0 0.4 15.0 0.2 +#pragma parameter vigdef " Vignette Size" 1.0 0.5 3.0 0.10 #define WP params.WP #define wp_saturation params.wp_saturation @@ -201,9 +204,9 @@ const mat3 D65_to_D55 = mat3 ( const mat3 D65_to_D93 = mat3 ( - 0.3683017655, 0.1899055978, 0.0172641453, - 0.3555467892, 0.7110935785, 0.1185155964, - 0.2475020592, 0.0990008237, 1.3035108450); + 0.3412754080, 0.1759701322, 0.0159972847, + 0.3646170520, 0.7292341040, 0.1215390173, + 0.2369894093, 0.0947957637, 1.2481442225); vec3 fix_lut(vec3 lutcolor, vec3 ref) @@ -215,18 +218,28 @@ vec3 fix_lut(vec3 lutcolor, vec3 ref) return mix(lutcolor, ref, LUTBR); } -vec2 ctransform (vec2 inputc) -{ - return vec2( inputc.x * sqrt(1.0 - 0.5*inputc.y*inputc.y), inputc.y * sqrt(1.0 - 0.5*inputc.x*inputc.x)); + +float vignette(vec2 pos) { + vec2 b = vec2(params.vigdef, params.vigdef) * vec2(1.0, params.OriginalSize.x/params.OriginalSize.y) * 0.125; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + vec2 res = mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, 0.70.xx); + return max(mix(1.0, sqrt(res.x*res.y), params.vigstr), 0.0); } -float vignette (vec2 coords) + +vec3 plant (vec3 tar, float r) { - vec2 ccoords = ctransform(2.0*(coords-0.5)); - ccoords = ccoords * ccoords; - float vstr = sqrt(ccoords.x+ccoords.y); - vstr = pow(vstr, params.vigdef); - return max(mix(1.0, 1.0-vstr, params.vigstr), 0.0); + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +float contrast(float x) +{ + float y = 2.0*x-1.0; + y = (sin(y*1.57079632679)+1.0)*0.5; + return mix(x, y, params.contr); } void main() @@ -239,8 +252,6 @@ void main() float l = length(aftglow.rgb); aftglow.rgb = AS*w*normalize(pow(aftglow.rgb + 0.01, vec3(sat)))*l; float bp = w * BP/255.0; - - imgColor.rgb = imgColor.rgb * params.pre_bb; if (params.sega_fix > 0.5) imgColor.rgb = imgColor.rgb * (255.0 / 239.0); @@ -326,11 +337,13 @@ void main() 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 scolor1 = plant(pow(color, vec3(wp_saturation)), max(max(color.r,color.g),color.b)); + float luma = dot(color, vec3(0.299, 0.587, 0.114)); vec3 scolor2 = mix(vec3(luma), color, wp_saturation); color = (wp_saturation > 1.0) ? scolor1 : scolor2; - + + color = plant(color, contrast(max(max(color.r,color.g),color.b))); + p = 2.2; color = pow(color, vec3(p)); @@ -349,8 +362,13 @@ void main() color = mix(color, comp, m); color = pow(max(color, 0.0), vec3(1.0/p)); + + if (BP > -0.5) color = color + aftglow.rgb + bp; else + { + color = max(color + BP/255.0, 0.0) / (1.0 + BP/255.0*step(- BP/255.0, max(max(color.r,color.g),color.b))) + aftglow.rgb; + } - color = color + aftglow.rgb + bp; + color = min(color * params.pre_bb, 1.0); FragColor = vec4(color, vignette(vTexCoord.xy)); } \ No newline at end of file diff --git a/crt/shaders/guest/linearize_scanlines.slang b/crt/shaders/guest/advanced/stock.slang similarity index 69% rename from crt/shaders/guest/linearize_scanlines.slang rename to crt/shaders/guest/advanced/stock.slang index 4450ec9..bf3cf6b 100644 --- a/crt/shaders/guest/linearize_scanlines.slang +++ b/crt/shaders/guest/advanced/stock.slang @@ -27,12 +27,9 @@ void main() #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D AfterglowPass; - -#define PassPrev6Texture AfterglowPass -#define COMPAT_TEXTURE(c,d) texture(c,d) +layout(set = 0, binding = 2) uniform sampler2D Source; void main() { - FragColor = vec4(pow(vec3(COMPAT_TEXTURE(PassPrev6Texture, vTexCoord).rgb), vec3(10.0)),1.0); -} + FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0); +} \ No newline at end of file diff --git a/crt/shaders/guest/afterglow.slang b/crt/shaders/guest/afterglow.slang deleted file mode 100644 index a77ed8f..0000000 --- a/crt/shaders/guest/afterglow.slang +++ /dev/null @@ -1,105 +0,0 @@ -#version 450 - -/* - Phosphor Afterglow Shader - - Copyright (C) 2018 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 AS, PR, PG, PB, sat; -} params; - -#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 PG "Persistence Green" 0.05 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 AS params.AS -#define PR params.PR -#define PG params.PG -#define PB params.PB -#define sat params.sat - -#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; -layout(set = 0, binding = 3) uniform sampler2D OriginalHistory1; -layout(set = 0, binding = 4) uniform sampler2D OriginalHistory2; -layout(set = 0, binding = 5) uniform sampler2D OriginalHistory3; -layout(set = 0, binding = 6) uniform sampler2D OriginalHistory4; -layout(set = 0, binding = 7) uniform sampler2D OriginalHistory5; -layout(set = 0, binding = 8) uniform sampler2D OriginalHistory6; - -#define Prev1Texture OriginalHistory1 -#define Prev2Texture OriginalHistory2 -#define Prev3Texture OriginalHistory3 -#define Prev4Texture OriginalHistory4 -#define Prev5Texture OriginalHistory5 -#define Prev6Texture OriginalHistory6 - -#define TEX0 vTexCoord - -#define eps 1e-3 - -vec3 afterglow(float number) -{ - return vec3(AS)*exp2(-vec3(PR, PG, PB)*vec3(number*number)); -} - -void main() -{ - vec3 color = COMPAT_TEXTURE(Source, TEX0.xy).rgb; - vec3 color1 = COMPAT_TEXTURE(Prev1Texture, TEX0.xy).rgb * afterglow(1.0); - vec3 color2 = COMPAT_TEXTURE(Prev2Texture, TEX0.xy).rgb * afterglow(2.0); - vec3 color3 = COMPAT_TEXTURE(Prev3Texture, TEX0.xy).rgb * afterglow(3.0); - vec3 color4 = COMPAT_TEXTURE(Prev4Texture, TEX0.xy).rgb * afterglow(4.0); - vec3 color5 = COMPAT_TEXTURE(Prev5Texture, TEX0.xy).rgb * afterglow(5.0); - vec3 color6 = COMPAT_TEXTURE(Prev6Texture, TEX0.xy).rgb * afterglow(6.0); - - vec3 glow = color1 + color2 + color3 + color4 + color5 + color6; - - float l = length(glow); - glow = normalize(pow(glow + vec3(eps), vec3(sat)))*l; - - float w = 1.0; - if ((color.r + color.g + color.b) > 7.0/255.0) w = 0.0; - - FragColor = vec4(color + w*glow,1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/avg-lum.slang b/crt/shaders/guest/avg-lum.slang deleted file mode 100644 index 8d1379e..0000000 --- a/crt/shaders/guest/avg-lum.slang +++ /dev/null @@ -1,88 +0,0 @@ -#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 Source; -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(Source, vec2(0.25, 0.25), m).rgb)); - ltotal+= max(0.0, length(textureLod(Source, vec2(0.25, 0.75), m).rgb)); - ltotal+= max(0.0, length(textureLod(Source, vec2(0.75, 0.25), m).rgb)); - ltotal+= max(0.0, length(textureLod(Source, 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/blur_horiz.slang b/crt/shaders/guest/blur_horiz.slang deleted file mode 100644 index 53e8363..0000000 --- a/crt/shaders/guest/blur_horiz.slang +++ /dev/null @@ -1,59 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float TAPSH; - float GLOW_FALLOFF_H; -} params; - -// Higher value, more centered glow. -// Lower values might need more taps. -#pragma parameter TAPSH "H. Glow Radius" 4.0 1.0 10.0 1.0 -#define TAPSH params.TAPSH -#pragma parameter GLOW_FALLOFF_H "Horizontal Glow Grade" 0.30 0.00 1.0 0.01 -#define GLOW_FALLOFF_H params.GLOW_FALLOFF_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 Source; - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define SourceSize params.SourceSize - -#define kernel(x) exp(-GLOW_FALLOFF_H * (x) * (x)) - -void main() -{ - vec3 col = vec3(0.0); - float dx = SourceSize.z; - - float k_total = 0.; - for (float i = -TAPSH; i <= TAPSH; i++) - { - float k = kernel(i); - k_total += k; - col += k * COMPAT_TEXTURE(Source, vTexCoord + vec2(float(i) * dx, 0.0)).rgb; - } - FragColor = vec4(col / k_total, 1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/blur_vert.slang b/crt/shaders/guest/blur_vert.slang deleted file mode 100644 index 735de11..0000000 --- a/crt/shaders/guest/blur_vert.slang +++ /dev/null @@ -1,60 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float TAPSV; - float GLOW_FALLOFF_V; -} params; - -// Higher value, more centered glow. -// Lower values might need more taps. -// Parameter lines go here: -#pragma parameter TAPSV "V. Glow Radius" 4.0 1.0 10.0 1.0 -#define TAPSV params.TAPSV -#pragma parameter GLOW_FALLOFF_V "Vertical Glow Grade" 0.30 0.00 1.0 0.01 -#define GLOW_FALLOFF_V params.GLOW_FALLOFF_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) -#define SourceSize params.SourceSize - -#define kernel(x) exp(-GLOW_FALLOFF_V * (x) * (x)) - -void main() -{ - vec3 col = vec3(0.0); - float dy = SourceSize.w; - - float k_total = 0.; - for (float i = -TAPSV; i <= TAPSV; i++) - { - float k = kernel(i); - k_total += k; - col += k * COMPAT_TEXTURE(Source, vTexCoord + vec2(0.0, float(i) * dy)).rgb; - } - FragColor = vec4(col / k_total, 1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/color-profiles.slang b/crt/shaders/guest/color-profiles.slang deleted file mode 100644 index cc017b4..0000000 --- a/crt/shaders/guest/color-profiles.slang +++ /dev/null @@ -1,160 +0,0 @@ -#version 450 - -/* - CRT Color Profiles - - Copyright (C) 2019 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 -{ - float CP, CS; -} params; - -#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 - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#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; -} - -#pragma stage fragment -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -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 -); - -void main() -{ - vec3 c = COMPAT_TEXTURE(Source, TEX0.xy).rgb; - - 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; } - - vec3 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 = pow(color, vec3(1.0/p)); - - if (CP == -1.0) color = c; - - FragColor = vec4(color,1.0); -} \ 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 deleted file mode 100644 index 2060f98..0000000 --- a/crt/shaders/guest/crt-gdv-new/avg-lum-ntsc.slang +++ /dev/null @@ -1,88 +0,0 @@ -#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 deleted file mode 100644 index d2b5538..0000000 --- a/crt/shaders/guest/crt-gdv-new/avg-lum.slang +++ /dev/null @@ -1,112 +0,0 @@ -#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/crt-guest-dr-venom2-hires.slang b/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang deleted file mode 100644 index b56b5a7..0000000 --- a/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-hires.slang +++ /dev/null @@ -1,729 +0,0 @@ -#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 deleted file mode 100644 index 64f9b69..0000000 --- a/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2-ntsc.slang +++ /dev/null @@ -1,729 +0,0 @@ -#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 deleted file mode 100644 index 2294b14..0000000 --- a/crt/shaders/guest/crt-gdv-new/crt-guest-dr-venom2.slang +++ /dev/null @@ -1,756 +0,0 @@ -#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 deleted file mode 100644 index 32bf562..0000000 --- a/crt/shaders/guest/crt-gdv-new/deconvergence.slang +++ /dev/null @@ -1,123 +0,0 @@ -#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 deleted file mode 100644 index 8e70131..0000000 --- a/crt/shaders/guest/crt-gdv-new/gaussian_horizontal.slang +++ /dev/null @@ -1,101 +0,0 @@ -#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 deleted file mode 100644 index 6dc6f17..0000000 --- a/crt/shaders/guest/crt-gdv-new/gaussian_vertical.slang +++ /dev/null @@ -1,100 +0,0 @@ -#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 deleted file mode 100644 index 9cd1ce2..0000000 --- a/crt/shaders/guest/crt-gdv-new/linearize-ntsc.slang +++ /dev/null @@ -1,163 +0,0 @@ -#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 deleted file mode 100644 index 3327d5c..0000000 --- a/crt/shaders/guest/crt-gdv-new/linearize.slang +++ /dev/null @@ -1,133 +0,0 @@ -#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-guest-dr-venom.slang b/crt/shaders/guest/crt-guest-dr-venom.slang deleted file mode 100644 index 56275bc..0000000 --- a/crt/shaders/guest/crt-guest-dr-venom.slang +++ /dev/null @@ -1,615 +0,0 @@ -#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, CGWG, gamma_out, spike, inter; -} params; - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float bloom; - float interm; - float scans; - float slotms; -} global; - -#pragma parameter TATE "TATE Mode" 0.0 0.0 1.0 1.0 -#define TATE params.TATE // Screen orientation -#pragma parameter IOS "Smart Integer Scaling: 1.0:Y, 2.0:'X'+Y" 0.0 0.0 2.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 brightboost "Bright Boost Dark Pixels" 1.40 0.50 4.00 0.05 -#define brightboost params.brightboost // adjust brightness -#pragma parameter brightboost1 "Bright Boost Bright Pixels" 1.15 0.50 3.00 0.05 -#define brightboost1 params.brightboost1 // adjust brightness -#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 1.0 15.0 1.0 -#define scanline1 params.scanline1 // scanline param, vertical sharpness -#pragma parameter scanline2 "Scanline beam shape high" 8.0 5.0 23.0 1.0 -#define scanline2 params.scanline2 // scanline param, vertical sharpness -#pragma parameter beam_min "Scanline dark" 1.35 0.5 2.5 0.05 -#define beam_min params.beam_min // dark area beam min - narrow -#pragma parameter beam_max "Scanline bright" 1.05 0.5 2.5 0.05 -#define beam_max params.beam_max // bright area beam max - wide -#pragma parameter beam_size "Increased bright scanline beam" 0.70 0.0 1.0 0.05 -#define beam_size params.beam_size // increased max. beam size -#pragma parameter h_sharp "Horizontal sharpness" 5.25 1.0 15.0 0.25 -#define h_sharp params.h_sharp // pixel sharpness -#pragma parameter s_sharp "Substractive sharpness (relative)" 0.40 0.0 1.0 0.10 -#define s_sharp params.s_sharp // substractive sharpness -#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 glow "Glow Strength" 0.02 0.0 0.5 0.01 -#define glow params.glow // Glow Strength -#pragma parameter shadowMask "CRT Mask: 0:CGWG, 1-4:Lottes, 5-6:'Trinitron'" 0.0 -1.0 7.0 1.0 -#define shadowMask params.shadowMask // Mask Style -#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 vertmask "PVM Like Colors" 0.0 -0.30 0.30 0.02 -#define vertmask params.vertmask // Vertical mask -#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 mcut "Mask 5-7 cutoff" 0.25 0.0 0.5 0.05 -#define mcut params.mcut // Mask 5-7 cutoff -#pragma parameter maskDark "Lottes&Trinitron maskDark" 0.5 0.0 2.0 0.05 -#define maskDark params.maskDark // Dark "Phosphor" -#pragma parameter maskLight "Lottes&Trinitron maskLight" 1.5 0.0 2.0 0.05 -#define maskLight params.maskLight // Light "Phosphor" -#pragma parameter CGWG "Mask 0&7 Mask Str." 0.3 0.0 1.0 0.05 -#define CGWG params.CGWG // CGWG Mask Strength -#pragma parameter gamma_out "Gamma out" 2.4 1.0 3.5 0.05 -#define gamma_out params.gamma_out // output gamma -#pragma parameter spike "Scanline Spike Removal" 1.0 0.0 2.0 0.10 -#define spike params.spike -#pragma parameter inter "Interlace Trigger Resolution :" 400.0 0.0 800.0 25.0 -#define inter params.inter // interlace resolution -#pragma parameter interm "Interlace Mode (0.0 = OFF):" 1.0 0.0 3.0 1.0 -#define interm global.interm // interlace mode -#pragma parameter bloom "Bloom Strength" 0.0 0.0 2.0 0.1 -#define bloom global.bloom // bloom effect -#pragma parameter scans "Scanline 1&2 Saturation" 0.5 0.0 1.0 0.1 -#define scans global.scans // scanline saturation - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define TEX0 vTexCoord -#define InputSize SourceSize -#define TextureSize SourceSize - -#define SourceSize global.SourceSize -#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 Source; -layout(set = 0, binding = 3) uniform sampler2D LinearizePass; -layout(set = 0, binding = 4) uniform sampler2D AvgLumPass; -layout(set = 0, binding = 5) uniform sampler2D GlowPass; - -#define Texture Source -#define PassPrev5Texture AvgLumPass -#define PassPrev4Texture LinearizePass -#define PassPrev2Texture GlowPass - -#define eps 1e-10 - -float st(float x) -{ - return exp2(-10.0*x*x); -} - -vec3 sw0(vec3 x, vec3 color, float scanline) -{ - vec3 tmp = mix(vec3(beam_min),vec3(beam_max), color); - vec3 ex = x*tmp; - return exp2(-scanline*ex*ex); -} - -vec3 sw1(vec3 x, vec3 color, float scanline) -{ - float mx = max(max(color.r, color.g),color.b); - x = mix (x, beam_min*x, max(x-0.4*mx,0.0)); - vec3 tmp = mix(vec3(1.2*beam_min),vec3(beam_max), color); - vec3 ex = x*tmp; - float br = clamp(0.8*beam_min - 1.0, 0.2, 0.45); - vec3 res = exp2(-scanline*ex*ex)/(1.0-br+br*mx); - mx = max(max(res.r,res.g),res.b); - float scans1 = scans; if (abs(vertmask) > 0.01) scans1=1.0; - return mix(vec3(mx), res, scans1); -} - -vec3 sw2(vec3 x, vec3 color, float scanline) -{ - float mx = max(max(color.r, color.g),color.b); - vec3 tmp = mix(vec3(2.5*beam_min),vec3(beam_max), color); - tmp = mix(vec3(beam_max), tmp, pow(abs(x), color+0.3)); - vec3 ex = x*tmp; - vec3 res = exp2(-scanline*ex*ex)/(0.6 + 0.4*mx); - mx = max(max(res.r,res.g),res.b); - float scans1 = scans; if (abs(vertmask) > 0.01) scans1=0.85; - return mix(vec3(mx), res, scans1); -} - -// Shadow mask (1-4 from PD CRT Lottes shader). -vec3 Mask(vec2 pos, vec3 c) -{ - pos = floor(pos/masksize); - vec3 mask = vec3(maskDark, maskDark, maskDark); - - // No mask - if (shadowMask == -1.0) - { - mask = vec3(1.0); - } - - // Phosphor. - else if (shadowMask == 0.0) - { - pos.x = fract(pos.x*0.5); - float mc = 1.0 - CGWG; - if (pos.x < 0.5) { mask.r = 1.1; mask.g = mc; mask.b = 1.1; } - else { mask.r = mc; mask.g = 1.1; 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; - } - - // Alternate mask 5 - else if (shadowMask == 5.0) - { - float mx = max(max(c.r,c.g),c.b); - vec3 maskTmp = vec3( min( 1.25*max(mx-mcut,0.0)/(1.0-mcut) ,maskDark + 0.2*(1.0-maskDark)*mx)); - float adj = 0.80*maskLight - 0.5*(0.80*maskLight - 1.0)*mx + 0.75*(1.0-mx); - mask = maskTmp; - pos.x = fract(pos.x/2.0); - if (pos.x < 0.5) - { mask.r = adj; - mask.b = adj; - } - else mask.g = adj; - } - - // Alternate mask 6 - else if (shadowMask == 6.0) - { - float mx = max(max(c.r,c.g),c.b); - vec3 maskTmp = vec3( min( 1.33*max(mx-mcut,0.0)/(1.0-mcut) ,maskDark + 0.225*(1.0-maskDark)*mx)); - float adj = 0.80*maskLight - 0.5*(0.80*maskLight - 1.0)*mx + 0.75*(1.0-mx); - mask = maskTmp; - pos.x = fract(pos.x/3.0); - if (pos.x < 0.333) mask.r = adj; - else if (pos.x < 0.666) mask.g = adj; - else mask.b = adj; - } - - // Alternate mask 7 - else if (shadowMask == 7.0) - { - float mc = 1.0 - CGWG; - float mx = max(max(c.r,c.g),c.b); - float maskTmp = min(1.6*max(mx-mcut,0.0)/(1.0-mcut) , mc); - mask = vec3(maskTmp); - pos.x = fract(pos.x/2.0); - if (pos.x < 0.5) mask = vec3(1.0 + 0.6*(1.0-mx)); - } - - return mask; -} - -float SlotMask(vec2 pos, vec3 c) -{ - if (slotmask == 0.0) return 1.0; - - pos = floor(pos/slotms); - float mx = pow(max(max(c.r,c.g),c.b),1.33); - 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 = mix(1.0-slotmask, 1.0-0.80*slotmask, mx); - float slot = 1.0 + 0.7*slotmask*(1.0-mx); - 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 *= SourceSize.xy / InputSize.xy; - 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; -} - -void main() -{ - float lum = COMPAT_TEXTURE(PassPrev5Texture, vec2(0.1,0.1)).a; - - // Calculating texel coordinates - - vec2 texcoord = TEX0.xy; - if (IOS > 0.0){ - vec2 ofactor = OutputSize.xy/InputSize.xy; - vec2 intfactor = round(ofactor); - vec2 diff = ofactor/intfactor; - float scan = mix(diff.y, diff.x, TATE); - texcoord = Overscan(texcoord*(SourceSize.xy/InputSize.xy), scan, scan)*(InputSize.xy/SourceSize.xy); - if (IOS == 1.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*(SourceSize.xy/InputSize.xy), factor, factor)*(InputSize.xy/SourceSize.xy); - vec2 pos = Warp(texcoord*(TextureSize.xy/InputSize.xy))*(InputSize.xy/TextureSize.xy); - vec2 pos0 = Warp(TEX0.xy*(TextureSize.xy/InputSize.xy))*(InputSize.xy/TextureSize.xy); - - vec2 coffset = vec2(0.5, 0.5); - bool interb = (interm > 0.5 && interm < 2.5 && inter <= mix(SourceSize.y, SourceSize.x, TATE)); - bool notate = (TATE < 0.5); - - if (interb) coffset = ((notate) ? vec2(0.5,0.0) : vec2(0.0, 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; - - 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 twl3 = max(wl3 - sharp1, 0.0); - float twl2 = max(wl2 - sharp1, mix(0.0,mix(-0.18, -0.01, fpx),float(s_sharp > 0.05))); - float twl1 = max(wl1 - sharp1, 0.0); - float twr1 = max(wr1 - sharp1, 0.0); - float twr2 = max(wr2 - sharp1, mix(0.0,mix(-0.18, -0.01, fp1),float(s_sharp > 0.05))); - float twr3 = max(wr3 - sharp1, 0.0); - - float wtt = 1.0/(twl3+twl2+twl1+twr1+twr2+twr3); - float wt = 1.0/(wl2+wl1+wr1+wr2); - bool sharp = (s_sharp > 0.05); - - vec3 l3 = COMPAT_TEXTURE(PassPrev4Texture, pC4 -off2).xyz; - vec3 l2 = COMPAT_TEXTURE(PassPrev4Texture, pC4 -offx).xyz; - vec3 l1 = COMPAT_TEXTURE(PassPrev4Texture, pC4 ).xyz; - vec3 r1 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +offx).xyz; - vec3 r2 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +off2).xyz; - vec3 r3 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +offx+off2).xyz; - - vec3 sl2 = COMPAT_TEXTURE(Texture, pC4 -offx).xyz; - vec3 sl1 = COMPAT_TEXTURE(Texture, pC4 ).xyz; - vec3 sr1 = COMPAT_TEXTURE(Texture, pC4 +offx).xyz; - vec3 sr2 = COMPAT_TEXTURE(Texture, pC4 +off2).xyz; - - vec3 color1 = (l3*twl3 + l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2 + r3*twr3)*wtt; - - vec3 colmin = min(min(l1,r1), min(l2,r2)); - vec3 colmax = max(max(l1,r1), max(l2,r2)); - - if (sharp) color1 = clamp(color1, colmin, colmax); - - vec3 gtmp = vec3(gamma_out*0.1); - vec3 scolor1 = color1; - - scolor1 = (sl2*wl2 + sl1*wl1 + sr1*wr1 + sr2*wr2)*wt; - scolor1 = pow(scolor1, gtmp); vec3 mcolor1 = scolor1; - scolor1 = mix(color1, scolor1, spike); - - pC4+=offy; - - l3 = COMPAT_TEXTURE(PassPrev4Texture, pC4 -off2).xyz; - l2 = COMPAT_TEXTURE(PassPrev4Texture, pC4 -offx).xyz; - l1 = COMPAT_TEXTURE(PassPrev4Texture, pC4 ).xyz; - r1 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +offx).xyz; - r2 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +off2).xyz; - r3 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +offx+off2).xyz; - - sl2 = COMPAT_TEXTURE(Texture, pC4 -offx).xyz; - sl1 = COMPAT_TEXTURE(Texture, pC4 ).xyz; - sr1 = COMPAT_TEXTURE(Texture, pC4 +offx).xyz; - sr2 = COMPAT_TEXTURE(Texture, pC4 +off2).xyz; - - vec3 color2 = (l3*twl3 + l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2 + r3*twr3)*wtt; - - colmin = min(min(l1,r1), min(l2,r2)); - colmax = max(max(l1,r1), max(l2,r2)); - - if (sharp) color2 = clamp(color2, colmin, colmax); - - vec3 scolor2 = color2; - - scolor2 = (sl2*wl2 + sl1*wl1 + sr1*wr1 + sr2*wr2)*wt; - scolor2 = pow(scolor2, gtmp); vec3 mcolor2 = scolor2; - scolor2 = mix(color2, scolor2, spike); - - vec3 color0 = color1; - - if (interb) - { - pC4-= 2.*offy; - - l3 = COMPAT_TEXTURE(PassPrev4Texture, pC4 -off2).xyz; - l2 = COMPAT_TEXTURE(PassPrev4Texture, pC4 -offx).xyz; - l1 = COMPAT_TEXTURE(PassPrev4Texture, pC4 ).xyz; - r1 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +offx).xyz; - r2 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +off2).xyz; - r3 = COMPAT_TEXTURE(PassPrev4Texture, pC4 +offx+off2).xyz; - - color0 = (l3*twl3 + l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2 + r3*twr3)*wtt; - - colmin = min(min(l1,r1), min(l2,r2)); - colmax = max(max(l1,r1), max(l2,r2)); - - if (sharp) color0 = clamp(color0, colmin, colmax); - } - - // calculating scanlines - - 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; - vec3 mcolor = (mcolor1*wt1 + mcolor2*wt2)/(wt1+wt2); - - vec3 ctmp = color00/(wt1+wt2); - vec3 sctmp = scolor0/(wt1+wt2); - - vec3 tmp = pow(ctmp, vec3(1.0/gamma_out)); - mcolor = clamp(mix(ctmp, mcolor, 1.5),0.0,1.0); - mcolor = pow(mcolor, vec3(1.4/gamma_out)); - - vec3 w1,w2 = vec3(0.0); - - vec3 cref1 = mix(sctmp, scolor1, beam_size); - vec3 cref2 = mix(sctmp, scolor2, beam_size); - - vec3 shift = vec3(-vertmask, vertmask, -vertmask); if (vertmask < 0.0) shift = shift.grr; - - vec3 f1 = vec3(f); - vec3 f2 = vec3(1.0-f); - - f1 = max(f1 + shift*0.5*(1.0+f), 0.75*f); - f2 = max(f2 - shift*0.5*(2.0-f), 0.75*(1.0-f)); - - if (gsl == 0.0) { w1 = sw0(f1,cref1,shape1); w2 = sw0(f2,cref2,shape2);} else - if (gsl == 1.0) { w1 = sw1(f1,cref1,shape1); w2 = sw1(f2,cref2,shape2);} else - if (gsl == 2.0) { w1 = sw2(f1,cref1,shape1); w2 = sw2(f2,cref2,shape2);} - - vec3 color = color1*w1 + color2*w2; - color = min(color, 1.0); - - if (interm > 0.5 && inter <= mix(SourceSize.y, SourceSize.x, TATE)) - { - if (interm < 2.5) - { - float line_no = floor(mod(mix( OGL2Pos.y, OGL2Pos.x, TATE),2.0)); - float frame_no = floor(mod(float(global.FrameCount),2.0)); - float ii = (interm > 1.5) ? 0.5 : abs(line_no-frame_no); - - vec3 icolor1 = mix(color1, color0, ii); - vec3 icolor2 = mix(color1, color2, ii); - - color = mix(icolor1, icolor2, f); - mcolor = sqrt(color); - } - else color = mix(color1, color2, f); - } - - ctmp = 0.5*(ctmp+tmp); - color*=mix(brightboost, brightboost1, max(max(ctmp.r,ctmp.g),ctmp.b)); - - // Apply Mask - - vec3 orig1 = color; float pixbr = max(max(ctmp.r,ctmp.g),ctmp.b); vec3 orig = ctmp; w1 = w1+w2; float w3 = max(max(w1.r,w1.g),w1.b); - vec3 cmask = vec3(1.0); vec3 cmask1 = cmask; vec3 one = vec3(1.0); - - cmask*= (notate) ? Mask(gl_FragCoord.xy * 1.000001,mcolor) : - Mask(gl_FragCoord.yx * 1.000001,mcolor); - - color = color*cmask; - - color = min(color,1.0); - - cmask1 *= (notate) ? SlotMask(gl_FragCoord.xy * 1.000001,tmp) : - SlotMask(gl_FragCoord.yx * 1.000001,tmp); - - color = color*cmask1; cmask = cmask*cmask1; cmask = min(cmask, 1.0); - - vec3 Bloom = COMPAT_TEXTURE(PassPrev2Texture, pos).xyz; - - vec3 Bloom1 = 2.0*Bloom*Bloom; - Bloom1 = min(Bloom1, 0.75); - float bmax = max(max(Bloom1.r,Bloom1.g),Bloom1.b); - float pmax = 0.80; - Bloom1 = min(Bloom1, pmax*bmax)/pmax; - - Bloom1 = mix(min( Bloom1, color), Bloom1, 0.5*(orig1+color)); - - Bloom1 = bloom*Bloom1; - - color = color + Bloom1; - - color = min(color, 1.0); - if (interm < 0.5 || inter > mix(SourceSize.y, SourceSize.x, TATE)) color = declip(color, pow(w3,0.6)); - color = min(color, mix(cmask,one,0.6)); - - color = color + glow*Bloom; - - color = pow(color, vec3(1.0/gamma_out)); - - FragColor = vec4(color*corner(pos0), 1.0); -} diff --git a/crt/shaders/guest/crt-sm/blur_horiz-sm.slang b/crt/shaders/guest/crt-sm/blur_horiz-sm.slang deleted file mode 100644 index a144a66..0000000 --- a/crt/shaders/guest/crt-sm/blur_horiz-sm.slang +++ /dev/null @@ -1,66 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float TAPSH; - float GLOW_FALLOFF_H; -} params; - -// Higher value, more centered glow. -// Lower values might need more taps. -// Adapted from crt-easymode-halation by Easymode. - -#pragma parameter TAPSH "H. Glow Radius" 4.0 1.0 10.0 1.0 -#define TAPSH params.TAPSH -#pragma parameter GLOW_FALLOFF_H "Horizontal Glow Grade" 0.35 0.00 1.5 0.02 -#define GLOW_FALLOFF_H params.GLOW_FALLOFF_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 * 1.00001; -} - -#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 RotPass; - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define SourceSize params.SourceSize - -#define kernel(x) exp(-GLOW_FALLOFF_H * (x) * (x)) - -void main() -{ - float ratio = COMPAT_TEXTURE(RotPass, vec2(0.5,0.1)).a; - if (vTexCoord.y > ratio) discard; - - vec3 col = vec3(0.0); vec3 tmp; - float dx = SourceSize.z; - - float k_total = 0.; - for (float i = -TAPSH; i <= TAPSH; i++) - { - float k = kernel(i); - k_total += k; - tmp = COMPAT_TEXTURE(Source, vTexCoord + vec2(float(i) * dx, 0.0)).rgb; - col += k * tmp; - } - FragColor = vec4(col / k_total, ratio); -} \ No newline at end of file diff --git a/crt/shaders/guest/crt-sm/blur_vert-sm.slang b/crt/shaders/guest/crt-sm/blur_vert-sm.slang deleted file mode 100644 index 60f5851..0000000 --- a/crt/shaders/guest/crt-sm/blur_vert-sm.slang +++ /dev/null @@ -1,66 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float TAPSV; - float GLOW_FALLOFF_V; -} params; - -// Higher value, more centered glow. -// Lower values might need more taps. -// Adapted from crt-easymode-halation by Easymode. - -// Parameter lines go here: -#pragma parameter TAPSV "V. Glow Radius" 4.0 1.0 10.0 1.0 -#define TAPSV params.TAPSV -#pragma parameter GLOW_FALLOFF_V "Vertical Glow Grade" 0.35 0.00 1.5 0.02 -#define GLOW_FALLOFF_V params.GLOW_FALLOFF_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 * 1.00001; -} - -#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 RotPass; - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define SourceSize params.SourceSize - -#define kernel(x) exp(-GLOW_FALLOFF_V * (x) * (x)) - -void main() -{ - float ratio = COMPAT_TEXTURE(RotPass, vec2(0.5,0.1)).a; - if (vTexCoord.y > ratio) discard; - - vec3 col = vec3(0.0); - float dy = SourceSize.w; - - float k_total = 0.; - for (float i = -TAPSV; i <= TAPSV; i++) - { - float k = kernel(i); - k_total += k; - col += k * COMPAT_TEXTURE(Source, vTexCoord + vec2(0.0, float(i) * dy)).rgb; - } - FragColor = vec4(col / k_total, ratio); -} \ No newline at end of file diff --git a/crt/shaders/guest/crt-sm/crt-guest-sm-rot0.slang b/crt/shaders/guest/crt-sm/crt-guest-sm-rot0.slang deleted file mode 100644 index 00339da..0000000 --- a/crt/shaders/guest/crt-sm/crt-guest-sm-rot0.slang +++ /dev/null @@ -1,59 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; -} params; - - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#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.00001; -} - -#pragma stage fragment -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -float Overscan(float pos, float dy){ - pos=pos*2.0-1.0; - pos*=dy; - return pos*0.5+0.5; -} - -void main() -{ - float sm_tate = COMPAT_TEXTURE(Source, vec2(0.5)).a; - float ratio = 1.0; - vec2 tex = TEX0.xy; vec3 color = vec3(0.0); - - if (sm_tate < 0.5) { ratio = 1.0/3.0; tex.y = tex.y / ratio; color = COMPAT_TEXTURE(Source, tex).rgb; } - else - { - ratio = (1.0/3.0) * params.SourceSize.x / params.SourceSize.y; - tex = tex.yx; - tex.x = tex.x / ratio; - tex.y = Overscan(tex.y, params.SourceSize.x / params.SourceSize.y); - color = COMPAT_TEXTURE(Source, tex).rgb; - } - - FragColor = vec4(color,ratio); -} \ No newline at end of file diff --git a/crt/shaders/guest/crt-sm/crt-guest-sm.slang b/crt/shaders/guest/crt-sm/crt-guest-sm.slang deleted file mode 100644 index 9c05780..0000000 --- a/crt/shaders/guest/crt-sm/crt-guest-sm.slang +++ /dev/null @@ -1,479 +0,0 @@ -#version 450 - -/* - CRT - Guest - SM (Scanline Mask) Shader - - Copyright (C) 2019-2020 guest(r) - guest.r@gmail.com - - Big thanks to Nesguy from the Libretro forums for the masks and other ideas. - - 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. - -*/ - -/* README - MASKS GUIDE - -To obtain the best results with masks 0, 1, 3, 4: -must leave “mask size†at 1 and the display must be set to its native resolution to result in evenly spaced “active†LCD subpixels. - -Mask 0: Uses a magenta and green pattern for even spacing of the LCD subpixels. - -Mask 1: Similar to Mask 0, but with "ZigZag" - -Mask 2: Intended for displays that have RBG subpixels (as opposed to the more common RGB). -Uses a yellow/blue pattern for even spacing of the LCD subpixels. - -Mask 3: Common red/green/blue pattern. - -Mask 4: This is useful for 4K displays, where masks 0 and 1 can look too fine. -Uses a red/yellow/cyan/blue pattern to result in even spacing of the LCD subpixels. - -Mask 5: Intended for displays that have the less common RBG subpixel pattern. -This is useful for 4K displays, where masks 0 and 1 can look too fine. -Uses a red/magenta/cyan/green pattern for even spacing of the LCD subpixels. - -*/ - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - // vec4 OriginalSize; - vec4 OutputSize; - - uint FrameCount; - float smart, brightboost1, brightboost2, stype, scanline1, scanline2, beam_min, beam_max, s_beam; - float h_sharp, cubic, mask, maskmode, maskdark, maskbright, masksize, gamma_out; -} params; - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 FinalViewportSize; - float bglow; - float warpx; - float warpy; - float bloom; - float halation; - float autobrm; - float sclip; -} global; - -#pragma parameter bglow "Base Glow" 0.0 0.0 1.0 0.01 -#pragma parameter bloom "Bloom" 0.40 0.0 2.0 0.05 -#pragma parameter halation "Halation" 0.0 0.0 2.0 0.05 -#pragma parameter autobrm "Automatic Brightness (Mask)" 0.5 0.0 1.0 0.1 -#pragma parameter smart "1:Smart 2:Crop 3:Overscan Y Integer Scaling" 0.0 0.0 3.0 1.0 -#pragma parameter brightboost1 "Bright boost dark colors" 1.40 0.5 5.0 0.10 -#pragma parameter brightboost2 "Bright boost bright colors" 1.15 0.5 3.0 0.05 -#pragma parameter stype "Scanline Type" 1.0 0.0 3.0 1.0 -#pragma parameter scanline1 "Scanline Shape Center" 5.0 2.0 20.0 0.5 -#pragma parameter scanline2 "Scanline Shape Edges" 7.0 4.0 20.0 0.5 -#pragma parameter beam_min "Scanline dark" 1.25 0.5 3.0 0.05 -#pragma parameter beam_max "Scanline bright" 1.10 0.5 3.0 0.05 -#pragma parameter sclip "Allow Scanline/Mask Clipping With Bloom" 0.50 0.0 1.0 0.05 -#pragma parameter s_beam "Overgrown Bright Beam" 0.70 0.0 1.0 0.05 -#pragma parameter h_sharp "Horizontal sharpness" 3.0 1.0 10.0 0.10 -#pragma parameter cubic "Cubic Filtering" 1.0 0.0 1.0 0.10 -#pragma parameter mask "CRT Mask (4&5 are 4k masks)" 0.0 0.0 5.0 1.0 -#pragma parameter maskmode "CRT Mask Mode: Classic, Fine, Coarse" 0.0 0.0 2.0 1.0 -#pragma parameter maskdark "CRT Mask Strength Dark Pixels" 1.0 0.0 1.5 0.05 -#pragma parameter maskbright "CRT Mask Strength Bright Pixels" 0.25 -0.5 1.0 0.05 -#pragma parameter masksize "CRT Mask Size" 1.0 1.0 2.0 1.0 -#pragma parameter warpx "Curvature X" 0.0 0.0 0.25 0.01 -#pragma parameter warpy "Curvature Y" 0.0 0.0 0.25 0.01 -#pragma parameter gamma_out "Gamma Out" 2.50 1.0 3.5 0.05 - -#define bglow global.bglow -#define autobrm global.autobrm -#define smart params.smart -#define brightboost1 params.brightboost1 -#define brightboost2 params.brightboost2 -#define bloom global.bloom -#define halation global.halation -#define stype params.stype -#define scanline1 params.scanline1 -#define scanline2 params.scanline2 -#define beam_min params.beam_min -#define beam_max params.beam_max -#define sclip global.sclip -#define s_beam params.s_beam -#define h_sharp params.h_sharp -#define cubic params.cubic -#define mask params.mask -#define maskmode params.maskmode -#define maskdark params.maskdark -#define maskbright params.maskbright -#define masksize params.masksize -#define warpx global.warpx -#define warpy global.warpy -#define gamma_out params.gamma_out - - -#define TEX0 vTexCoord -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define Texture Source -#define InputSize SourceSize -#define gl_FragCoord (vTexCoord * params.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; -} - -#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 WpPass; -layout(set = 0, binding = 4) uniform sampler2D RotPass; -layout(set = 0, binding = 5) uniform sampler2D LinPass; - -float st(float x) -{ - return exp2(-10.0*x*x); -} - - - -vec3 sw0(float x, vec3 color, float scan) -{ - vec3 tmp = mix(vec3(beam_min),vec3(beam_max), color); - vec3 ex = x*tmp; - return exp2(-scan*ex*ex); -} - - -vec3 sw1(float x, vec3 color, float scan) -{ - float mx1 = max(max(color.r,color.g),color.b); - vec3 tmp = mix(vec3(2.50*beam_min),vec3(beam_max), color); - tmp = mix(vec3(beam_max), tmp, pow(vec3(x), color + 0.30)); - vec3 ex = vec3(x)*tmp; - vec3 res = exp2(-scan*ex*ex); - float mx2 = max(max(res.r,res.g),res.b); - float br = clamp(mix(0.30, 0.50, 2.0*(beam_min-1.0)),0.10, 0.60); - return mix(vec3(mx2), res, 0.50)/(1.0 - br + br*mx1); -} - - -vec3 sw2(float x, vec3 color, float scan) -{ - float mx1 = max(max(color.r,color.g),color.b); - vec3 ex = mix(vec3(2.0*beam_min), vec3(beam_max), color); - vec3 m = min(0.3 + 0.35*ex, 1.0); - ex = x*ex; - vec3 xx = ex*ex; - xx = mix(xx, ex*xx, m); - vec3 res = exp2(-1.25*scan*xx); - float mx2 = max(max(res.r,res.g),res.b); - float br = clamp(mix(0.20, 0.50, 2.0*(beam_min-1.0)),0.10, 0.60); - return mix(vec3(mx2), res, 0.50)/(1.0 - br + br*mx1); -} - - -float Overscan(float pos, float dy){ - pos=pos*2.0-1.0; - pos*=dy; - return pos*0.5+0.5; -} - - -// Distortion of scanlines, and end of screen alpha (PD CRT 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; -} - -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 bd, float mb) -{ - float m = max(max(c.r,c.g),c.b)+0.00001; - float b2 = mix(bd, 1.0, pow(m,0.50)); - return b2*c; -} - -void main() -{ - vec2 tex = TEX0.xy * 1.00001; - float sm_tate = COMPAT_TEXTURE(WpPass, vec2(0.5)).a; - float ratio = COMPAT_TEXTURE(RotPass, vec2(0.5, 0.1)).a; - - vec4 SourceSize1 = params.SourceSize; - float vertres = SourceSize1.y*ratio; - - tex.y *= ratio; - if (sm_tate > 0.25) { tex.x = Overscan(tex.x, (1.0/3.0)*SourceSize1.y/SourceSize1.x); } - - float factor = params.OutputSize.y/vertres; - - float gamma = COMPAT_TEXTURE(LinPass, vec2(0.5,0.1)).a; - - if (smart == 1.0 || smart == 2.0 || smart == 3.0) - { - float intfactor = round(factor); if (smart == 2.0) intfactor = floor(factor); if (smart == 3.0) intfactor = ceil(factor); - float diff = factor/intfactor; - tex.y = Overscan(tex.y/(ratio), diff)*ratio; - } - - tex = Warp(tex/vec2(1.0,ratio))*vec2(1.0,ratio); - - vec2 OGL2Pos = tex * SourceSize1.xy - vec2(0.5,0.5); - vec2 fp = fract(OGL2Pos); - float fpx = fp.x; - float fp1 = 1.0-fpx; - - vec2 pC4 = (floor(OGL2Pos) + vec2(0.5)) * SourceSize1.zw; - - // Reading the texels - vec2 dx = vec2(SourceSize1.z,0.0); - vec2 dy = vec2(0.0,SourceSize1.w); - vec2 x2 = dx+dx; - float zero = mix(0.0, exp2(-h_sharp), cubic); - - float wl2 = 1.0 + fp.x; - float wl1 = fp.x; - float wr1 = 1.0 - fp.x; - float wr2 = 2.0 - fp.x; - - 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); - - wl2 = max(wl2 - zero, mix(0.0,mix(-0.14, 0.0, 1.0-fp1*fp1),float(cubic > 0.05))); - wl1 = max(wl1 - zero, 0.0); - wr1 = max(wr1 - zero, 0.0); - wr2 = max(wr2 - zero, mix(0.0,mix(-0.14, 0.0, 1.0-fpx*fpx),float(cubic > 0.05))); - - float sl2 = max(wl2,0.0); - float sl1 = wl1; - float sr1 = wr1; - float sr2 = max(wr2,0.0); - - float wtt = 1.0/(wl2+wl1+wr1+wr2); - float wts = 1.0/(sl2+sl1+sr1+sr2); - - vec3 l2 = COMPAT_TEXTURE(LinPass, pC4 - dx).rgb; - vec3 l1 = COMPAT_TEXTURE(LinPass, pC4 ).rgb; - vec3 r1 = COMPAT_TEXTURE(LinPass, pC4 + dx).rgb; - vec3 r2 = COMPAT_TEXTURE(LinPass, pC4 + x2).rgb; - - vec3 color1 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt; - - vec3 colmin = min(min(l2,l1),min(r1,r2)); - vec3 colmax = max(max(l2,l1),max(r1,r2)); - - if (cubic > 0.05) color1 = clamp(color1, colmin, colmax); - - l1*=l1; l1*=l1*l1; r1*=r1; r1*=r1*r1; l2*=l2; l2*=l2*l2; r2*=r2; r2*=r2*r2; - vec3 scolor1 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts; - scolor1 = pow(scolor1, vec3(1.0/6.0)); vec3 mscolor1 = scolor1; - - scolor1 = mix(color1, scolor1, 1.0); - - pC4+=dy; - l2 = COMPAT_TEXTURE(LinPass, pC4 - dx).rgb; - l1 = COMPAT_TEXTURE(LinPass, pC4 ).rgb; - r1 = COMPAT_TEXTURE(LinPass, pC4 + dx).rgb; - r2 = COMPAT_TEXTURE(LinPass, pC4 + x2).rgb; - - vec3 color2 = (wl2*l2+wl1*l1+wr1*r1+wr2*r2)*wtt; - - colmin = min(min(l2,l1),min(r1,r2)); - colmax = max(max(l2,l1),max(r1,r2)); - - if (cubic > 0.05) color2 = clamp(color2, colmin, colmax); - - l1*=l1; l1*=l1*l1; r1*=r1; r1*=r1*r1; l2*=l2; l2*=l2*l2; r2*=r2; r2*=r2*r2; - vec3 scolor2 = (sl2*l2+sl1*l1+sr1*r1+sr2*r2)*wts; - scolor2 = pow(scolor2, vec3(1.0/6.0)); vec3 mscolor2 = scolor2; - - scolor2 = mix(color2, scolor2, 1.0); - - float f1 = fp.y; - float f2 = 1.0 - fp.y; - float f3 = fract(tex.y * SourceSize1.y); - - vec3 color; - float t1 = st(f1); - float t2 = st(f2); - float wt = 1.0/(t1+t2); - -// calculating scanlines - - float scan1 = mix(scanline1, scanline2, f1); - float scan2 = mix(scanline1, scanline2, f2); - - vec3 sctemp = (t1*scolor1 + t2*scolor2)*wt; - vec3 msctemp = (t1*mscolor1 + t2*mscolor2)*wt; - - vec3 ref1 = mix(sctemp, scolor1.rgb, s_beam); ref1 = pow(ref1, mix(vec3(1.25), vec3(0.65), ref1)); - vec3 ref2 = mix(sctemp, scolor2.rgb, s_beam); ref2 = pow(ref2, mix(vec3(1.25), vec3(0.65), ref2)); - - vec3 w1, w2 = vec3(0.0); - - if (stype < 0.5) - { - w1 = sw0(f1, ref1, scan1); - w2 = sw0(f2, ref2, scan2); - } - else - if (stype < 1.5) - { - w1 = sw1(f1, ref1, scan1); - w2 = sw1(f2, ref2, scan2); - } - else - if (stype < 2.5) - { - w1 = sw2(f1, ref1, scan1); - w2 = sw2(f2, ref2, scan2); - } - else - { - w1 = vec3(f2); - w2 = vec3(f1); - } - - - vec3 ctemp = (t1*color1 + t2*color2)*wt; vec3 orig = ctemp; float pixbr = max(max(orig.r,orig.g),orig.b); vec3 one = vec3(1.0); - vec3 tmp1 = clamp(mix(orig, msctemp, 1.25),0.0,1.0); - ctemp = w1+w2; - float w3 = max(max(ctemp.r,ctemp.g),ctemp.b); - - tmp1 = pow(tmp1, vec3(0.65)); - float pixbr1 = max(max(tmp1.r,tmp1.g),tmp1.b); - - float maskd = mix(min(maskdark,1.0), 0.25*max(maskbright,0.0), pixbr1); if (mask == 3.0 || mask == 4.0) maskd*=1.33; maskd = mix(1.0, 1.0/(1.0-0.5*maskd), autobrm); - maskd = mix(maskd, 1.0, pow(pixbr,0.85)); - - float brightboost_d = brightboost1; - float brightboost_b = brightboost2; - - if (stype == 1.0) { brightboost_d = min(brightboost1, 1.40); maskd = 1.0; } - - color1 = gc(color1, brightboost_d, maskd); - color2 = gc(color2, brightboost_d, maskd); - - color1 = min(color1, 1.0); - color2 = min(color2, 1.0); - - color = w1*color1.rgb + w2*color2.rgb; - color = maskd*color; - - vec3 scan3 = vec3(0.0); - - float spos = (gl_FragCoord.x); - float spos2 = floor(1.000001*gl_FragCoord.x/masksize) + floor(1.000001*gl_FragCoord.y/masksize); - - spos = floor((spos * 1.000001)/masksize); float spos1 = 0.0; - - - if (mask == 0.0 || mask == 1.0) - { - if (mask == 1.0) spos = spos2; - spos1 = fract(spos*0.5); - if (spos1 < 0.5) scan3.rb = one.rb; - else scan3.g = one.g; - } - else - if (mask == 2.0) - { - spos1 = fract(spos*0.5); - if (spos1 < 0.5) scan3.rg = one.rg; - else scan3.b = one.b; - } - else - if (mask == 3.0) - { - spos1 = fract(spos/3.0); - if (spos1 < 0.3333) scan3.r = one.r; - else if (spos1 < 0.6666) scan3.g = one.g; - else scan3.b = one.b; - } - else - if (mask == 4.0) - { - spos1 = fract(spos*0.25); - if (spos1 < 0.25) scan3.r = one.r; - else if (spos1 < 0.50) scan3.rg = one.rg; - else if (spos1 < 0.75) scan3.gb = one.gb; - else scan3.b = one.b; - } - else - { - spos1 = fract(spos*0.25); - if (spos1 < 0.25) scan3.r = one.r; - else if (spos1 < 0.50) scan3.rb = one.rb; - else if (spos1 < 0.75) scan3.gb = one.gb; - else scan3.g = one.g; - } - - vec3 mixmask = tmp1; - if (maskmode == 1.0) mixmask = vec3(pixbr1); else - if (maskmode == 2.0) mixmask = tmp1*w3; - - vec3 cmask = clamp(mix( mix(one, scan3, maskdark), mix(one, scan3, maskbright), mixmask), 0.0, 1.0); - vec3 orig1 = color; - color = color*cmask*brightboost_b; - - vec3 Bloom = COMPAT_TEXTURE(Source, tex).rgb; - vec3 Bglow = COMPAT_TEXTURE(LinPass, tex).rgb; - Bglow = clamp(Bloom - Bglow,0.0,1.0); - vec3 hglow = 0.5*(Bloom + Bglow); - float maxb = max(max(hglow.r,hglow.g),hglow.b); maxb*=maxb; - - vec3 Bloom1 = 2.0*Bloom*Bloom; - Bloom1 = min(Bloom1, 0.75); - float bmax = max(max(Bloom1.r,Bloom1.g),Bloom1.b); - float pmax = 0.85; - Bloom1 = min(Bloom1, pmax*bmax)/pmax; - - Bloom1 = mix(min( Bloom1, 0.5*(orig1+color)), Bloom1, 0.5*(orig1+color)); - Bloom1 = Bloom1*mix(w1+w2,one,1.0-color); - - Bloom1 = bloom*Bloom1*cmask; - - color = color + Bloom1; - color = min(color,1.0); - color = declip(color, pow(w3, 1.0-sclip)); - - float colmx = pixbr1; - Bloom = mix(0.5*(Bloom + Bloom*Bloom), Bloom*Bloom, colmx); - color = color + 0.75*(0.75+maxb)*Bloom*(0.75+sqrt(colmx))*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.35 + 0.4*maxb)*halation; - - color = color + bglow*Bglow; - color = min(color, mix(cmask,one,sclip)); - - float fgamma = 1.0/gamma_out; - if (stype == 1.0) fgamma = gamma; - vec3 color1g = pow(color, vec3(fgamma)); - - FragColor = vec4(color1g, 1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/crt-sm/d65-d50-sm.slang b/crt/shaders/guest/crt-sm/d65-d50-sm.slang deleted file mode 100644 index e3932f4..0000000 --- a/crt/shaders/guest/crt-sm/d65-d50-sm.slang +++ /dev/null @@ -1,97 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float WP; - float wp_saturation; - float tate, fliph, flipv; -} params; - -#pragma parameter tate "TATE Mode" 0.0 0.0 1.0 1.0 -#pragma parameter fliph "Flip The Image Horizontally" -1.0 -1.0 1.0 2.0 -#pragma parameter flipv "Flip The Image Vertically" 1.0 -1.0 1.0 2.0 -#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 -#define tate params.tate -#define fliph params.fliph -#define flipv params.flipv - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#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 * mix(vec4(1.0), vec4(flipv, fliph, 1.0, 1.0), tate); - 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; - -const mat3 D65_to_XYZ = mat3 ( - 0.4306190, 0.2220379, 0.0201853, - 0.3415419, 0.7066384, 0.1295504, - 0.1783091, 0.0713236, 0.9390944); - -const mat3 XYZ_to_D65 = mat3 ( - 3.0628971, -0.9692660, 0.0678775, - -1.3931791, 1.8760108, -0.2288548, - -0.4757517, 0.0415560, 1.0693490); - -const mat3 D50_to_XYZ = mat3 ( - 0.4552773, 0.2323025, 0.0145457, - 0.3675500, 0.7077956, 0.1049154, - 0.1413926, 0.0599019, 0.7057489); - -const mat3 XYZ_to_D50 = mat3 ( - 2.9603944, -0.9787684, 0.0844874, - -1.4678519, 1.9161415, -0.2545973, - -0.4685105, 0.0334540, 1.4216174); - -void main() -{ - vec3 color = COMPAT_TEXTURE(Source, TEX0.xy).rgb; - - color = normalize(pow(color + 1e-4, vec3(wp_saturation)))*length(color); - - float p = 2.4; - - color = pow(color, vec3(p)); - - vec3 warmer = D50_to_XYZ*color; - warmer = XYZ_to_D65*warmer; - - vec3 cooler = D65_to_XYZ*color; - cooler = XYZ_to_D50*cooler; - - float m = abs(WP)/100.0; - - vec3 comp = (WP < 0.0) ? cooler : warmer; - - color = mix(color, comp, m); - - color = pow(color, vec3(1.0/p)); - - float a = 0.0; if (tate > 0.5) a = 0.5; - - FragColor = vec4(color,a); -} \ No newline at end of file diff --git a/crt/shaders/guest/crt-sm/linearize-sm.slang b/crt/shaders/guest/crt-sm/linearize-sm.slang deleted file mode 100644 index aedb5a6..0000000 --- a/crt/shaders/guest/crt-sm/linearize-sm.slang +++ /dev/null @@ -1,41 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - float GAMMA_INPUT; -} params; - -#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 0.5 5.0 0.05 -#define GAMMA_INPUT params.GAMMA_INPUT - -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; - -void main() -{ - float ratio = texture(Source, vec2(0.5,0.1)).a; - if (vTexCoord.y > ratio) discard; - float gamma = 2.4; - if ( (GAMMA_INPUT > 0.4) || (GAMMA_INPUT < 4.1) ) gamma = GAMMA_INPUT; - gamma = 1.0/gamma; - - FragColor = vec4(pow(vec3(texture(Source, vTexCoord).rgb), vec3(GAMMA_INPUT)),gamma); -} \ No newline at end of file diff --git a/crt/shaders/guest/d65-d50.slang b/crt/shaders/guest/d65-d50.slang deleted file mode 100644 index d387111..0000000 --- a/crt/shaders/guest/d65-d50.slang +++ /dev/null @@ -1,88 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float WP; - float wp_saturation; -} params; - -#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 - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#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; -} - -#pragma stage fragment -layout(location = 0) in vec2 vTexCoord; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -const mat3 D65_to_XYZ = mat3 ( - 0.4306190, 0.2220379, 0.0201853, - 0.3415419, 0.7066384, 0.1295504, - 0.1783091, 0.0713236, 0.9390944); - -const mat3 XYZ_to_D65 = mat3 ( - 3.0628971, -0.9692660, 0.0678775, - -1.3931791, 1.8760108, -0.2288548, - -0.4757517, 0.0415560, 1.0693490); - -const mat3 D50_to_XYZ = mat3 ( - 0.4552773, 0.2323025, 0.0145457, - 0.3675500, 0.7077956, 0.1049154, - 0.1413926, 0.0599019, 0.7057489); - -const mat3 XYZ_to_D50 = mat3 ( - 2.9603944, -0.9787684, 0.0844874, - -1.4678519, 1.9161415, -0.2545973, - -0.4685105, 0.0334540, 1.4216174); - -void main() -{ - vec3 color = COMPAT_TEXTURE(Source, TEX0.xy).rgb; - - color = normalize(pow(color + 1e-4, vec3(wp_saturation)))*length(color); - - float p = 2.4; - - color = pow(color, vec3(p)); - - vec3 warmer = D50_to_XYZ*color; - warmer = XYZ_to_D65*warmer; - - vec3 cooler = D65_to_XYZ*color; - cooler = XYZ_to_D50*cooler; - - float m = abs(WP)/100.0; - - vec3 comp = (WP < 0.0) ? cooler : warmer; - - color = mix(color, comp, m); - - color = pow(color, vec3(1.0/p)); - - FragColor = vec4(color,1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/fast/bloom_horizontal.slang b/crt/shaders/guest/fast/bloom_horizontal.slang index 1e88d1d..a962146 100644 --- a/crt/shaders/guest/fast/bloom_horizontal.slang +++ b/crt/shaders/guest/fast/bloom_horizontal.slang @@ -21,11 +21,9 @@ */ -#pragma format R16G16B16A16_SFLOAT - layout(push_constant) uniform Push { - vec4 LinearizePassSize; + vec4 SourceSize; vec4 OriginalSize; vec4 OutputSize; uint FrameCount; @@ -38,7 +36,7 @@ layout(push_constant) uniform Push #pragma parameter SIZEHB " H. Bloom/Halation/Glow Radius" 4.0 1.0 30.0 1.0 #define SIZEHB params.SIZEHB -#pragma parameter SIGMA_HB " Horizontal Bloom/Halation/Glow Sigma" 0.70 0.5 15.0 0.05 +#pragma parameter SIGMA_HB " Horizontal Bloom/Halation/Glow Sigma" 1.0 0.25 15.0 0.05 #define SIGMA_HB params.SIGMA_HB layout(std140, set = 0, binding = 0) uniform UBO diff --git a/crt/shaders/guest/fast/bloom_vertical.slang b/crt/shaders/guest/fast/bloom_vertical.slang index bc7a423..4c45599 100644 --- a/crt/shaders/guest/fast/bloom_vertical.slang +++ b/crt/shaders/guest/fast/bloom_vertical.slang @@ -21,7 +21,6 @@ */ -#pragma format R16G16B16A16_SFLOAT layout(push_constant) uniform Push { @@ -37,7 +36,7 @@ layout(push_constant) uniform Push #pragma parameter SIZEVB " V. Bloom/Halation/Glow Radius" 4.0 1.0 30.0 1.0 #define SIZEVB params.SIZEVB -#pragma parameter SIGMA_VB " Vertical Bloom/Halation/Glow Sigma" 0.70 0.5 15.0 0.05 +#pragma parameter SIGMA_VB " Vertical Bloom/Halation/Glow Sigma" 1.0 0.25 15.0 0.05 #define SIGMA_VB params.SIGMA_VB layout(std140, set = 0, binding = 0) uniform UBO diff --git a/crt/shaders/guest/fast/crt-guest-advanced-pass1.slang b/crt/shaders/guest/fast/crt-guest-advanced-pass1.slang index 21af228..4525a4f 100644 --- a/crt/shaders/guest/fast/crt-guest-advanced-pass1.slang +++ b/crt/shaders/guest/fast/crt-guest-advanced-pass1.slang @@ -30,7 +30,7 @@ layout(push_constant) uniform Push vec4 OriginalSize; vec4 OutputSize; uint FrameCount; - float IOS, h_sharp, s_sharp, spike; + float IOS, h_sharp, s_sharp, spike, ring; float prescalex; } params; @@ -48,6 +48,9 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 ring " Substractive sharpness Ringing" 0.0 0.0 3.0 0.05 +#define ring params.ring // substractive sharpness ringing + #pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 #define spike params.spike @@ -68,7 +71,7 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + vTexCoord = TexCoord * 1.0001; } #pragma stage fragment @@ -146,18 +149,19 @@ void main() 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); - float ts = 0.033; + if (sharp) color1 = clamp(mix(clamp(color1, colmin, colmax), color1, ring), 0.0, 1.0); + float ts = 0.025; + vec3 luma = vec3(0.2126, 0.7152, 0.0722); float lm2 = max(max(l2.r,l2.g),l2.b); float lm1 = max(max(l1.r,l1.g),l1.b); float rm1 = max(max(r1.r,r1.g),r1.b); float rm2 = max(max(r2.r,r2.g),r2.b); - float swl2 = max(twl2,0.0) * (lm2+ts); - float swl1 = twl1 * (lm1+ts); - float swr1 = twr1 * (rm1+ts); - float swr2 = max(twr2,0.0) * (rm2+ts); + float swl2 = max(twl2, 0.0) * (dot(l2,luma) + ts); + float swl1 = twl1 * (dot(l1,luma) + ts); + float swr1 = twr1 * (dot(r1,luma) + ts); + float swr2 = max(twr2, 0.0) * (dot(r2,luma) + ts); float fscolor1 = (lm2*swl2 + lm1*swl1 + rm1*swr1 + rm2*swr2)/(swl2+swl1+swr1+swr2); float sresult = clamp(mix(max(max(color1.r,color1.g),color1.b), fscolor1, spike), 0.0, 1.0); diff --git a/crt/shaders/guest/fast/crt-guest-advanced-pass1f.slang b/crt/shaders/guest/fast/crt-guest-advanced-pass1f.slang index 1e4a298..c8687f4 100644 --- a/crt/shaders/guest/fast/crt-guest-advanced-pass1f.slang +++ b/crt/shaders/guest/fast/crt-guest-advanced-pass1f.slang @@ -30,7 +30,7 @@ layout(push_constant) uniform Push vec4 OriginalSize; vec4 OutputSize; uint FrameCount; - float IOS, h_sharp, s_sharp, spike; + float IOS, h_sharp, s_sharp, spike, ring; } params; layout(std140, set = 0, binding = 0) uniform UBO @@ -47,6 +47,9 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 ring " Substractive sharpness Ringing" 0.0 0.0 3.0 0.05 +#define ring params.ring // substractive sharpness ringing + #pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 #define spike params.spike @@ -65,7 +68,7 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + vTexCoord = TexCoord * 1.0001; } #pragma stage fragment @@ -100,57 +103,50 @@ void main() 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 twl3 = max(wl3 - sharp1, 0.0); float twl2 = max(wl2 - sharp1, mix(-0.12, 0.0, 1.0-fp1*fp1)); 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 twr3 = max(wr3 - sharp1, 0.0); bool sharp = (sharp1 > 0.0); - vec3 l3, l2, l1, r1, r2, r3, color1, colmin, colmax; + vec3 l2, l1, r1, r2, color1, colmin, colmax; - l3 = COMPAT_TEXTURE(LinearizePass, pC4 -x2).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 +x2).rgb; - r3 = COMPAT_TEXTURE(LinearizePass, pC4 +offx+x2).rgb; colmin = min(min(l1,r1), min(l2,r2)); colmax = max(max(l1,r1), max(l2,r2)); - color1 = (l3*twl3 + l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2 + r3*twr3)/(twl3+twl2+twl1+twr1+twr2+twr3); + color1 = (l2*twl2 + l1*twl1 + r1*twr1 + r2*twr2)/(twl2+twl1+twr1+twr2); - if (sharp) color1 = clamp(color1, colmin, colmax); - float ts = 0.033; + if (sharp) color1 = clamp(mix(clamp(color1, colmin, colmax), color1, ring), 0.0, 1.0); + float ts = 0.025; + vec3 luma = vec3(0.2126, 0.7152, 0.0722); float lm2 = max(max(l2.r,l2.g),l2.b); float lm1 = max(max(l1.r,l1.g),l1.b); float rm1 = max(max(r1.r,r1.g),r1.b); float rm2 = max(max(r2.r,r2.g),r2.b); - float swl2 = max(twl2,0.0) * (lm2+ts); - float swl1 = twl1 * (lm1+ts); - float swr1 = twr1 * (rm1+ts); - float swr2 = max(twr2,0.0) * (rm2+ts); + float swl2 = max(twl2, 0.0) * (dot(l2,luma) + ts); + float swl1 = twl1 * (dot(l1,luma) + ts); + float swr1 = twr1 * (dot(r1,luma) + ts); + float swr2 = max(twr2, 0.0) * (dot(r2,luma) + ts); float fscolor1 = (lm2*swl2 + lm1*swl1 + rm1*swr1 + rm2*swr2)/(swl2+swl1+swr1+swr2); float sresult = clamp(mix(max(max(color1.r,color1.g),color1.b), fscolor1, spike), 0.0, 1.0); diff --git a/crt/shaders/guest/fast/crt-guest-advanced-pass2.slang b/crt/shaders/guest/fast/crt-guest-advanced-pass2.slang index 69a1309..cde68ab 100644 --- a/crt/shaders/guest/fast/crt-guest-advanced-pass2.slang +++ b/crt/shaders/guest/fast/crt-guest-advanced-pass2.slang @@ -3,7 +3,7 @@ /* CRT - Guest - Advanced - Fast - Pass2 - Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com Incorporates many good ideas and suggestions from Dr. Venom. I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. @@ -27,8 +27,7 @@ layout(push_constant) uniform Push { float brightboost, brightboost1, gsl, scanline1, scanline2, beam_min, beam_max, beam_size, - glow, shadowMask, masksize, vertmask, slotmask, slotwidth, double_slot, mcut, maskDark, maskLight, - maskstr, inters, bloom, halation, scans, slotms, mclip, gamma_c, gamma_out, DER, DEG, DEB, DES, IOS; + glow, vertmask, maskstr, inters, bloom, halation, scans, gamma_c, gamma_out, IOS; } params; layout(std140, set = 0, binding = 0) uniform UBO @@ -42,13 +41,17 @@ layout(std140, set = 0, binding = 0) uniform UBO float warpX; float warpY; float csize; - float bsize; + float bsize1; float intres; float c_shape; float barspeed; float barintensity; float bardir; - float slotmask1; + float sborder; + float scan_falloff; + float overscanX; + float overscanY; + float bloom_dist; } global; #pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 @@ -67,11 +70,20 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 #define c_shape global.c_shape // curvature shape -#pragma parameter csize " Corner size" 0.0 0.0 0.25 0.01 +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#pragma parameter csize " Corner Size" 0.0 0.0 0.25 0.005 #define csize global.csize // corner size -#pragma parameter bsize " Border smoothness" 400.0 100.0 700.0 10.0 -#define bsize global.bsize // border smoothness +#pragma parameter bsize1 " Border Size" 0.01 0.0 3.0 0.01 +#define bsize1 global.bsize1 // border size + +#pragma parameter sborder " Border Intensity" 0.75 0.25 2.0 0.05 +#define sborder global.sborder // border intensity #pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 @@ -84,9 +96,15 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 #define glow params.glow // Glow Strength -#pragma parameter bloom " Bloom Strength" 0.0 0.0 2.0 0.05 +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 #define bloom params.bloom // bloom effect +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + #pragma parameter halation " Halation Strength" 0.0 0.0 2.0 0.025 #define halation params.halation // halation effect @@ -104,16 +122,16 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter gsl " Scanline Type" 0.0 -1.0 2.0 1.0 #define gsl params.gsl // Alternate scanlines -#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 0.0 20.0 0.5 +#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 -20.0 20.0 0.5 #define scanline1 params.scanline1 // scanline param, vertical sharpness -#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 3.0 40.0 1.0 +#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 3.0 70.0 1.0 #define scanline2 params.scanline2 // scanline param, vertical sharpness -#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 3.5 0.05 +#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 5.0 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 +#pragma parameter beam_max " Scanline Shape Bright Pixels" 1.00 0.4 3.5 0.025 #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 @@ -122,49 +140,13 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 +#pragma parameter scans " Scanline Saturation / Mask Falloff" 0.60 0.0 2.5 0.05 #define scans params.scans // scanline saturation -#pragma parameter bogus_masks "[ CRT MASK OPTIONS ]: " 0.0 0.0 1.0 1.0 +#pragma parameter scan_falloff " Scanline Falloff" 1.0 0.25 2.0 0.05 +#define scan_falloff global.scan_falloff // scanline falloff -#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 mcut " Mask 5-7 Low Strength" 1.10 0.0 2.0 0.05 -#define mcut params.mcut // Mask 5-7 dark color strength - -#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 4.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 slotmask " Slot Mask Strength Bright Pixels" 0.0 0.0 1.0 0.05 -#define slotmask params.slotmask - -#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 -#define slotmask1 global.slotmask1 - -#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 4.0 1.0 -#define slotms params.slotms // Slot Mask Size - -#pragma parameter mclip " Keep Mask effect with clipping" 0.50 0.0 1.0 0.05 -#define mclip params.mclip // Slot Mask Size - -#pragma parameter gamma_out "Gamma out" 2.4 1.0 5.0 0.05 -#define gamma_out params.gamma_out // output gamma #define COMPAT_TEXTURE(c,d) texture(c,d) #define TEX0 vTexCoord @@ -180,13 +162,13 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + 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 Source; +layout(set = 0, binding = 2) uniform sampler2D Pass1; layout(set = 0, binding = 3) uniform sampler2D LinearizePass; layout(set = 0, binding = 4) uniform sampler2D BloomPass; layout(set = 0, binding = 5) uniform sampler2D PrePass; @@ -222,155 +204,6 @@ float sw2(float x, float color, float scanline) 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; - else - { - 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; - } -} - - -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) { @@ -379,12 +212,6 @@ vec3 gc(vec3 c) return c * mg/(mc + eps); } -vec3 plant (vec3 tar, float r) -{ - float t = max(max(tar.r,tar.g),tar.b) + 0.00001; - return tar * r / t; -} - vec2 Overscan(vec2 pos, float dx, float dy){ pos=pos*2.0-1.0; pos*=vec2(dx,dy); @@ -398,34 +225,13 @@ vec2 Warp(vec2 pos) return pos*0.5 + 0.5; } -float humbar(float pos) -{ - if (global.barintensity == 0.0) return 1.0; else - { - pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); - pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); - pos = (global.barintensity < 0.0) ? pos : (1.0-pos); - return (1.0-global.barintensity) + global.barintensity*pos; - } -} - -// Borrowed from cgwg's crt-geom, under GPL - -float corner(vec2 coord) -{ - coord = min(coord, vec2(1.0)-coord) * vec2(1.0, OutputSize.y/OutputSize.x); - vec2 cdist = vec2(max(csize/3.0, 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); -} void main() { vec4 SourceSize = vec4(global.SourceSize.x, global.OriginalSize.y, global.SourceSize.z, global.OriginalSize.w); 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 interb = (intera < 0.5); float SourceY = SourceSize.y; float sy = 1.0; @@ -438,7 +244,7 @@ void main() vec2 texcoord = TEX0.xy; - if (IOS > 0.0){ + if (IOS > 0.0 && !interb){ vec2 ofactor = OutputSize.xy/SourceSize.xy; vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); vec2 diff = ofactor/intfactor; @@ -446,10 +252,10 @@ void main() texcoord = Overscan(texcoord, scan, scan); if (IOS == 1.0 || IOS == 3.0) texcoord = vec2(TEX0.x, texcoord.y); } + + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); vec2 pos = Warp(texcoord); - vec2 cpos = (IOS > 2.5) ? TEX0 : texcoord; - float corner0 = corner(Warp(cpos)); float coffset = 0.5; @@ -469,17 +275,17 @@ void main() if (interb) pC4.y = pos.y; - vec3 color1 = COMPAT_TEXTURE(Source, pC4 ).rgb; - vec3 scolor1 = COMPAT_TEXTURE(Source, pC4 ).aaa; + vec3 color1 = COMPAT_TEXTURE(Pass1, pC4 ).rgb; + vec3 scolor1 = COMPAT_TEXTURE(Pass1, pC4 ).aaa; pC4+=dy; - vec3 color2 = COMPAT_TEXTURE(Source, pC4 ).rgb; - vec3 scolor2 = COMPAT_TEXTURE(Source, pC4 ).aaa; + vec3 color2 = COMPAT_TEXTURE(Pass1, pC4 ).rgb; + vec3 scolor2 = COMPAT_TEXTURE(Pass1, pC4 ).aaa; // calculating scanlines - vec3 ctmp; vec3 mcolor; float w3; vec3 color; + vec3 ctmp = color1; float w3 = 1.0; vec3 color = color1; vec3 one = vec3(1.0); if (!interb) @@ -495,12 +301,11 @@ if (!interb) ctmp = color00/(wt1+wt2); vec3 sctmp = max(scolor0/(wt1+wt2), ctmp); - mcolor = sctmp; 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); + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = pow(max(max(cref1.r,cref1.g),cref1.b), scan_falloff); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = pow(max(max(cref2.r,cref2.g),cref2.b), scan_falloff); float f1 = f; float f2 = 1.0-f; @@ -516,11 +321,16 @@ if (!interb) vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); w3 = wf1+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); + float mc1 = max(max(color1.r,color1.g),color1.b) + eps; + float mc2 = max(max(color2.r,color2.g),color2.b) + eps; + + cref1 = color1 / mc1; cref1=cref1*cref1; cref1*=cref1; + cref2 = color2 / mc2; cref2=cref2*cref2; cref2*=cref2; - w1 = mix(w1*mix(one, cref1*cref1*cref1, scans), w1, wf1); - w2 = mix(w2*mix(one, cref2*cref2*cref2, scans), w2, wf2); + w1 = max( mix(w1*mix(one, cref1, scans), w1, wf1*min((1.0+0.15*scans), 1.2)), 0.0); w1 = min(w1*color1, mc1)/(color1 + eps); + w2 = max( mix(w2*mix(one, cref2, scans), w2, wf2*min((1.0+0.15*scans), 1.2)), 0.0); w2 = min(w2*color2, mc2)/(color2 + eps); + + // Scanline Deconvergence vec3 cd1 = one; vec3 cd2 = one; float vm = sqrt(abs(vertmask)); @@ -549,53 +359,9 @@ if (!interb) if (interb) { color = gc(color1); - mcolor = clamp(mix(color1, scolor1, 1.25), 0.0, 1.0); } - float mx = max(max(mcolor.r,mcolor.g),mcolor.b); - mx = pow(mx, 1.20/gamma_in); + float colmx = pow(max(max(ctmp.r,ctmp.g),ctmp.b), 1.40/gamma_in); - // Apply Mask - - vec3 orig1 = color; - vec3 cmask = one; - - float smask = SlotMask(gl_FragCoord.xy * 1.000001, mx); - cmask*= Mask(gl_FragCoord.xy * 1.000001, mx); - - color = color*cmask; - color = min(color,1.0); - color = color*smask; - - 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(BloomPass, pos ).rgb; - vec3 Bloom = Glow; - float maxb = COMPAT_TEXTURE(BloomPass, pos ).a; - float vig = COMPAT_TEXTURE(PrePass, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; - - 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.60)); - - if (halation > 0.025) { - Bloom = mix(0.5*(Bloom + Bloom*Bloom), Bloom*Bloom, colmx); - color = color + (1.1-0.25*colmx)*(0.75+maxb)*Bloom*(0.75 + 0.70*pow(colmx,0.33333))*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.35 + 0.4*maxb)*halation; } - - Glow = mix(Glow, 0.25*color, 0.7*colmx); - if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } - - color = min(color, 1.0); - - color = pow(color, vec3(1.0/gamma_out)); - - FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir)), corner0); -} \ No newline at end of file + FragColor = vec4(color, colmx); +} diff --git a/crt/shaders/guest/fast/crt-guest-advanced-pass2f.slang b/crt/shaders/guest/fast/crt-guest-advanced-pass2f.slang index 8b7ab4c..354720f 100644 --- a/crt/shaders/guest/fast/crt-guest-advanced-pass2f.slang +++ b/crt/shaders/guest/fast/crt-guest-advanced-pass2f.slang @@ -3,7 +3,7 @@ /* CRT - Guest - Advanced - Fastest - Pass2 - Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com Incorporates many good ideas and suggestions from Dr. Venom. I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. @@ -44,6 +44,10 @@ layout(std140, set = 0, binding = 0) uniform UBO float bsize; float c_shape; float slotmask1; + float scan_falloff; + float bloom_dist; + float mshift; + float mask_layout; } global; #pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 @@ -60,7 +64,7 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 #define c_shape global.c_shape // curvature shape -#pragma parameter csize " Corner size" 0.0 0.0 0.25 0.01 +#pragma parameter csize " Corner Size" 0.0 0.0 0.25 0.01 #define csize global.csize // corner size #pragma parameter bsize " Border smoothness" 400.0 100.0 700.0 10.0 @@ -74,6 +78,9 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter bloom " Bloom Strength" 0.0 0.0 2.0 0.05 #define bloom params.bloom // bloom effect +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + #pragma parameter halation " Halation Strength" 0.0 0.0 1.0 0.025 #define halation params.halation // halation effect @@ -91,16 +98,16 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter gsl " Scanline Type" 0.0 -1.0 2.0 1.0 #define gsl params.gsl // Alternate scanlines -#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 0.0 20.0 0.5 +#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 -20.0 20.0 0.5 #define scanline1 params.scanline1 // scanline param, vertical sharpness -#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 3.0 40.0 1.0 +#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 3.0 70.0 1.0 #define scanline2 params.scanline2 // scanline param, vertical sharpness -#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 3.5 0.05 +#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.30 0.25 5.0 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 +#pragma parameter beam_max " Scanline Shape Bright Pixels" 1.00 0.4 3.5 0.025 #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 @@ -109,21 +116,24 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 +#pragma parameter scans " Scanline Saturation / Mask Falloff" 0.60 0.0 3.5 0.05 #define scans params.scans // scanline saturation +#pragma parameter scan_falloff " Scanline Falloff" 1.0 0.25 2.0 0.05 +#define scan_falloff global.scan_falloff // scanline falloff + #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 +#pragma parameter shadowMask " CRT Mask: 0:CGWG, 1-4:Lottes, 5-12:'Trinitron'" 0.0 -1.0 12.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 maskstr " Mask Strength (0, 5-12)" 0.3 -0.5 1.0 0.025 +#define maskstr params.maskstr // Mask Strength -#pragma parameter mcut " Mask 5-7 Low Strength" 1.10 0.0 2.0 0.05 -#define mcut params.mcut // Mask 5-7 dark color strength +#pragma parameter mcut " Mask 5-12 Low Strength" 1.10 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-12 dark color strength -#pragma parameter masksize " CRT Mask Size (2.0 is nice in 4k)" 1.0 1.0 4.0 1.0 +#pragma parameter masksize " CRT Mask Size" 1.0 1.0 4.0 1.0 #define masksize params.masksize // Mask Size #pragma parameter maskDark " Lottes maskDark" 0.5 0.0 2.0 0.05 @@ -132,16 +142,22 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter maskLight " Lottes maskLight" 1.5 0.0 2.0 0.05 #define maskLight params.maskLight // Light "Phosphor" +#pragma parameter mshift " Mask Shift/Stagger" 0.0 -8.0 8.0 1.0 +#define mshift global.mshift // mask 'line' shift/stagger + +#pragma parameter mask_layout " Mask Layout: RGB or BGR (check LCD panel) " 0.0 0.0 1.0 1.0 +#define mask_layout global.mask_layout // mask layout: RGB or BGR + #pragma parameter slotmask " Slot Mask Strength Bright Pixels" 0.0 0.0 1.0 0.05 #define slotmask params.slotmask #pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 #define slotmask1 global.slotmask1 -#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 6.0 0.5 +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 8.0 1.0 #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 +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1..." 1.0 1.0 4.0 1.0 #define double_slot params.double_slot // Slot Mask Height #pragma parameter slotms " Slot Mask Size" 1.0 1.0 4.0 1.0 @@ -166,6 +182,7 @@ layout(std140, set = 0, binding = 0) uniform UBO #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 @@ -227,10 +244,15 @@ float sw2(float x, float color, float scanline) vec3 Mask(vec2 pos, float mx) { - pos = floor(pos/masksize); + vec2 pos0 = pos; + pos.y = floor(pos.y/masksize); + float next_line = float(fract(pos.y*0.5) > 0.25); + pos0.x = pos0.x + next_line * mshift; + pos = floor(pos0/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 dark_compensate = mix(max( clamp( mix (mcut, maskstr, mx),0.0, 1.0) - 0.4, 0.0) + 1.0, 1.0, mx); float mc = 1.0 - max(maskstr, 0.0); // No mask @@ -243,7 +265,7 @@ vec3 Mask(vec2 pos, float mx) 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; } + if (pos.x < 0.49) { mask.r = 1.0; mask.g = mc; mask.b = 1.0; } else { mask.r = mc; mask.g = 1.0; mask.b = mc; } } @@ -253,15 +275,15 @@ vec3 Mask(vec2 pos, float mx) float line = maskLight; float odd = 0.0; - if (fract(pos.x/6.0) < 0.5) + if (fract(pos.x/6.0) < 0.49) odd = 1.0; - if (fract((pos.y + odd)/2.0) < 0.5) + if (fract((pos.y + odd)/2.0) < 0.49) 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; + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) mask.g = maskLight; else mask.b = maskLight; mask*=line; @@ -272,8 +294,8 @@ vec3 Mask(vec2 pos, float mx) { pos.x = fract(pos.x/3.0); - if (pos.x < 0.333) mask.r = maskLight; - else if (pos.x < 0.666) mask.g = maskLight; + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) mask.g = maskLight; else mask.b = maskLight; } @@ -283,8 +305,8 @@ vec3 Mask(vec2 pos, float mx) 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; + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) mask.g = maskLight; else mask.b = maskLight; } @@ -295,8 +317,8 @@ vec3 Mask(vec2 pos, float mx) 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; + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) mask.g = maskLight; else mask.b = maskLight; } @@ -305,7 +327,7 @@ vec3 Mask(vec2 pos, float mx) { mask = vec3(0.0); pos.x = fract(pos.x/2.0); - if (pos.x < 0.5) + if (pos.x < 0.49) { mask.r = 1.0; mask.b = 1.0; } @@ -318,8 +340,8 @@ vec3 Mask(vec2 pos, float mx) { 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; + if (pos.x < 0.3) mask.r = 1.0; + else if (pos.x < 0.6) 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; } @@ -327,14 +349,51 @@ vec3 Mask(vec2 pos, float mx) // 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); + mask = vec3(0.0); pos.x = fract(pos.x/2.0); - if (pos.x < 0.5) mask = vec3(1.0); + if (pos.x < 0.49) + { mask = 0.0.xxx; + } + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; } + // BW Trinitron mask 8 + else if (shadowMask == 8.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask = 1.0.xxx; + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Magenta - Green - Black mask + else if (shadowMask == 9.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask.rb = 1.0.xx; + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RGBX + else if (shadowMask == 10.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask = 0.0.xxx; + else if (pos.x < 0.4) mask.r = 1.0; + else if (pos.x < 0.7) 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; + } + // 4k mask - else + else if (shadowMask == 11.0) { mask = vec3(mc); pos.x = fract(pos.x * 0.25); @@ -343,13 +402,34 @@ vec3 Mask(vec2 pos, float mx) else if (pos.x < 0.7) mask.gb = 1.0.xx; else mask.b = 1.0; } + else if (shadowMask == 12.0) + { + 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.rb = 1.0.xx; + else if (pos.x < 0.7) mask.gb = 1.0.xx; + else mask.g = 1.0; + } + + // RRGGBBX mask + else + { + mask = vec3(0.0); + pos.x = floor(mod(pos.x,7.0)); + if (pos.x < 1.0) mask = 0.0.xxx; + else if (pos.x < 3.0) mask.r = 1.0; + else if (pos.x < 5.0) 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; + } return mask; -} +} float SlotMask(vec2 pos, float m) { - if (slotmask == 0.0) return 1.0; + if (slotmask == 0.0 && slotmask1 == 0.0) return 1.0; else { pos = floor(pos/slotms); @@ -431,7 +511,7 @@ void main() vec2 texcoord = TEX0.xy; - if (IOS > 0.0){ + if (IOS > 0.0 && !interb){ vec2 ofactor = OutputSize.xy/SourceSize.xy; vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); vec2 diff = ofactor/intfactor; @@ -510,7 +590,7 @@ if ( not_same || frames || interb ) // calculating scanlines - vec3 ctmp; vec3 mcolor; float w3; vec3 color; + vec3 ctmp = color1; vec3 mcolor = scolor1; float w3 = 1.0; vec3 color = color1; vec3 one = vec3(1.0); if (!interb) @@ -530,8 +610,8 @@ if (!interb) 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); + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = pow(max(max(cref1.r,cref1.g),cref1.b), scan_falloff); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = pow(max(max(cref2.r,cref2.g),cref2.b), scan_falloff); float f1 = f; float f2 = 1.0-f; @@ -547,11 +627,16 @@ if (!interb) vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); w3 = wf1+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); + float mc1 = max(max(color1.r,color1.g),color1.b) + eps; + float mc2 = max(max(color2.r,color2.g),color2.b) + eps; + + cref1 = color1 / mc1; cref1=cref1*cref1; cref1*=cref1; + cref2 = color2 / mc2; cref2=cref2*cref2; cref2*=cref2; - w1 = mix(w1*mix(one, cref1*cref1*cref1, scans), w1, wf1); - w2 = mix(w2*mix(one, cref2*cref2*cref2, scans), w2, wf2); + w1 = max( mix(w1*mix(one, cref1, scans), w1, wf1*min((1.0+0.15*scans), 1.2)), 0.0); w1 = min(w1*color1, mc1)/(color1 + eps); + w2 = max( mix(w2*mix(one, cref2, scans), w2, wf2*min((1.0+0.15*scans), 1.2)), 0.0); w2 = min(w2*color2, mc2)/(color2 + eps); + + // Scanline Deconvergence vec3 cd1 = one; vec3 cd2 = one; float vm = sqrt(abs(vertmask)); @@ -579,8 +664,8 @@ if (!interb) if (interb) { - color = gc(0.5*(color1+color2)); - mcolor = clamp(mix(color1, scolor1, 1.25), 0.0, 1.0); + color = gc(color1); + mcolor = scolor1; } float mx = max(max(mcolor.r,mcolor.g),mcolor.b); @@ -593,6 +678,8 @@ if (!interb) float smask = SlotMask(gl_FragCoord.xy * 1.000001, mx); cmask*= Mask(gl_FragCoord.xy * 1.000001, mx); + + if (mask_layout > 0.5) cmask = cmask.rbg; color = color*cmask; color = min(color,1.0); @@ -612,13 +699,17 @@ if (!interb) vec3 Bloom = Glow; vec3 Bloom1 = min(Bloom*(orig1+color), max(0.5*(colmx + orig1 - color),0.0)); - color = color + bloom*Bloom1; + Bloom1 = 0.5*(Bloom1 + mix(Bloom1, mix(colmx*orig1, Bloom1, 0.5), 1.0-color)); + + Bloom1 = Bloom1 * mix(1.0, 2.0-colmx, bloom_dist); + + color = color + bloom * Bloom1; color = min(color, mix(one, cmask, mclip)); if (!interb) color = declip(color, pow(w3,0.60)); if (halation > 0.025) { - Bloom = mix(0.5*(Bloom + Bloom*Bloom), Bloom*Bloom, colmx); + Bloom = mix(0.5*(Bloom + Bloom*Bloom), 0.75*Bloom*Bloom, colmx); color = color + 0.75*(0.75+maxb)*Bloom*(0.4+sqrt(colmx))*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.35 + 0.4*maxb)*halation; } Glow = mix(Glow, 0.25*color, 0.7*colmx); @@ -628,6 +719,7 @@ if (!interb) color = min(color, 1.0); result = vec4(color*corner0, corner0); + } FragColor = result; } \ No newline at end of file diff --git a/crt/shaders/guest/fast/crt-guest-dr-venom-pass1.slang b/crt/shaders/guest/fast/crt-guest-dr-venom-pass1.slang deleted file mode 100644 index 050e77d..0000000 --- a/crt/shaders/guest/fast/crt-guest-dr-venom-pass1.slang +++ /dev/null @@ -1,126 +0,0 @@ -#version 450 - -/* - CRT - Guest - Dr. Venom - Pass1 - - Copyright (C) 2018-2019 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 -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float h_sharp; - float s_sharp; - float h_smart; -} params; - -#pragma parameter h_sharp "Horizontal sharpness" 5.00 1.5 20.0 0.25 -#define h_sharp params.h_sharp -#pragma parameter s_sharp "Substractive sharpness" 0.05 0.0 0.20 0.01 -#define s_sharp params.s_sharp -#pragma parameter h_smart "Smart Horizontal Smoothing" 0.0 0.0 1.0 0.1 -#define h_smart params.h_smart -#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 Source; -layout(set = 0, binding = 3) uniform sampler2D SmoothPass; - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define PassPrev2Texture SmoothPass - -void main() -{ - vec2 ps = SourceSize.zw; - vec2 OGL2Pos = vTexCoord * SourceSize.xy; - vec2 fp = fract(OGL2Pos); - vec2 dx = vec2(ps.x,0.0); - vec2 dy = vec2(0.0, ps.y); - vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps; - - // Reading the texels - vec2 x2 = 2.0*dx; - vec2 y2 = 2.0*dy; - - bool sharp = (s_sharp > 0.0); - - float hsharp_tl, hsharp_tr, hsharp_tc; float s_sharpl = s_sharp; float s_sharpr = s_sharp; float s_sharpc = s_sharp; - - if (h_smart == 0.0) - { - hsharp_tl = h_sharp; hsharp_tr = h_sharp; hsharp_tc = h_sharp; - } - else - { - // reading differences for smoothing - vec2 diffs = COMPAT_TEXTURE(PassPrev2Texture, pC4).xy; - - float ls = mix (4.25, 2.25, h_smart); - hsharp_tl = mix(h_sharp, ls, diffs.x); - hsharp_tr = mix(h_sharp, ls, diffs.y); - - s_sharpl = mix(s_sharp, 0.0, diffs.x); - s_sharpr = mix(s_sharp, 0.0, diffs.y); - - hsharp_tc = hsharp_tl; - if (fp.x == 0.5) { hsharp_tc = 0.5*(hsharp_tl + hsharp_tr); s_sharpc = 0.5*(s_sharpl + s_sharpr); } - if (fp.x > 0.5) { hsharp_tc = hsharp_tr; } - } - - float wl2 = 1.5 + fp.x; wl2*=wl2; float twl2 = exp2(-hsharp_tl*wl2); twl2 = max(twl2 - s_sharpl, -twl2); - float wl1 = 0.5 + fp.x; wl1*=wl1; float twl1 = exp2(-hsharp_tl*wl1); twl1 = max(twl1 - s_sharpl, -0.4*s_sharpl); - float wct = 0.5 - fp.x; wct*=wct; float twct = exp2(-hsharp_tc*wct); twct = max(twct - s_sharpc, s_sharpc); - float wr1 = 1.5 - fp.x; wr1*=wr1; float twr1 = exp2(-hsharp_tr*wr1); twr1 = max(twr1 - s_sharpr, -0.4*s_sharpr); - float wr2 = 2.5 - fp.x; wr2*=wr2; float twr2 = exp2(-hsharp_tr*wr2); twr2 = max(twr2 - s_sharpr, -twr2); - - float wtt = 1.0/(twl2+twl1+twct+twr1+twr2); - - vec3 l2 = COMPAT_TEXTURE(Source, pC4 -x2).xyz; - vec3 l1 = COMPAT_TEXTURE(Source, pC4 -dx).xyz; - vec3 ct = COMPAT_TEXTURE(Source, pC4 ).xyz; - vec3 r1 = COMPAT_TEXTURE(Source, pC4 +dx).xyz; - vec3 r2 = COMPAT_TEXTURE(Source, pC4 +x2).xyz; - - vec3 color = (l2*twl2 + l1*twl1 + ct*twct + r1*twr1 + r2*twr2)*wtt; - if (sharp) color = clamp(color, 0.8*min(min(l1,r1),ct), 1.2*max(max(l1,r1),ct)); - - FragColor = vec4(color, 1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/fast/crt-guest-dr-venom-pass2.slang b/crt/shaders/guest/fast/crt-guest-dr-venom-pass2.slang deleted file mode 100644 index 3a8b700..0000000 --- a/crt/shaders/guest/fast/crt-guest-dr-venom-pass2.slang +++ /dev/null @@ -1,328 +0,0 @@ -#version 450 - -/* - CRT - Guest - Dr. Venom - Pass2 - - Copyright (C) 2018-2019 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 brightboost, IOS, gsl, scanline1, scanline2, beam_min, beam_max, s_power, beam_size, shadowMask, - masksize, vertmask, slotmask, slotwidth, double_slot, mcut, maskDark, maskLight, CGWG, gamma_out; -} params; - -#pragma parameter brightboost "Bright boost" 1.30 0.50 2.00 0.01 -#define brightboost params.brightboost // adjust brightness -#pragma parameter IOS "Smart Y Integer Scaling" 0.0 0.0 1.0 1.0 -#define IOS params.IOS // smart integer scaling -#pragma parameter gsl "Scanline Type" 1.0 0.0 2.0 1.0 -#define gsl params.gsl // Alternate scanlines -#pragma parameter scanline1 "Scanline beam shape low" 8.0 1.0 15.0 1.0 -#define scanline1 params.scanline1 // scanline param, vertical sharpness -#pragma parameter scanline2 "Scanline beam shape high" 8.0 5.0 23.0 1.0 -#define scanline2 params.scanline2 // scanline param, vertical sharpness -#pragma parameter beam_min "Scanline dark" 1.25 0.5 2.0 0.05 -#define beam_min params.beam_min // dark area beam min - narrow -#pragma parameter beam_max "Scanline bright" 1.05 0.5 2.0 0.05 -#define beam_max params.beam_max // bright area beam max - wide -#pragma parameter s_power "Scanline intensity" 1.0 0.5 2.5 0.05 -#define s_power params.s_power // scanline intensity -#pragma parameter beam_size "Increased bright scanline beam" 0.65 0.0 1.0 0.05 -#define beam_size params.beam_size // increased max. beam size -#pragma parameter shadowMask "CRT Mask: 0:CGWG, 1-4:Lottes, 5-6:'Trinitron'" 5.0 -1.0 6.0 1.0 -#define shadowMask params.shadowMask // Mask Style -#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 vertmask "PVM Like Colors" 0.05 0.0 0.25 0.01 -#define vertmask params.vertmask // Vertical mask -#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 2.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 mcut "Mask 5&6 cutoff" 0.2 0.0 0.5 0.05 -#define mcut params.mcut // Mask 5&6 cutoff -#pragma parameter maskDark "Mask Dark" 0.5 0.0 2.0 0.05 -#define maskDark params.maskDark // Dark "Phosphor" -#pragma parameter maskLight "Mask Light" 1.5 0.0 2.0 0.05 -#define maskLight params.maskLight // Light "Phosphor" -#pragma parameter CGWG "CGWG Mask Str." 0.3 0.0 1.0 0.05 -#define CGWG params.CGWG // CGWG Mask Strength -#pragma parameter gamma_out "Gamma out" 2.4 1.0 3.5 0.05 -#define gamma_out params.gamma_out // output gamma - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 SourceSize; - vec4 OutputSize; - uint FrameCount; -} global; - -#define SourceSize global.SourceSize -#define OutputSize global.OutputSize -#define FrameCount global.FrameCount - -#define COMPAT_TEXTURE(c,d) texture(c,d) -#define gl_FragCoord (vTexCoord.xy * OutputSize.xy) -#define InputSize SourceSize - -#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 Source; - -#define eps 1e-10 - -float st(float x, float scanline) -{ - return exp2(-scanline*x*x); -} - -vec3 sw0(vec3 x, vec3 color, float scanline) -{ - vec3 tmp = mix(vec3(beam_min),vec3(beam_max), color); - vec3 ex = x*tmp; - return exp2(-scanline*ex*ex); -} - -vec3 sw1(vec3 x, vec3 color, float scanline) -{ - float mx = max(max(color.r, color.g),color.b); - x = mix (x, beam_min*x, max(x-0.4*mx,0.0)); - vec3 tmp = mix(vec3(1.2*beam_min),vec3(beam_max), color); - vec3 ex = x*tmp; - float br = clamp(0.8*beam_min - 1.0, 0.2, 0.45); - return exp2(-scanline*ex*ex)/(1.0-br+br*color); -} - -vec3 sw2(vec3 x, vec3 color, float scanline) -{ - vec3 tmp = mix(vec3(2.75*beam_min),vec3(beam_max), color); - tmp = mix(vec3(beam_max), tmp, pow(x, vec3(max(max(color.r, color.g),color.b)+0.3))); - vec3 ex = x*tmp; - return exp2(-scanline*ex*ex)/(0.6 + 0.4*color); -} - -// Shadow mask (1-4 from PD CRT Lottes shader). -vec3 Mask(vec2 pos, vec3 c) -{ - pos = floor(pos/masksize); - vec3 mask = vec3(maskDark, maskDark, maskDark); - - - // No mask - if (shadowMask == -1.0) - { - mask = vec3(1.0); - } - - // Phosphor. - else if (shadowMask == 0.0) - { - pos.x = fract(pos.x*0.5); - float mc = 1.0 - CGWG; - if (pos.x < 0.5) { mask.r = 1.1; mask.g = mc; mask.b = 1.1; } - else { mask.r = mc; mask.g = 1.1; 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; - } - - // Alternate mask 5 - else if (shadowMask == 5.0) - { - float mx = max(max(c.r,c.g),c.b); - vec3 maskTmp = vec3( min( 1.25*max(mx-mcut,0.0)/(1.0-mcut) ,maskDark + 0.2*(1.0-maskDark)*mx)); - float adj = 0.80*maskLight - 0.5*(0.80*maskLight - 1.0)*mx + 0.75*(1.0-mx); - mask = maskTmp; - pos.x = fract(pos.x/2.0); - if (pos.x < 0.5) - { mask.r = adj; - mask.b = adj; - } - else mask.g = adj; - } - - // Alternate mask 6 - else if (shadowMask == 6.0) - { - float mx = max(max(c.r,c.g),c.b); - vec3 maskTmp = vec3( min( 1.5*max(mx-mcut,0.0)/(1.0-mcut) ,maskDark + 0.225*(1.0-maskDark)*mx)); - float adj = 0.80*maskLight - 0.5*(0.80*maskLight - 1.0)*mx + 0.75*(1.0-mx); - mask = maskTmp; - pos.x = fract(pos.x/3.0); - if (pos.x < 0.333) mask.r = adj; - else if (pos.x < 0.666) mask.g = adj; - else mask.b = adj; - } - - return mask; -} - -float SlotMask(vec2 pos, vec3 c) -{ - if (slotmask == 0.0) return 1.0; - - float mx = pow(max(max(c.r,c.g),c.b),1.33); - 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 = mix(1.0-slotmask, 1.0-0.80*slotmask, mx); - float slot = 1.0 + 0.7*slotmask*(1.0-mx); - if (py == 0.0 && px < 0.5) slot = slot_dark; else - if (py == double_slot && px >= 0.5) slot = slot_dark; - - return slot; -} - -float Overscan2(float pos, float dy){ - pos=pos*2.0-1.0; - pos*=dy; - return pos*0.5+0.5; -} - -void main() -{ - vec2 texcoord = vTexCoord; - - if (IOS == 1.0){ - float ofactor = OutputSize.y/InputSize.y; - float intfactor = round(ofactor); - float diff = ofactor/intfactor; - texcoord.y = Overscan2(texcoord.y*(SourceSize.y/InputSize.y), diff)*(InputSize.y/SourceSize.y); - } - - vec2 ps = SourceSize.zw; - vec2 OGL2Pos = texcoord * SourceSize.xy - vec2(0.0,0.5); - vec2 fp = fract(OGL2Pos); - vec2 dx = vec2(ps.x,0.0); - vec2 dy = vec2(0.0, ps.y); - - vec2 pC4 = floor(OGL2Pos) * ps + 0.5*ps; - - vec3 color1 = COMPAT_TEXTURE(Source, pC4 ).xyz; - vec3 color2 = COMPAT_TEXTURE(Source, pC4 +dy).xyz; - - // calculating scanlines - - float f = fp.y; - float shape1 = mix(scanline1, scanline2, f); - float shape2 = mix(scanline1, scanline2, 1.0-f); - - float wt1 = st(f, shape1); - float wt2 = st(1.0-f, shape2); - vec3 color0 = color1*wt1 + color2*wt2; - vec3 ctmp = color0/(wt1+wt2); - vec3 tmp = pow(ctmp, vec3(1.0/gamma_out)); - - vec3 w1,w2 = vec3(0.0); - - vec3 cref1 = mix(ctmp, color1, beam_size); - vec3 cref2 = mix(ctmp, color2, beam_size); - - vec3 shift = vec3(-vertmask, vertmask, -vertmask); - - vec3 f1 = clamp(vec3(f) + shift*0.5*(1.0+f), 0.0, 1.0); - vec3 f2 = clamp(vec3(1.0-f) - shift*0.5*(2.0-f), 0.0, 1.0); - - if (gsl == 0.0) { w1 = sw0(f1,cref1,shape1); w2 = sw0(f2,cref2,shape2);} else - if (gsl == 1.0) { w1 = sw1(f1,cref1,shape1); w2 = sw1(f2,cref2,shape2);} else - if (gsl == 2.0) { w1 = sw2(f1,cref1,shape1); w2 = sw2(f2,cref2,shape2);} - - vec3 color = color1*pow(w1, vec3(s_power)) + color2*pow(w2, vec3(s_power)); - - color*=brightboost; - color = min(color, 1.0); - - // Apply Mask - - color *= Mask(gl_FragCoord.xy * 1.000001,tmp); - - color = min(color,1.0); - - color *= SlotMask(gl_FragCoord.xy * 1.000001,tmp); - - color = pow(color, vec3(1.0/gamma_out)); - FragColor = vec4(color, 1.0); -} \ No newline at end of file diff --git a/crt/shaders/guest/fast/deconvergence-f.slang b/crt/shaders/guest/fast/deconvergence-f.slang index dac9614..66fe06a 100644 --- a/crt/shaders/guest/fast/deconvergence-f.slang +++ b/crt/shaders/guest/fast/deconvergence-f.slang @@ -1,9 +1,12 @@ #version 450 /* - CRT - Guest - Advanced - Deconvergence pass (NTSC) + noise + CRT - Guest - Advanced - Copyright (C) 2021 guest(r) - guest.r@gmail.com + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,11 +26,34 @@ layout(push_constant) uniform Push { + float IOS, brightboost, brightboost1, csize, bsize1, warpX, warpY, glow, shadowMask, masksize, + slotmask, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, addnoised, noiseresd, barintensity, mshift, mask_layout, mask_bloom; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; vec4 OutputSize; uint FrameCount; + float bloom; + float halation; + float slotms; + float mclip; + float mask_gamma; + float gamma_out; + float overscanX; + float overscanY; + float intres; + float prescalex; + float c_shape; + float barspeed; + float bardir; + float sborder; + float bloom_dist; + float deconr; float decons; - float addnoised; - float noiseresd; float deconrr; float deconrg; float deconrb; @@ -35,41 +61,158 @@ layout(push_constant) uniform Push float deconrgy; float deconrby; float dctypex; - float dctypey; -} params; + float dctypey; + float post_br; + float noisetype; +} global; + + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + +#pragma parameter halation " Halation Strength" 0.0 0.0 2.0 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.25 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#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 csize " Corner Size" 0.0 0.0 0.25 0.005 +#define csize params.csize // corner size + +#pragma parameter bsize1 " Border Size" 0.01 0.0 3.0 0.01 +#define bsize1 params.bsize1 // border Size + +#pragma parameter sborder " Border Intensity" 0.75 0.25 2.0 0.05 +#define sborder global.sborder // border intensity + +#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 + +#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 + +#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 +#define c_shape global.c_shape // curvature shape + +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#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-12:'Trinitron'" 0.0 -1.0 12.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-12)" 0.3 -0.5 1.0 0.025 +#define maskstr params.maskstr // Mask Strength + +#pragma parameter mcut " Mask 5-12 Low Strength" 1.10 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-12 dark color strength + +#pragma parameter masksize " CRT Mask Size" 1.0 1.0 4.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 mshift " Mask Shift/Stagger" 0.0 -8.0 8.0 1.0 +#define mshift params.mshift // mask 'line' shift/stagger + +#pragma parameter mask_layout " Mask Layout: RGB or BGR (check LCD panel) " 0.0 0.0 1.0 1.0 +#define mask_layout params.mask_layout // mask layout: RGB or BGR + +#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 Bright Pixels" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask + +#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 +#define slotmask1 params.slotmask1 + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 8.0 1.0 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1..." 1.0 1.0 4.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 4.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.50 0.0 1.0 0.05 +#define mclip global.mclip // + +#pragma parameter gamma_out "Gamma out" 2.4 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + #pragma parameter bogus_deconvergence11 "[ HORIZONTAL/VERTICAL DECONVERGENCE ]: " 0.0 0.0 1.0 1.0 -#pragma parameter dctypex " Deconvergence type X : 0.0 - static, other - dynamic" 0.0 0.0 1.0 0.05 +#pragma parameter dctypex " Deconvergence type X : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 -#pragma parameter dctypey " Deconvergence type Y : 0.0 - static, other - dynamic" 0.0 0.0 1.0 0.05 +#pragma parameter dctypey " Deconvergence type Y : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 -#pragma parameter deconrr " Horizontal Deconvergence Red Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrr " Horizontal Deconvergence Red Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrg " Horizontal Deconvergence Green Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrg " Horizontal Deconvergence Green Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrb " Horizontal Deconvergence Blue Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrb " Horizontal Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrry " Vertical Deconvergence Red Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrry " Vertical Deconvergence Red Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrgy " Vertical Deconvergence Green Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrgy " Vertical Deconvergence Green Range" 0.0 -15.0 15.0 0.25 -#pragma parameter deconrby " Vertical Deconvergence Blue Range" 0.0 -12.0 12.0 0.25 +#pragma parameter deconrby " Vertical Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 -#pragma parameter decons " Deconvergence Strength (and Type)" 0.5 -4.0 4.0 0.10 -#define decons params.decons // Horizontal deconvergence colors strength +#pragma parameter decons " Deconvergence Strength" 1.0 0.0 3.0 0.10 #pragma parameter addnoised " Add Noise" 0.0 -1.0 1.0 0.02 -#define addnoised params.addnoised // add noise -#pragma parameter noiseresd " Noise Resolution" 2.0 0.0 10.0 1.0 -#define noiseresd params.noiseresd // add noise +#pragma parameter noiseresd " Noise Resolution" 2.0 1.0 10.0 1.0 + +#pragma parameter noisetype " Noise Type: Colored, Luma" 0.0 0.0 1.0 1.0 + +#pragma parameter post_br " Post Brightness" 1.0 0.25 5.0 0.01 -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; -} global; +#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; @@ -79,15 +222,254 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord; + 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 Source; +layout(set = 0, binding = 2) uniform sampler2D PrePass; +layout(set = 0, binding = 3) uniform sampler2D LinearizePass; +layout(set = 0, binding = 4) uniform sampler2D BloomPass; +layout(set = 0, binding = 5) uniform sampler2D Source; + +#define eps 1e-10 + +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + vec2 pos0 = pos; + pos.y = floor(pos.y/masksize); + float next_line = float(fract(pos.y*0.5) > 0.25); + pos0.x = (mshift > -0.25) ? (pos0.x + next_line * mshift) : (pos0.x + pos.y * mshift); + pos = floor(pos0/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.4, 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.49) { 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.49) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.49) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.49) + { 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.3) mask.r = 1.0; + else if (pos.x < 0.6) 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) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.49) + { mask = 0.0.xxx; + } + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 8 + else if (shadowMask == 8.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask = 1.0.xxx; + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Magenta - Green - Black mask + else if (shadowMask == 9.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask.rb = 1.0.xx; + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RGBX + else if (shadowMask == 10.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask = 0.0.xxx; + else if (pos.x < 0.4) mask.r = 1.0; + else if (pos.x < 0.7) 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; + } + + // 4k mask + else if (shadowMask == 11.0) + { + mask = vec3(0.0); + 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; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RRGGBBX mask + else + { + mask = vec3(0.0); + pos.x = floor(mod(pos.x,7.0)); + if (pos.x < 1.0) mask = 0.0.xxx; + else if (pos.x < 3.0) mask.r = 1.0; + else if (pos.x < 5.0) 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; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if ((slotmask + slotmask1) == 0.0) return 1.0; + else + { + 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; + } +} + +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); + 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; +} + +float humbar(float pos) +{ + if (params.barintensity == 0.0) return 1.0; else + { + pos = (params.barintensity >= 0.0) ? pos : (1.0-pos); + pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); + pos = (params.barintensity < 0.0) ? pos : (1.0-pos); + return (1.0-params.barintensity) + params.barintensity*pos; + } +} + + +float corner(vec2 pos) { + vec2 b = vec2(bsize1, bsize1) * vec2(1.0, OutputSize.x/OutputSize.y) * 0.05; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + float csize1 = mix(400.0, 7.0, pow(4.0*csize, 0.10)); + float crn = dot(pow(pos, csize1.xx), vec2(1.0, OutputSize.y/OutputSize.x)); + crn = (csize == 0.0) ? max(pos.x, pos.y) : pow(crn, 1.0/csize1); + pos = max(pos, crn); + vec2 res = (bsize1 == 0.0) ? 1.0.xx : mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, sborder.xx); + return sqrt(res.x*res.y); +} -#define COMPAT_TEXTURE(c,d) texture(c,d) vec3 plant (vec3 tar, float r) { @@ -95,13 +477,20 @@ vec3 plant (vec3 tar, float r) return tar * r / t; } +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; +} + // noise function: // Dedicated to the public domain. // If you want a real license, you may consider this MIT/BSD/CC0/WTFPL-licensed (take your pick). // Adapted from ChuckNorris - shadertoy: https://www.shadertoy.com/view/XtK3Dz vec3 noise(vec3 v){ - if (addnoised < 0.0) v.z = -addnoised; else v.z = v.z/6000.0; + if (params.addnoised < 0.0) v.z = -params.addnoised; else v.z = mod(v.z,6001.0)/1753.0; // ensure reasonable range v = fract(v) + fract(v*1e4) + fract(v*1e-4); // seed @@ -109,68 +498,181 @@ vec3 noise(vec3 v){ // more iterations => more random v = fract(v*dot(v, v)*123.456); v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); v = fract(v*dot(v, v)*123.456); return v; -} +} -void main() +void fetch_pixel (inout vec3 c, inout vec3 b, vec2 coord, vec2 bcoord) { - - vec3 color = COMPAT_TEXTURE(Source, vTexCoord).rgb; - vec3 result = color; - - if ((abs(params.deconrr) + abs(params.deconrg) + abs(params.deconrb) + abs(params.deconrry) + abs(params.deconrgy) + abs(params.deconrby)) > 0.20) - { - float stepx = params.OutputSize.z; - float stepy = params.OutputSize.w; + float stepx = OutputSize.z; + float stepy = OutputSize.w; + float ds = global.decons; + vec2 dx = vec2(stepx, 0.0); - vec2 dy = vec2(0.0, stepy); + vec2 dy = vec2(0.0, stepy); - float ds = decons; - - float posx = 2.0*vTexCoord.x - 1.0; - float posy = 2.0*vTexCoord.y - 1.0; + float posx = 2.0*coord.x - 1.0; + float posy = 2.0*coord.y - 1.0; - if (params.dctypex > 0.025) + if (global.dctypex > 0.025) { - posx = sign(posx)*pow(abs(posx), 1.05-params.dctypex); + posx = sign(posx)*pow(abs(posx), 1.05-global.dctypex); dx = posx * dx; } - if (params.dctypey > 0.025) + if (global.dctypey > 0.025) { - posy = sign(posy)*pow(abs(posy), 1.05-params.dctypey); + posy = sign(posy)*pow(abs(posy), 1.05-global.dctypey); dy = posy * dy; } - if (params.dctypex > 0.025 || params.dctypey > 0.025) ds *= sqrt(posx*posx*sign(params.dctypex) + posy*posy*sign(params.dctypey)); + // if (global.dctypex > 0.025 || global.dctypey > 0.025) ds *= sqrt(posx*posx*sign(global.dctypex) + posy*posy*sign(global.dctypey)); - vec2 rc = params.deconrr * dx + params.deconrry*dy; - vec2 gc = params.deconrg * dx + params.deconrgy*dy; - vec2 bc = params.deconrb * dx + params.deconrby*dy; - - float r = COMPAT_TEXTURE(Source, vTexCoord + rc ).r; - float g = COMPAT_TEXTURE(Source, vTexCoord + gc ).g; - float b = COMPAT_TEXTURE(Source, vTexCoord + bc ).b; + vec2 rc = global.deconrr * dx + global.deconrry*dy; + vec2 gc = global.deconrg * dx + global.deconrgy*dy; + vec2 bc = global.deconrb * dx + global.deconrby*dy; - result = vec3(r,g,b); + float r1 = COMPAT_TEXTURE(Source, coord + rc).r; + float g1 = COMPAT_TEXTURE(Source, coord + gc).g; + float b1 = COMPAT_TEXTURE(Source, coord + bc).b; + + vec3 d = vec3(r1, g1, b1); + c = clamp(mix(c, d, ds), 0.0, 1.0); - vec3 dcolor = max(max(COMPAT_TEXTURE(Source, vTexCoord + dx).rgb, COMPAT_TEXTURE(Source, vTexCoord - dx).rgb), color); - - float mc = max(max(dcolor.r, dcolor.g), dcolor.b); - if (decons < 0.0) mc = 0.9; - float dclamp = min(2.0-0.40*abs(ds),1.0); + r1 = COMPAT_TEXTURE(BloomPass, bcoord + rc).r; + g1 = COMPAT_TEXTURE(BloomPass, bcoord + gc).g; + b1 = COMPAT_TEXTURE(BloomPass, bcoord + bc).b; + + d = vec3(r1, g1, b1); + b = clamp(mix(b, d, ds), 0.0, 1.0); +} + +void main() +{ + vec4 SourceSize = global.OriginalSize; - result = clamp(mix(color, sqrt(mix(result*result, color*result, sqrt(mc))), abs(ds)), dclamp*min(result,color), min(1.0/dclamp*max(result, color),1.0)); + 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.5); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + + if (IOS > 0.0 && !interb){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = diff.y; + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = vec2(TEX0.x, texcoord.y); + } + + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); + + vec2 pos1 = TEX0.xy; + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + vec3 color0 = COMPAT_TEXTURE(Source,pos1).rgb; + float c0 = max(max(color0.r, color0.g),color0.b); + + // color and bloom fetching + vec3 color = COMPAT_TEXTURE(Source,pos1).rgb; + vec3 Bloom = COMPAT_TEXTURE(BloomPass, pos).rgb; + fetch_pixel(color, Bloom, pos1, pos); + + float cm = max(max(color.r,color.g),color.b); + float mx1 = COMPAT_TEXTURE(Source, pos1 ).a; + float colmx = max(mx1, cm); + float w3 = min((c0 + 0.0005) / (pow(colmx, gamma_in/1.4) + 0.0005), 1.0); + + vec2 dx = vec2(0.001, 0.0); + float mx0 = COMPAT_TEXTURE(Source, pos1 - dx).a; + float mx2 = COMPAT_TEXTURE(Source, pos1 + dx).a; + float mx = max(max(mx0,mx1),max(mx2,cm)); + + vec3 one = vec3(1.0); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + vec2 maskcoord = gl_FragCoord.xy * 1.000001; + + float smask = SlotMask(maskcoord, mx); + cmask*= Mask(maskcoord, mx); + + if (mask_layout > 0.5) cmask = cmask.rbg; + + vec3 cmask1 = cmask; + float smask1 = smask; + + if (mask_bloom > 0.025) + { + float maxb = max(max(Bloom.r,Bloom.g),Bloom.b); + maxb = pow(sqrt(maxb*mix(maxb, colmx, 0.75)),0.275); + vec3 mBloom = 0.5*(1.5*Bloom+0.5*maxb) * mix(1.0, 2.0-colmx, (bloom_dist + 0.5)); + float maskmx = 1.0; if (shadowMask > 0.5 || shadowMask < 4.5) maskmx = maskLight; else if (shadowMask > 6.5 && shadowMask < 10.5) maskmx = 1.0; else maskmx = max(max(cmask.r,cmask.g),cmask.b); + cmask = min(cmask + maxb*mBloom*mask_bloom, maskmx); + smask = min(smask + 0.9*maxb*max(max(mBloom.r,mBloom.g),mBloom.b)*mask_bloom, 1.0); + } + + 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); + cmask1 = min(cmask1*smask1, 1.0); + + 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(BloomPass, pos).rgb; + vec3 Ref = COMPAT_TEXTURE(LinearizePass, pos).rgb; + float maxb = COMPAT_TEXTURE(BloomPass, pos).a; + float vig = COMPAT_TEXTURE(PrePass, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; + + vec3 Bloom1 = Bloom; + + if (bloom < -0.01) Bloom1 = plant(Bloom, maxb); + + Bloom1 = min(Bloom1*(orig1+color), max(0.5*(colmx + orig1 - color),0.001*Bloom1)); + Bloom1 = 0.5*(Bloom1 + mix(Bloom1, mix(colmx*orig1, Bloom1, 0.5), 1.0-color)); + + Bloom1 = Bloom1 * mix(1.0, 2.0-colmx, bloom_dist); + + color = color + abs(bloom) * Bloom1; + + color = min(color, mix(one, cmask1, mclip)); + + if (!interb) color = declip(color, mix(1.0, w3, 0.6)); else w3 = 1.0; + + if (halation > 0.01) { + Bloom = mix(0.5*(Bloom + Bloom*Bloom), 0.75*Bloom*Bloom, colmx); + color = color + 2.0*max((2.0*mix(maxb*maxb, maxb, colmx)-0.5*max(max(Ref.r,Ref.g),Ref.b)),0.25)*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.6)*Bloom*halation; } + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } + + color = min(color, 1.0); + + color = pow(color, vec3(1.0/gamma_out)); + + float rc = 0.6*sqrt(max(max(color.r, color.g), color.b))+0.4; + + if (abs(params.addnoised) > 0.01) + { + vec3 noise0 = noise(vec3(floor(OutputSize.xy * vTexCoord / params.noiseresd), float(global.FrameCount))); + if (global.noisetype < 0.5) color = mix(color, noise0, 0.25*abs(params.addnoised) * rc); + else color = min(color * mix(1.0, 1.5*noise0.x, 0.5*abs(params.addnoised)),1.0); } - float rc = 0.6*sqrt(max(max(result.r, result.g), result.b))+0.4; - - if (abs(addnoised) > 0.01) result = mix(result, noise(vec3(floor(params.OutputSize.xy * vTexCoord / noiseresd), float(params.FrameCount))), 0.25*abs(addnoised) * rc); - - float corner = COMPAT_TEXTURE(Source, vTexCoord).a; - - FragColor = vec4(result*corner, 1.0); -} \ No newline at end of file + FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir))*global.post_br*corner(pos0), 1.0); +} diff --git a/crt/shaders/guest/fast/linearize.slang b/crt/shaders/guest/fast/linearize.slang index 2a21a03..c9bd9a8 100644 --- a/crt/shaders/guest/fast/linearize.slang +++ b/crt/shaders/guest/fast/linearize.slang @@ -72,7 +72,7 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader)" 1.0 1.0 4.0 1.0 // Joint parameter with main pass, values must match #define prescalex params.prescalex // prescale-x factor -#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.40 0.0 1.0 0.05 +#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.25 0.0 1.0 0.05 #define iscans params.iscans // interlace saturation #pragma stage vertex @@ -83,7 +83,7 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + vTexCoord = TexCoord * 1.0001; } #pragma stage fragment @@ -152,7 +152,7 @@ void main() if (inter <= params.OriginalSize.y/yres_div && interm > 0.5 && intres != 1.0 && intres != 0.5) { - intera = 0.5; + intera = 0.25; float line_no = clamp(floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)), 0.0, 1.0); float frame_no = clamp(floor(mod(float(params.FrameCount),2.0)), 0.0, 1.0); float ii = abs(line_no-frame_no); @@ -163,10 +163,9 @@ void main() r = clamp(max(m1*ii, (1.0-iscan)*min(m1,m2)), 0.0, 1.0); 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 == 4.0) c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); - if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b));} + if (interm == 4.0) { c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } + if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } } c = pow(c, vec3(gamma_in)); diff --git a/crt/shaders/guest/fast/linearizef.slang b/crt/shaders/guest/fast/linearizef.slang index bba8cce..9d0d3ac 100644 --- a/crt/shaders/guest/fast/linearizef.slang +++ b/crt/shaders/guest/fast/linearizef.slang @@ -68,7 +68,7 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + vTexCoord = TexCoord * 1.0001; } #pragma stage fragment @@ -107,7 +107,7 @@ void main() if (inter <= params.OriginalSize.y && interm > 0.5) { - intera = 0.5; + intera = 0.25; float line_no = clamp(floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)), 0.0, 1.0); float frame_no = clamp(floor(mod(float(params.FrameCount),2.0)), 0.0, 1.0); float ii = abs(line_no-frame_no); @@ -117,9 +117,8 @@ void main() r = clamp(max(m1*ii, (1.0-iscan)*min(m1,m2)), 0.0, 1.0); 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); } + if (interm == 5.0) { c = mix(c2, c1, 0.5); intera = 0.45; } } c = pow(c, vec3(gamma_in)); diff --git a/crt/shaders/guest/fast/pre-shaders.slang b/crt/shaders/guest/fast/pre-shaders.slang index e68dd01..7e15741 100644 --- a/crt/shaders/guest/fast/pre-shaders.slang +++ b/crt/shaders/guest/fast/pre-shaders.slang @@ -3,7 +3,7 @@ /* CRT Advanced color altering - Copyright (C) 2019-2021 guest(r) and Dr. Venom + Copyright (C) 2019-2022 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 @@ -37,7 +37,8 @@ layout(push_constant) uniform Push float vigstr; float vigdef; float sega_fix; - float pre_bb; + float pre_bb; + float contr; } params; #pragma parameter bogus_color "[ COLOR TWEAKS ]:" 0.0 0.0 1.0 1.0 @@ -49,7 +50,7 @@ layout(push_constant) uniform Push #define CP params.CP #define CS params.CS -#pragma parameter TNTC " LUT Colors: Trin. | invTrin. | Nec Mult. | NTSC" 0.0 0.0 4.0 1.0 +#pragma parameter TNTC " LUT Colors: Trin.1 | Trin.2 | Nec Mult. | NTSC" 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 @@ -65,17 +66,19 @@ layout(push_constant) uniform Push #pragma parameter pre_bb " Brightness Adjustment" 1.0 0.0 2.0 0.01 +#pragma parameter contr " Contrast Adjustment" 0.0 -1.0 1.0 0.05 + #define WP params.WP #define wp_saturation params.wp_saturation #pragma parameter sega_fix " Sega Brightness Fix" 0.0 0.0 1.0 1.0 -#pragma parameter BP " Raise Black Level" 0.0 0.0 25.0 1.0 +#pragma parameter BP " Raise Black Level" 0.0 -100.0 25.0 1.0 #define BP params.BP -#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.025 +#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.05 -#pragma parameter vigdef " Vignette Definition" 7.0 0.4 15.0 0.2 +#pragma parameter vigdef " Vignette Size" 1.0 0.5 3.0 0.10 layout(std140, set = 0, binding = 0) uniform UBO @@ -194,9 +197,9 @@ const mat3 D65_to_D55 = mat3 ( const mat3 D65_to_D93 = mat3 ( - 0.3683017655, 0.1899055978, 0.0172641453, - 0.3555467892, 0.7110935785, 0.1185155964, - 0.2475020592, 0.0990008237, 1.3035108450); + 0.3412754080, 0.1759701322, 0.0159972847, + 0.3646170520, 0.7292341040, 0.1215390173, + 0.2369894093, 0.0947957637, 1.2481442225); vec3 fix_lut(vec3 lutcolor, vec3 ref) @@ -208,18 +211,26 @@ vec3 fix_lut(vec3 lutcolor, vec3 ref) return mix(lutcolor, ref, LUTBR); } -vec2 ctransform (vec2 inputc) -{ - return vec2( inputc.x * sqrt(1.0 - 0.5*inputc.y*inputc.y), inputc.y * sqrt(1.0 - 0.5*inputc.x*inputc.x)); +float vignette(vec2 pos) { + vec2 b = vec2(params.vigdef, params.vigdef) * vec2(1.0, params.OriginalSize.x/params.OriginalSize.y) * 0.125; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + vec2 res = mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, 0.70.xx); + return max(mix(1.0, sqrt(res.x*res.y), params.vigstr), 0.0); } -float vignette (vec2 coords) +vec3 plant (vec3 tar, float r) { - vec2 ccoords = ctransform(2.0*(coords-0.5)); - ccoords = ccoords * ccoords; - float vstr = sqrt(ccoords.x+ccoords.y); - vstr = pow(vstr, params.vigdef); - return max(mix(1.0, 1.0-vstr, params.vigstr), 0.0); + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +float contrast(float x) +{ + float y = 2.0*x-1.0; + y = (sin(y*1.57079632679)+1.0)*0.5; + return mix(x, y, params.contr); } void main() @@ -227,9 +238,7 @@ void main() vec4 imgColor = COMPAT_TEXTURE(Source, vTexCoord.xy); float w = float ((imgColor.r + imgColor.g + imgColor.b) < 1.5/255.0); - float bp = w * BP/255.0; - - imgColor.rgb = imgColor.rgb * params.pre_bb; + float bp = w * BP/255.0; if (params.sega_fix > 0.5) imgColor.rgb = imgColor.rgb * (255.0 / 239.0); @@ -279,10 +288,6 @@ void main() 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)); @@ -319,11 +324,13 @@ void main() 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 scolor1 = plant(pow(color, vec3(wp_saturation)), max(max(color.r,color.g),color.b)); + float luma = dot(color, vec3(0.299, 0.587, 0.114)); vec3 scolor2 = mix(vec3(luma), color, wp_saturation); color = (wp_saturation > 1.0) ? scolor1 : scolor2; - + + color = plant(color, contrast(max(max(color.r,color.g),color.b))); + p = 2.2; color = pow(color, vec3(p)); @@ -343,7 +350,12 @@ void main() color = mix(color, comp, m); color = pow(max(color, 0.0), vec3(1.0/p)); - color = color + bp; + if (BP > -0.5) color = color + bp; else + { + color = max(color + BP/255.0, 0.0) / (1.0 + BP/255.0*step(- BP/255.0, max(max(color.r,color.g),color.b))); + } + + color = min(color * params.pre_bb, 1.0); FragColor = vec4(color, vignette(vTexCoord.xy)); } \ No newline at end of file diff --git a/crt/shaders/guest/fast/pre-shadersf.slang b/crt/shaders/guest/fast/pre-shadersf.slang index bc2ebe7..9f765c5 100644 --- a/crt/shaders/guest/fast/pre-shadersf.slang +++ b/crt/shaders/guest/fast/pre-shadersf.slang @@ -3,7 +3,7 @@ /* CRT Advanced color altering - Copyright (C) 2019-2021 guest(r) and Dr. Venom + Copyright (C) 2019-2022 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 @@ -36,12 +36,13 @@ layout(push_constant) uniform Push float vigstr; float vigdef; float sega_fix; - float pre_bb; + float pre_bb; + float contr; } params; #pragma parameter bogus_color "[ COLOR TWEAKS ]:" 0.0 0.0 1.0 1.0 -#pragma parameter TNTC " LUT Colors: Trin. | invTrin. | Nec Mult. | NTSC" 0.0 0.0 4.0 1.0 +#pragma parameter TNTC " LUT Colors: Trin.1 | Trin.2 | Nec Mult. | NTSC" 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 @@ -52,16 +53,20 @@ layout(push_constant) uniform Push #define LUTBR 1.0 // "Fix LUT Brightness" from 0.0 to 1.0 #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 + #pragma parameter pre_bb " Brightness Adjustment" 1.0 0.0 2.0 0.01 +#pragma parameter contr " Contrast Adjustment" 0.0 -1.0 1.0 0.05 + #pragma parameter sega_fix " Sega Brightness Fix" 0.0 0.0 1.0 1.0 -#pragma parameter BP " Raise Black Level" 0.0 0.0 25.0 1.0 +#pragma parameter BP " Raise Black Level" 0.0 -100.0 25.0 1.0 -#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.025 +#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.05 -#pragma parameter vigdef " Vignette Definition" 7.0 0.4 15.0 0.2 +#pragma parameter vigdef " Vignette Size" 1.0 0.5 3.0 0.10 #define WP params.WP @@ -120,9 +125,9 @@ const mat3 D65_to_D55 = mat3 ( const mat3 D65_to_D93 = mat3 ( - 0.3683017655, 0.1899055978, 0.0172641453, - 0.3555467892, 0.7110935785, 0.1185155964, - 0.2475020592, 0.0990008237, 1.3035108450); + 0.3412754080, 0.1759701322, 0.0159972847, + 0.3646170520, 0.7292341040, 0.1215390173, + 0.2369894093, 0.0947957637, 1.2481442225); vec3 fix_lut(vec3 lutcolor, vec3 ref) @@ -134,20 +139,27 @@ vec3 fix_lut(vec3 lutcolor, vec3 ref) return mix(lutcolor, ref, LUTBR); } -vec2 ctransform (vec2 inputc) -{ - return vec2( inputc.x * sqrt(1.0 - 0.5*inputc.y*inputc.y), inputc.y * sqrt(1.0 - 0.5*inputc.x*inputc.x)); +float vignette(vec2 pos) { + vec2 b = vec2(params.vigdef, params.vigdef) * vec2(1.0, params.OriginalSize.x/params.OriginalSize.y) * 0.125; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + vec2 res = mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, 0.70.xx); + return max(mix(1.0, sqrt(res.x*res.y), params.vigstr), 0.0); } -float vignette (vec2 coords) +vec3 plant (vec3 tar, float r) { - vec2 ccoords = ctransform(2.0*(coords-0.5)); - ccoords = ccoords * ccoords; - float vstr = sqrt(ccoords.x+ccoords.y); - vstr = pow(vstr, params.vigdef); - return max(mix(1.0, 1.0-vstr, params.vigstr), 0.0); + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; } +float contrast(float x) +{ + float y = 2.0*x-1.0; + y = (sin(y*1.57079632679)+1.0)*0.5; + return mix(x, y, params.contr); +} void main() { @@ -156,8 +168,6 @@ void main() float w = float ((imgColor.r + imgColor.g + imgColor.b) < 1.5/255.0); float bp = w * BP/255.0; - - imgColor.rgb = imgColor.rgb * params.pre_bb; if (params.sega_fix > 0.5) imgColor.rgb = imgColor.rgb * (255.0 / 239.0); @@ -214,11 +224,13 @@ void main() vec3 c = clamp(color, 0.0, 1.0); - vec3 scolor1 = normalize(pow(color + 0.000000001, vec3(wp_saturation)))*length(color); - float luma = dot(color, vec3(0.2126, 0.7152, 0.0722)); + vec3 scolor1 = plant(pow(color, vec3(wp_saturation)), max(max(color.r,color.g),color.b)); + float luma = dot(color, vec3(0.299, 0.587, 0.114)); vec3 scolor2 = mix(vec3(luma), color, wp_saturation); color = (wp_saturation > 1.0) ? scolor1 : scolor2; - + + color = plant(color, contrast(max(max(color.r,color.g),color.b))); + float p = 2.2; color = pow(color, vec3(p)); @@ -238,21 +250,24 @@ void main() color = mix(color, comp, m); color = pow(max(color, 0.0), vec3(1.0/p)); - color = color + bp; + if (BP > -0.5) color = color + bp; else + { + color = max(color + BP/255.0, 0.0) / (1.0 + BP/255.0*step(- BP/255.0, max(max(color.r,color.g),color.b))); + } + + color = min(color * params.pre_bb, 1.0); vec2 dx = vec2(params.SourceSize.z, 0.0); - vec2 x2 = 2.5 *dx; vec2 xx = dx + dx; - vec2 x3 = x2 + dx; vec2 pC4 = vTexCoord; - vec3 ol3 = COMPAT_TEXTURE(OriginalHistory1, pC4 -x2).rgb; + vec3 ol3 = COMPAT_TEXTURE(OriginalHistory1, pC4 -xx).rgb; vec3 ol2 = COMPAT_TEXTURE(OriginalHistory1, pC4 -dx).rgb; vec3 ol1 = COMPAT_TEXTURE(OriginalHistory1, pC4 ).rgb; vec3 or1 = COMPAT_TEXTURE(OriginalHistory1, pC4 +dx).rgb; vec3 or2 = COMPAT_TEXTURE(OriginalHistory1, pC4 +xx).rgb; - vec3 cl3 = COMPAT_TEXTURE(OriginalHistory0, pC4 -x2).rgb; + vec3 cl3 = COMPAT_TEXTURE(OriginalHistory0, pC4 -xx).rgb; vec3 cl2 = COMPAT_TEXTURE(OriginalHistory0, pC4 -dx).rgb; vec3 cl1 = COMPAT_TEXTURE(OriginalHistory0, pC4 ).rgb; vec3 cr1 = COMPAT_TEXTURE(OriginalHistory0, pC4 +dx).rgb; diff --git a/crt/shaders/guest/fast/smoothing.slang b/crt/shaders/guest/fast/smoothing.slang deleted file mode 100644 index ee1d9ab..0000000 --- a/crt/shaders/guest/fast/smoothing.slang +++ /dev/null @@ -1,84 +0,0 @@ -#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); -} diff --git a/crt/shaders/guest/fast/linearize-multipass.slang b/crt/shaders/guest/fast/stock.slang similarity index 68% rename from crt/shaders/guest/fast/linearize-multipass.slang rename to crt/shaders/guest/fast/stock.slang index 0701b8b..bf3cf6b 100644 --- a/crt/shaders/guest/fast/linearize-multipass.slang +++ b/crt/shaders/guest/fast/stock.slang @@ -6,12 +6,8 @@ layout(push_constant) uniform Push vec4 OriginalSize; vec4 OutputSize; uint FrameCount; - float GAMMA_INPUT; } params; -#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 0.1 5.0 0.05 -#define GAMMA_INPUT params.GAMMA_INPUT - layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; @@ -31,9 +27,9 @@ void main() #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Original; +layout(set = 0, binding = 2) uniform sampler2D Source; void main() { - FragColor = pow(texture(Original, vTexCoord), vec4(GAMMA_INPUT)); + FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0); } \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/afterglow0.slang b/crt/shaders/guest/hd/afterglow0.slang similarity index 72% rename from crt/shaders/guest/crt-gdv-new/afterglow0.slang rename to crt/shaders/guest/hd/afterglow0.slang index f5c3501..5b92c2d 100644 --- a/crt/shaders/guest/crt-gdv-new/afterglow0.slang +++ b/crt/shaders/guest/hd/afterglow0.slang @@ -26,10 +26,15 @@ layout(push_constant) uniform Push float PR, PG, PB; } params; +#pragma parameter bogus_resolution "[ INTERNAL RESOLUTION (Quick setup) ]:" 0.0 0.0 1.0 1.0 +#pragma parameter internal_res " Internal Resolution" 1.0 1.0 8.0 0.10 + #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 +#pragma parameter PR " Persistence Red" 0.32 0.0 0.50 0.01 +#pragma parameter PG " Persistence Green" 0.32 0.0 0.50 0.01 +#pragma parameter PB " Persistence Blue" 0.32 0.0 0.50 0.01 +#pragma parameter AS " Afterglow Strength" 0.20 0.0 0.60 0.01 +#pragma parameter sat " Afterglow saturation" 0.50 0.0 1.0 0.01 #define PR params.PR #define PG params.PG @@ -68,9 +73,9 @@ void main() 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; } + if ((color.r + color.g + color.b < 5.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); + vec3 result = mix( max(mix(color, accumulate, 0.49 + vec3(PR, PG, PB))- 2.0/255.0, 0.0), color, w); FragColor = vec4(result, w); } \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang b/crt/shaders/guest/hd/bloom_horizontal.slang similarity index 78% rename from crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang rename to crt/shaders/guest/hd/bloom_horizontal.slang index e447629..a962146 100644 --- a/crt/shaders/guest/crt-gdv-new/bloom_horizontal.slang +++ b/crt/shaders/guest/hd/bloom_horizontal.slang @@ -3,7 +3,7 @@ /* Gaussian blur - horizontal pass, dynamic range, resizable - Copyright (C) 2020 guest(r) - guest.r@gmail.com + 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 @@ -21,11 +21,9 @@ */ -#pragma format R16G16B16A16_SFLOAT - layout(push_constant) uniform Push { - vec4 LinearizePassSize; + vec4 SourceSize; vec4 OriginalSize; vec4 OutputSize; uint FrameCount; @@ -33,10 +31,12 @@ layout(push_constant) uniform Push float SIGMA_HB; } params; -#pragma parameter SIZEHB " H. Bloom/Halation Radius" 3.0 1.0 30.0 1.0 +#pragma parameter bogus_glow "[ GLOW/BLOOM PASS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter SIZEHB " H. Bloom/Halation/Glow Radius" 4.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 +#pragma parameter SIGMA_HB " Horizontal Bloom/Halation/Glow Sigma" 1.0 0.25 15.0 0.05 #define SIGMA_HB params.SIGMA_HB layout(std140, set = 0, binding = 0) uniform UBO @@ -75,18 +75,20 @@ void main() 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); + vec4 color = vec4(0.0); vec2 dx = vec2(SourceSize1.z, 0.0); float w; float wsum = 0.0; - vec3 pixel; + vec4 pixel; float n = -SIZEHB; do { - pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx).rgb; + pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx); w = gaussian(n+f); + pixel.a = max(max(pixel.r, pixel.g),pixel.b); + pixel.a*=pixel.a*pixel.a; color = color + w * pixel; wsum = wsum + w; n = n + 1.0; @@ -95,5 +97,5 @@ void main() color = color / wsum; - FragColor = vec4(color, 1.0); + FragColor = vec4(color.rgb, pow(color.a, 0.333333)); } \ No newline at end of file diff --git a/crt/shaders/guest/crt-gdv-new/bloom_vertical.slang b/crt/shaders/guest/hd/bloom_vertical.slang similarity index 81% rename from crt/shaders/guest/crt-gdv-new/bloom_vertical.slang rename to crt/shaders/guest/hd/bloom_vertical.slang index f18b7eb..4c45599 100644 --- a/crt/shaders/guest/crt-gdv-new/bloom_vertical.slang +++ b/crt/shaders/guest/hd/bloom_vertical.slang @@ -21,7 +21,6 @@ */ -#pragma format R16G16B16A16_SFLOAT layout(push_constant) uniform Push { @@ -34,10 +33,10 @@ layout(push_constant) uniform Push } params; -#pragma parameter SIZEVB " V. Bloom/Halation Radius" 3.0 1.0 30.0 1.0 +#pragma parameter SIZEVB " V. Bloom/Halation/Glow Radius" 4.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 +#pragma parameter SIGMA_VB " Vertical Bloom/Halation/Glow Sigma" 1.0 0.25 15.0 0.05 #define SIGMA_VB params.SIGMA_VB layout(std140, set = 0, binding = 0) uniform UBO @@ -72,24 +71,24 @@ float gaussian(float x) void main() { - vec4 SourceSize1 = params.SourceSize; - SourceSize1.yw = params.OriginalSize.yw; - + vec4 SourceSize1 = vec4(params.SourceSize.x, params.OriginalSize.y, params.SourceSize.z, params.OriginalSize.w); + 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); + vec4 color = vec4(0.0); vec2 dy = vec2(0.0, SourceSize1.w); float w; float wsum = 0.0; - vec3 pixel; + vec4 pixel; float n = -SIZEVB; do { - pixel = COMPAT_TEXTURE(Source, tex + n*dy).rgb; + pixel = COMPAT_TEXTURE(Source, tex + n*dy); w = gaussian(n+f); + pixel.a*=pixel.a*pixel.a; color = color + w * pixel; wsum = wsum + w; n = n + 1.0; @@ -98,5 +97,5 @@ void main() color = color / wsum; - FragColor = vec4(color, 1.0); + FragColor = vec4(color.rgb, pow(color.a, 0.175)); } \ No newline at end of file diff --git a/crt/shaders/guest/hd/crt-guest-advanced-hd-pass1.slang b/crt/shaders/guest/hd/crt-guest-advanced-hd-pass1.slang new file mode 100644 index 0000000..f7e11bc --- /dev/null +++ b/crt/shaders/guest/hd/crt-guest-advanced-hd-pass1.slang @@ -0,0 +1,193 @@ +#version 450 + +/* + CRT - Guest - Advanced - HD - Pass1 + + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. + + 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 SIGMA_HOR; + float HSHARPNESS; + float S_SHARP; + float HARNG; + float HSHARP; + float prescalex; + float prescaley; + float spike; + float SIGMA_VER; + float VSHARPNESS; + float S_SHARPV; + float VARNG; + float VSHARP; + float internal_res; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + + +#pragma parameter bogus_filtering "[ FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter internal_res " Internal Resolution" 1.0 1.0 8.0 0.10 +#define internal_res params.internal_res + +#pragma parameter HSHARPNESS " Horizontal Filter Range" 1.0 1.0 8.0 0.25 +#define HSHARPNESS params.HSHARPNESS + +#pragma parameter SIGMA_HOR " Horizontal Blur Sigma" 0.50 0.1 7.0 0.025 +#define SIGMA_HOR params.SIGMA_HOR + +#pragma parameter S_SHARP " Substractive Sharpness" 1.0 0.0 2.0 0.10 +#define S_SHARP params.S_SHARP + +#pragma parameter HSHARP " Sharpness Definition" 1.25 0.0 2.0 0.10 +#define HSHARP params.HSHARP + +#pragma parameter HARNG " Substractive Sharpness Ringing" 0.2 0.0 4.0 0.10 +#define HARNG params.HARNG + +#pragma parameter bogus_vfiltering "[ VERTICAL/INTERLACING FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter VSHARPNESS " Vertical Filter Range" 1.0 1.0 8.0 0.25 +#define VSHARPNESS global.VSHARPNESS + +#pragma parameter SIGMA_VER " Vertical Blur Sigma" 0.50 0.1 7.0 0.025 +#define SIGMA_VER global.SIGMA_VER + +#pragma parameter S_SHARPV " Vert. Substractive Sharpness" 1.0 0.0 2.0 0.10 +#define S_SHARPV global.S_SHARPV + +#pragma parameter VSHARP " Vert. Sharpness Definition" 1.25 0.0 2.0 0.10 +#define VSHARP global.VSHARP + +#pragma parameter VARNG " Substractive Sharpness Ringing" 0.2 0.0 4.0 0.10 +#define VARNG params.VARNG + +#pragma parameter spike " Scanline Spike Removal" 1.0 0.0 2.0 0.10 +#define spike params.spike + +#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader...)" 1.0 1.0 5.0 0.25 // Joint parameter with main pass, values must match +#define prescalex params.prescalex // prescale-x factor + +#pragma parameter prescaley " Prescale-Y Factor (for xBR...pre-shader...)" 1.0 1.0 5.0 0.25 // Joint parameter with Linearize Pass pass, values must match +#define prescaley params.prescaley // prescale-y factor + +#define COMPAT_TEXTURE(c,d) texture(c,d) +#define TEX0 vTexCoord + +#define OutputSize params.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.0001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D LinearizePass; + +float invsqrsigma = 1.0/(2.0*SIGMA_HOR*SIGMA_HOR*internal_res*internal_res); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +void main() +{ + vec4 SourceSize = params.OriginalSize * vec4(prescalex, prescaley, 1.0/prescalex, 1.0/prescaley); + + float f = fract(SourceSize.x * vTexCoord.x); + f = 0.5 - f; + vec2 tex = floor(SourceSize.xy * vTexCoord)*SourceSize.zw + 0.5*SourceSize.zw; + vec3 color = 0.0.xxx; + float scolor = 0.0; + vec2 dx = vec2(SourceSize.z, 0.0); + + float w = 0.0; + float swsum = 0.0; + float wsum = 0.0; + vec3 pixel; + + float hsharpness = HSHARPNESS * internal_res; + vec3 cmax = 0.0.xxx; + vec3 cmin = 1.0.xxx; + float sharp = gaussian(hsharpness) * S_SHARP; + float maxsharp = 0.20; + float FPR = hsharpness; + float fpx = 0.0; + float sp = 0.0; + float sw = 0.0; + + float ts = 0.025; + vec3 luma = vec3(0.2126, 0.7152, 0.0722); + + float LOOPSIZE = ceil(2.0*FPR); + float CLAMPSIZE = round(2.0*LOOPSIZE/3.0); + + float n = -LOOPSIZE; + + do + { + pixel = COMPAT_TEXTURE(LinearizePass, tex + n*dx).rgb; + sp = max(max(pixel.r,pixel.g),pixel.b); + + w = gaussian(n+f) - sharp; + fpx = abs(n+f-sign(n)*FPR)/FPR; + if (abs(n) <= CLAMPSIZE) { cmax = max(cmax, pixel); cmin = min(cmin, pixel); } + if (w < 0.0) w = clamp(w, mix(-maxsharp, 0.0, pow(fpx, HSHARP)), 0.0); + + color = color + w * pixel; + wsum = wsum + w; + + sw = max(w, 0.0) * (dot(pixel,luma) + ts); + scolor = scolor + sw * sp; + swsum = swsum + sw; + + n = n + 1.0; + + } while (n <= LOOPSIZE); + + color = color / wsum; + scolor = scolor / swsum; + + color = clamp(mix(clamp(color, cmin, cmax), color, HARNG), 0.0, 1.0); + + scolor = clamp(mix(max(max(color.r, color.g),color.b), scolor, spike), 0.0, 1.0); + + FragColor = vec4(color, scolor); +} \ No newline at end of file diff --git a/crt/shaders/guest/hd/crt-guest-advanced-hd-pass2.slang b/crt/shaders/guest/hd/crt-guest-advanced-hd-pass2.slang new file mode 100644 index 0000000..07abb62 --- /dev/null +++ b/crt/shaders/guest/hd/crt-guest-advanced-hd-pass2.slang @@ -0,0 +1,461 @@ +#version 450 + +/* + CRT - Guest - Advanced - HD - Pass2 + + Copyright (C) 2018-2021 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. + + 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 IOS, brightboost, brightboost1, gsl, scanline1, scanline2, beam_min, beam_max, beam_size, + glow, vertmask, inters, bloom, halation, scans, gamma_c; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float warpX; + float warpY; + float csize; + float bsize1; + float intres; + float c_shape; + float barspeed; + float barintensity; + float bardir; + float SIGMA_VER; + float VSHARPNESS; + float S_SHARPV; + float VARNG; + float VSHARP; + float prescaley; + float internal_res; + float scangamma; + float sborder; + float scan_falloff; + float overscanX; + float overscanY; + float bloom_dist; +} global; + +#pragma parameter bogus_vfiltering "[ VERTICAL/INTERLACING FILTERING OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter VSHARPNESS " Vertical Filter Range" 1.0 1.0 8.0 0.25 +#define VSHARPNESS global.VSHARPNESS + +#pragma parameter SIGMA_VER " Vertical Blur Sigma" 0.50 0.1 7.0 0.025 +#define SIGMA_VER global.SIGMA_VER + +#pragma parameter S_SHARPV " Vert. Substractive Sharpness" 1.0 0.0 2.0 0.10 +#define S_SHARPV global.S_SHARPV + +#pragma parameter VSHARP " Vert. Sharpness Definition" 1.25 0.0 2.0 0.10 +#define VSHARP global.VSHARP + +#pragma parameter VARNG " Substractive Sharpness Ringing" 0.2 0.0 4.0 0.10 +#define VARNG global.VARNG + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter intres " Internal Resolution Y: 224p/240p, 1.5...y-dowsample" 0.0 0.0 6.0 0.5 // Joint parameter with linearize pass, values must match + +#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 warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 +#define warpX global.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 +#define warpY global.warpY // Curvature Y + +#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 +#define c_shape global.c_shape // curvature shape + +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#pragma parameter csize " Corner Size" 0.0 0.0 0.25 0.005 +#define csize global.csize // corner size + +#pragma parameter bsize1 " Border Size" 0.01 0.0 3.0 0.01 +#define bsize1 global.bsize1 // border size + +#pragma parameter sborder " Border Intensity" 0.75 0.25 2.0 0.05 +#define sborder global.sborder // border intensity + +#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 + +#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 + +#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 +#define bloom params.bloom // bloom effect + +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + +#pragma parameter halation " Halation Strength" 0.0 0.0 2.0 0.025 +#define halation params.halation // halation effect + +#pragma parameter gamma_c " Gamma correct" 1.0 0.50 2.0 0.02 +#define gamma_c params.gamma_c // adjust brightness + +#pragma parameter brightboost " Bright Boost Dark Pixels" 1.40 0.25 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 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 -1.0 2.0 1.0 +#define gsl params.gsl // Alternate scanlines + +#pragma parameter scanline1 " Scanline Beam Shape Center" 6.0 -20.0 20.0 0.5 +#define scanline1 params.scanline1 // scanline param, vertical sharpness + +#pragma parameter scanline2 " Scanline Beam Shape Edges" 8.0 3.0 70.0 1.0 +#define scanline2 params.scanline2 // scanline param, vertical sharpness + +#pragma parameter beam_min " Scanline Shape Dark Pixels" 1.20 0.25 5.0 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 3.5 0.025 +#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 / Mask Falloff" 0.60 0.0 2.5 0.05 +#define scans params.scans // scanline saturation + +#pragma parameter scan_falloff " Scanline Falloff" 1.0 0.25 2.0 0.05 +#define scan_falloff global.scan_falloff // scanline falloff + +#pragma parameter scangamma " Scanline Gamma" 2.40 0.5 5.0 0.05 +#define scangamma global.scangamma + +#pragma parameter prescaley " Prescale-Y Factor (for xBR...pre-shader...)" 1.0 1.0 5.0 0.25 // Joint parameter with Linearize Pass pass, values must match +#define prescaley global.prescaley // prescale-y factor + +#pragma parameter internal_res " Internal Resolution" 1.0 1.0 8.0 0.10 +#define internal_res global.internal_res + +#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.0001; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Pass1; +layout(set = 0, binding = 3) uniform sampler2D LinearizePass; +layout(set = 0, binding = 4) uniform sampler2D BloomPass; +layout(set = 0, binding = 5) uniform sampler2D PrePass; + +#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; + ex = (gsl > -0.5) ? ex*ex : mix(ex*ex, ex*ex*ex, 0.4); + return exp2(-scanline*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-0.5*color)*beam_min, beam_max, color); + tmp = mix(beam_max, tmp, pow(x, color+0.3)); + float ex = x*tmp; + return exp2(-scanline*ex*ex); +} + + +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); +} + +vec2 Overscan(vec2 pos, float dx, float dy){ + pos=pos*2.0-1.0; + pos*=vec2(dx,dy); + return pos*0.5+0.5; +} + +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); + return pos*0.5 + 0.5; +} + + +float invsqrsigma = 1.0/(2.0*SIGMA_VER*SIGMA_VER*internal_res*internal_res); + +float gaussian(float x) +{ + return exp(-x*x*invsqrsigma); +} + +vec3 v_resample (vec2 tex0, vec4 Size) { + + float f = fract(Size.y * tex0.y); + f = 0.5 - f; + vec2 tex = tex0; + tex.y = floor(Size.y *tex.y)*Size.w + 0.5*Size.w; + vec3 color = 0.0.xxx; + vec2 dy = vec2(0.0, Size.w); + + float w = 0.0; + float wsum = 0.0; + vec3 pixel; + + vec3 cmax = 0.0.xxx; + vec3 cmin = 1.0.xxx; + float vsharpness = VSHARPNESS*internal_res; + float sharp = gaussian(vsharpness) * S_SHARPV; + float maxsharp = 0.20; + float FPR = vsharpness; + float fpx = 0.0; + + float LOOPSIZE = ceil(2.0*FPR); + float CLAMPSIZE = round(2.0*LOOPSIZE/3.0); + + float n = -LOOPSIZE; + + do + { + pixel = COMPAT_TEXTURE(Pass1, tex + n*dy).rgb; + + w = gaussian(n+f) - sharp; + fpx = abs(n+f-sign(n)*FPR)/FPR; + if (abs(n) <= CLAMPSIZE) { cmax = max(cmax, pixel); cmin = min(cmin, pixel); } + if (w < 0.0) w = clamp(w, mix(-maxsharp, 0.0, pow(fpx, VSHARP)), 0.0); + + color = color + w * pixel; + wsum = wsum + w; + + n = n + 1.0; + + } while (n <= LOOPSIZE); + + color = color / wsum; + + color = clamp(mix(clamp(color, cmin, cmax), color, VARNG), 0.0, 1.0); + + return color; +} + + +void main() +{ + vec4 SourceSize = vec4(global.SourceSize.x, global.OriginalSize.y, global.SourceSize.z, global.OriginalSize.w); + 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.5); + + float SourceY = SourceSize.y; + float sy = 1.0; + if (global.intres == 0.5) sy = SourceY/224.0; else + if (global.intres == 1.0) sy = SourceY/240.0; else + if (global.intres > 1.25) sy = global.intres; + SourceSize*=vec4(1.0, 1.0/sy, 1.0, sy); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + + if (IOS > 0.0 && !interb){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = diff.y; + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = vec2(TEX0.x, texcoord.y); + } + + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); + + vec2 pos = Warp(texcoord); + + float coffset = 0.5; + + vec2 ps = SourceSize.zw; + float OGL2Pos = pos.y * SourceSize.y - coffset; + float f = fract(OGL2Pos); + + vec2 dx = vec2(ps.x,0.0); + vec2 dy = vec2(0.0, ps.y); + + // Reading the texels + + vec2 pC4; + + pC4.y = floor(OGL2Pos) * ps.y + 0.5*ps.y; + pC4.x = pos.x; + + vec3 color1 = COMPAT_TEXTURE(Pass1, pC4 ).rgb; + vec3 scolor1 = COMPAT_TEXTURE(Pass1, pC4 ).aaa; + + color1 = pow(color1, vec3(scangamma/gamma_in)); + + if (interb) color1 = v_resample(pos, SourceSize * vec4(1.0, prescaley, 1.0, 1.0/prescaley)); + + pC4+=dy; + + vec3 color2 = COMPAT_TEXTURE(Pass1, pC4 ).rgb; + vec3 scolor2 = COMPAT_TEXTURE(Pass1, pC4 ).aaa; + + color2 = pow(color2, vec3(scangamma/gamma_in)); + + // calculating scanlines + + vec3 ctmp = color1; vec3 mcolor = scolor1; float w3 = 1.0; vec3 color = color1; + 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; + + ctmp = color00/(wt1+wt2); + vec3 sctmp = max(scolor0/(wt1+wt2), ctmp); + + float wf1, wf2; + + vec3 cref1 = mix(sctmp, scolor1, beam_size); float creff1 = pow(max(max(cref1.r,cref1.g),cref1.b), scan_falloff); + vec3 cref2 = mix(sctmp, scolor2, beam_size); float creff2 = pow(max(max(cref2.r,cref2.g),cref2.b), scan_falloff); + + float f1 = f; + float f2 = 1.0-f; + + if (gsl < 0.5) { 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 + { 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 saturation application + + vec3 w1 = vec3(wf1); vec3 w2 = vec3(wf2); + w3 = wf1+wf2; + + float mc1 = max(max(color1.r,color1.g),color1.b) + eps; + float mc2 = max(max(color2.r,color2.g),color2.b) + eps; + + cref1 = color1 / mc1; cref1=cref1*cref1; cref1*=cref1; + cref2 = color2 / mc2; cref2=cref2*cref2; cref2*=cref2; + + w1 = max( mix(w1*mix(one, cref1, scans), w1, wf1*min((1.0+0.15*scans), 1.2)), 0.0); w1 = min(w1*color1, mc1)/(color1 + eps); + w2 = max( mix(w2*mix(one, cref2, scans), w2, wf2*min((1.0+0.15*scans), 1.2)), 0.0); w2 = min(w2*color2, mc2)/(color2 + eps); + + // Scanline Deconvergence + + 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(max(1.0-w3*w3, 2.5*f1), 1.0); + float ds2 = min(max(1.0-w3*w3, 2.5*f2), 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); +} + + if (interb) + { + color = gc(color1); + } + + float colmx = pow(max(max(ctmp.r,ctmp.g),ctmp.b), 1.40/gamma_in); + + if (!interb) color = pow( color, vec3(gamma_in/scangamma) ); + + FragColor = vec4(color, colmx); +} diff --git a/crt/shaders/guest/hd/deconvergence-hd.slang b/crt/shaders/guest/hd/deconvergence-hd.slang new file mode 100644 index 0000000..d312df6 --- /dev/null +++ b/crt/shaders/guest/hd/deconvergence-hd.slang @@ -0,0 +1,682 @@ +#version 450 + +/* + CRT - Guest - Advanced + + Copyright (C) 2018-2022 guest(r) - guest.r@gmail.com + + Incorporates many good ideas and suggestions from Dr. Venom. + I would also like give thanks to many Libretro forums members for continuous feedback, suggestions and caring about the shader. + + 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 IOS, brightboost, brightboost1, csize, bsize1, warpX, warpY, glow, shadowMask, masksize, + slotmask, slotmask1, slotwidth, double_slot, mcut, maskDark, maskLight, maskstr, mshift, mask_layout, mask_bloom; +} params; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float bloom; + float halation; + float slotms; + float mclip; + float mask_gamma; + float gamma_out; + float overscanX; + float overscanY; + float intres; + float prescalex; + float c_shape; + float barspeed; + float barintensity; + float bardir; + float sborder; + float bloom_dist; + float deconr; + float decons; + float addnoised; + float noiseresd; + float noisetype; + float deconrr; + float deconrg; + float deconrb; + float deconrry; + float deconrgy; + float deconrby; + float dctypex; + float dctypey; + float post_br; +} global; + + +#pragma parameter bogus_brightness "[ BRIGHTNESS SETTINGS ]:" 0.0 0.0 1.0 1.0 + +#pragma parameter glow " Glow Strength" 0.08 -2.0 2.0 0.01 +#define glow params.glow // Glow Strength + +#pragma parameter bloom " Bloom Strength" 0.0 -2.0 2.0 0.05 +#define bloom global.bloom // bloom effect + +#pragma parameter mask_bloom " Mask Bloom" 0.0 0.0 2.0 0.05 +#define mask_bloom params.mask_bloom // bloom effect + +#pragma parameter bloom_dist " Bloom Distribution" 0.0 0.0 3.0 0.05 +#define bloom_dist global.bloom_dist // bloom effect distribution + +#pragma parameter halation " Halation Strength" 0.0 0.0 2.0 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.25 10.0 0.05 +#define brightboost params.brightboost // adjust brightness + +#pragma parameter brightboost1 " Bright Boost Bright Pixels" 1.10 0.25 3.00 0.025 +#define brightboost1 params.brightboost1 // adjust brightness + +#pragma parameter bogus_screen "[ SCREEN OPTIONS ]: " 0.0 0.0 1.0 1.0 + +#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 csize " Corner Size" 0.0 0.0 0.25 0.005 +#define csize params.csize // corner size + +#pragma parameter bsize1 " Border Size" 0.01 0.0 3.0 0.01 +#define bsize1 params.bsize1 // border Size + +#pragma parameter sborder " Border Intensity" 0.75 0.25 2.0 0.05 +#define sborder global.sborder // border intensity + +#pragma parameter barspeed " Hum Bar Speed" 50.0 5.0 200.0 1.0 + +#pragma parameter barintensity " Hum Bar Intensity" 0.0 -1.0 1.0 0.01 + +#pragma parameter bardir " Hum Bar Direction" 0.0 0.0 1.0 1.0 + +#pragma parameter warpX " CurvatureX (default 0.03)" 0.0 0.0 0.25 0.01 +#define warpX params.warpX // Curvature X + +#pragma parameter warpY " CurvatureY (default 0.04)" 0.0 0.0 0.25 0.01 +#define warpY params.warpY // Curvature Y + +#pragma parameter c_shape " Curvature Shape" 0.25 0.05 0.60 0.05 +#define c_shape global.c_shape // curvature shape + +#pragma parameter overscanX " Overscan X original pixels" 0.0 -200.0 200.0 1.0 +#define overscanX global.overscanX // OverscanX pixels + +#pragma parameter overscanY " Overscan Y original pixels" 0.0 -200.0 200.0 1.0 +#define overscanY global.overscanY // OverscanY pixels + +#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-12:'Trinitron'" 0.0 -1.0 12.0 1.0 +#define shadowMask params.shadowMask // Mask Style + +#pragma parameter maskstr " Mask Strength (0, 5-12)" 0.3 -0.5 1.0 0.025 +#define maskstr params.maskstr // Mask Strength + +#pragma parameter mcut " Mask 5-12 Low Strength" 1.10 0.0 2.0 0.05 +#define mcut params.mcut // Mask 5-12 dark color strength + +#pragma parameter masksize " CRT Mask Size" 1.0 1.0 4.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 mshift " Mask Shift/Stagger" 0.0 -8.0 8.0 1.0 +#define mshift params.mshift // mask 'line' shift/stagger + +#pragma parameter mask_layout " Mask Layout: RGB or BGR (check LCD panel) " 0.0 0.0 1.0 1.0 +#define mask_layout params.mask_layout // mask layout: RGB or BGR + +#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 Bright Pixels" 0.0 0.0 1.0 0.05 +#define slotmask params.slotmask + +#pragma parameter slotmask1 " Slot Mask Strength Dark Pixels" 0.0 0.0 1.0 0.05 +#define slotmask1 params.slotmask1 + +#pragma parameter slotwidth " Slot Mask Width" 2.0 1.0 8.0 1.0 +#define slotwidth params.slotwidth // Slot Mask Width + +#pragma parameter double_slot " Slot Mask Height: 2x1 or 4x1..." 1.0 1.0 4.0 1.0 +#define double_slot params.double_slot // Slot Mask Height + +#pragma parameter slotms " Slot Mask Size" 1.0 1.0 4.0 1.0 +#define slotms global.slotms // Slot Mask Size + +#pragma parameter mclip " Keep Mask effect with clipping" 0.50 0.0 1.0 0.05 +#define mclip global.mclip // + +#pragma parameter gamma_out "Gamma out" 1.75 1.0 5.0 0.05 +#define gamma_out global.gamma_out // output gamma + + +#pragma parameter bogus_deconvergence11 "[ HORIZONTAL/VERTICAL DECONVERGENCE ]: " 0.0 0.0 1.0 1.0 + +#pragma parameter dctypex " Deconvergence type X : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 + +#pragma parameter dctypey " Deconvergence type Y : 0.0 - static, other - dynamic" 0.0 0.0 0.75 0.05 + +#pragma parameter deconrr " Horizontal Deconvergence Red Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrg " Horizontal Deconvergence Green Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrb " Horizontal Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrry " Vertical Deconvergence Red Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrgy " Vertical Deconvergence Green Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter deconrby " Vertical Deconvergence Blue Range" 0.0 -15.0 15.0 0.25 + +#pragma parameter decons " Deconvergence Strength" 1.0 0.0 3.0 0.10 + +#pragma parameter addnoised " Add Noise" 0.0 -1.0 1.0 0.02 + +#pragma parameter noiseresd " Noise Resolution" 2.0 1.0 10.0 1.0 + +#pragma parameter noisetype " Noise Type: Colored, Luma" 0.0 0.0 1.0 1.0 + +#pragma parameter post_br " Post Brightness" 1.0 0.25 5.0 0.01 + + +#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.0001; +} + +#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 BloomPass; +layout(set = 0, binding = 4) uniform sampler2D PrePass; +layout(set = 0, binding = 5) uniform sampler2D Source; + +#define eps 1e-10 + +// Shadow mask (1-4 from PD CRT Lottes shader). + +vec3 Mask(vec2 pos, float mx) +{ + vec2 pos0 = pos; + pos.y = floor(pos.y/masksize); + float next_line = float(fract(pos.y*0.5) > 0.25); + pos0.x = (mshift > -0.25) ? (pos0.x + next_line * mshift) : (pos0.x + pos.y * mshift); + pos = floor(pos0/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.4, 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.49) { 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.49) + odd = 1.0; + if (fract((pos.y + odd)/2.0) < 0.49) + line = maskDark; + + pos.x = fract(pos.x/3.0); + + if (pos.x < 0.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.3) mask.r = maskLight; + else if (pos.x < 0.6) 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.49) + { 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.3) mask.r = 1.0; + else if (pos.x < 0.6) 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) + { + mask = vec3(0.0); + pos.x = fract(pos.x/2.0); + if (pos.x < 0.49) + { mask = 0.0.xxx; + } + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // BW Trinitron mask 8 + else if (shadowMask == 8.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask = 1.0.xxx; + else mask = 1.0.xxx; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // Magenta - Green - Black mask + else if (shadowMask == 9.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x/3.0); + if (pos.x < 0.3) mask = 0.0.xxx; + else if (pos.x < 0.6) mask.rb = 1.0.xx; + else mask.g = 1.0; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RGBX + else if (shadowMask == 10.0) + { + mask = vec3(0.0); + pos.x = fract(pos.x * 0.25); + if (pos.x < 0.2) mask = 0.0.xxx; + else if (pos.x < 0.4) mask.r = 1.0; + else if (pos.x < 0.7) 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; + } + + // 4k mask + else if (shadowMask == 11.0) + { + mask = vec3(0.0); + 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; + mask = clamp(mix( mix(one, mask, mcut), mix(one, mask, maskstr), mx), 0.0, 1.0) * dark_compensate; + } + + // RRGGBBX mask + else + { + mask = vec3(0.0); + pos.x = floor(mod(pos.x,7.0)); + if (pos.x < 1.0) mask = 0.0.xxx; + else if (pos.x < 3.0) mask.r = 1.0; + else if (pos.x < 5.0) 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; + } + + return mask; +} + +float SlotMask(vec2 pos, float m) +{ + if ((slotmask + slotmask1) == 0.0) return 1.0; + else + { + 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 = mix(1.0-slotmask1, 1.0-slotmask, 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; + } +} + +vec2 Warp(vec2 pos) +{ + pos = pos*2.0-1.0; + pos = mix(pos, vec2(pos.x*inversesqrt(1.0-c_shape*pos.y*pos.y), pos.y*inversesqrt(1.0-c_shape*pos.x*pos.x)), vec2(warpX, warpY)/c_shape); + 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; +} + +float humbar(float pos) +{ + if (global.barintensity == 0.0) return 1.0; else + { + pos = (global.barintensity >= 0.0) ? pos : (1.0-pos); + pos = fract(pos + mod(float(global.FrameCount),global.barspeed)/(global.barspeed-1.0)); + pos = (global.barintensity < 0.0) ? pos : (1.0-pos); + return (1.0-global.barintensity) + global.barintensity*pos; + } +} + + +float corner(vec2 pos) { + vec2 b = vec2(bsize1, bsize1) * vec2(1.0, OutputSize.x/OutputSize.y) * 0.05; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + float csize1 = mix(400.0, 7.0, pow(4.0*csize, 0.10)); + float crn = dot(pow(pos, csize1.xx), vec2(1.0, OutputSize.y/OutputSize.x)); + crn = (csize == 0.0) ? max(pos.x, pos.y) : pow(crn, 1.0/csize1); + pos = max(pos, crn); + vec2 res = (bsize1 == 0.0) ? 1.0.xx : mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, sborder.xx); + return sqrt(res.x*res.y); +} + + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +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; +} + +// noise function: +// Dedicated to the public domain. +// If you want a real license, you may consider this MIT/BSD/CC0/WTFPL-licensed (take your pick). +// Adapted from ChuckNorris - shadertoy: https://www.shadertoy.com/view/XtK3Dz + +vec3 noise(vec3 v){ + if (global.addnoised < 0.0) v.z = -global.addnoised; else v.z = mod(v.z,6001.0)/1753.0; + // ensure reasonable range + v = fract(v) + fract(v*1e4) + fract(v*1e-4); + // seed + v += vec3(0.12345, 0.6789, 0.314159); + // more iterations => more random + v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); + v = fract(v*dot(v, v)*123.456); + return v; +} + +void fetch_pixel (inout vec3 c, inout vec3 b, vec2 coord, vec2 bcoord) +{ + float stepx = OutputSize.z; + float stepy = OutputSize.w; + + float ds = global.decons; + + vec2 dx = vec2(stepx, 0.0); + vec2 dy = vec2(0.0, stepy); + + float posx = 2.0*coord.x - 1.0; + float posy = 2.0*coord.y - 1.0; + + if (global.dctypex > 0.025) + { + posx = sign(posx)*pow(abs(posx), 1.05-global.dctypex); + dx = posx * dx; + } + + if (global.dctypey > 0.025) + { + + posy = sign(posy)*pow(abs(posy), 1.05-global.dctypey); + dy = posy * dy; + } + + // if (global.dctypex > 0.025 || global.dctypey > 0.025) ds *= sqrt(posx*posx*sign(global.dctypex) + posy*posy*sign(global.dctypey)); + + vec2 rc = global.deconrr * dx + global.deconrry*dy; + vec2 gc = global.deconrg * dx + global.deconrgy*dy; + vec2 bc = global.deconrb * dx + global.deconrby*dy; + + float r1 = COMPAT_TEXTURE(Source, coord + rc).r; + float g1 = COMPAT_TEXTURE(Source, coord + gc).g; + float b1 = COMPAT_TEXTURE(Source, coord + bc).b; + + vec3 d = vec3(r1, g1, b1); + c = clamp(mix(c, d, ds), 0.0, 1.0); + + r1 = COMPAT_TEXTURE(BloomPass, bcoord + rc).r; + g1 = COMPAT_TEXTURE(BloomPass, bcoord + gc).g; + b1 = COMPAT_TEXTURE(BloomPass, bcoord + bc).b; + + d = vec3(r1, g1, b1); + b = clamp(mix(b, d, ds), 0.0, 1.0); +} + + +void main() +{ + vec4 SourceSize = global.OriginalSize; + + 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.5); + + // Calculating texel coordinates + + vec2 texcoord = TEX0.xy; + + if (IOS > 0.0 && !interb){ + vec2 ofactor = OutputSize.xy/SourceSize.xy; + vec2 intfactor = (IOS < 2.5) ? floor(ofactor) : ceil(ofactor); + vec2 diff = ofactor/intfactor; + float scan = diff.y; + texcoord = Overscan(texcoord, scan, scan); + if (IOS == 1.0 || IOS == 3.0) texcoord = vec2(TEX0.x, texcoord.y); + } + + texcoord = Overscan(texcoord, (global.OriginalSize.x - overscanX)/global.OriginalSize.x, (global.OriginalSize.y - overscanY)/global.OriginalSize.y); + + vec2 pos1 = TEX0.xy; + vec2 pos = Warp(texcoord); + vec2 pos0 = Warp(TEX0.xy); + vec3 color0 = COMPAT_TEXTURE(Source,pos1).rgb; + float c0 = max(max(color0.r, color0.g),color0.b); + + // color and bloom fetching + vec3 color = COMPAT_TEXTURE(Source,pos1).rgb; + vec3 Bloom = COMPAT_TEXTURE(BloomPass, pos).rgb; + fetch_pixel(color, Bloom, pos1, pos); + + float cm = max(max(color.r,color.g),color.b); + float mx1 = COMPAT_TEXTURE(Source, pos1 ).a; + float colmx = max(mx1, cm); + float w3 = min((c0 + 0.0005) / (pow(colmx, gamma_in/1.4) + 0.0005), 1.0); + + vec2 dx = vec2(0.001, 0.0); + float mx0 = COMPAT_TEXTURE(Source, pos1 - dx).a; + float mx2 = COMPAT_TEXTURE(Source, pos1 + dx).a; + float mx = max(max(mx0,mx1),max(mx2,cm)); + + vec3 one = vec3(1.0); + + // Apply Mask + + vec3 orig1 = color; + vec3 cmask = one; + + vec2 maskcoord = gl_FragCoord.xy * 1.000001; + + float smask = SlotMask(maskcoord, mx); + cmask*= Mask(maskcoord, mx); + + if (mask_layout > 0.5) cmask = cmask.rbg; + + vec3 cmask1 = cmask; + float smask1 = smask; + + if (mask_bloom > 0.025) + { + float maxb = max(max(Bloom.r,Bloom.g),Bloom.b); + maxb = pow(sqrt(maxb*mix(maxb, colmx, 0.75)),0.275); + vec3 mBloom = 0.5*(1.5*Bloom+0.5*maxb) * mix(1.0, 2.0-colmx, (bloom_dist + 0.5)); + float maskmx = 1.0; if (shadowMask > 0.5 || shadowMask < 4.5) maskmx = maskLight; else if (shadowMask > 6.5 && shadowMask < 10.5) maskmx = 1.0; else maskmx = max(max(cmask.r,cmask.g),cmask.b); + cmask = min(cmask + maxb*mBloom*mask_bloom, maskmx); + smask = min(smask + 0.9*maxb*max(max(mBloom.r,mBloom.g),mBloom.b)*mask_bloom, 1.0); + } + + 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); + cmask1 = min(cmask1*smask1, 1.0); + + 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(BloomPass, pos).rgb; + vec3 Ref = COMPAT_TEXTURE(LinearizePass, pos).rgb; + float maxb = COMPAT_TEXTURE(BloomPass, pos).a; + float vig = COMPAT_TEXTURE(PrePass, clamp(pos, 0.0+0.5*global.OriginalSize.zw, 1.0-0.5*global.OriginalSize.zw)).a; + + vec3 Bloom1 = Bloom; + + if (bloom < -0.01) Bloom1 = plant(Bloom, maxb); + + Bloom1 = min(Bloom1*(orig1+color), max(0.5*(colmx + orig1 - color),0.001*Bloom1)); + Bloom1 = 0.5*(Bloom1 + mix(Bloom1, mix(colmx*orig1, Bloom1, 0.5), 1.0-color)); + + Bloom1 = Bloom1 * mix(1.0, 2.0-colmx, bloom_dist); + + color = color + abs(bloom) * Bloom1; + + color = min(color, mix(one, cmask1, mclip)); + + if (!interb) color = declip(color, mix(1.0, w3, 0.6)); else w3 = 1.0; + + if (halation > 0.01) { + Bloom = mix(0.5*(Bloom + Bloom*Bloom), 0.75*Bloom*Bloom, colmx); + color = color + 2.0*max((2.0*mix(maxb*maxb, maxb, colmx)-0.5*max(max(Ref.r,Ref.g),Ref.b)),0.25)*mix(1.0,w3,0.5*colmx)*mix(one,cmask,0.6)*Bloom*halation; } + + Glow = mix(Glow, 0.25*color, 0.7*colmx); + if (glow >= 0.0) color = color + 0.5*Glow*glow; else { cmask*=cmask; cmask*=cmask; color = color + (-glow)*cmask*Glow; } + + color = min(color, 1.0); + + color = pow(color, vec3(1.0/gamma_out)); + + float rc = 0.6*sqrt(max(max(color.r, color.g), color.b))+0.4; + + if (abs(global.addnoised) > 0.01) + { + vec3 noise0 = noise(vec3(floor(OutputSize.xy * vTexCoord / global.noiseresd), float(global.FrameCount))); + if (global.noisetype < 0.5) color = mix(color, noise0, 0.25*abs(global.addnoised) * rc); + else color = min(color * mix(1.0, 1.5*noise0.x, 0.5*abs(global.addnoised)), 1.0); + } + + FragColor = vec4(color*vig*humbar(mix(pos.y, pos.x, global.bardir))*global.post_br*corner(pos0), 1.0); +} diff --git a/crt/shaders/guest/advanced/linearize-hires.slang b/crt/shaders/guest/hd/linearize-hd.slang similarity index 64% rename from crt/shaders/guest/advanced/linearize-hires.slang rename to crt/shaders/guest/hd/linearize-hd.slang index cbacce9..8fc44fe 100644 --- a/crt/shaders/guest/advanced/linearize-hires.slang +++ b/crt/shaders/guest/hd/linearize-hd.slang @@ -3,7 +3,7 @@ /* Interlacing - Copyright (C) 2020 guest(r) - guest.r@gmail.com + Copyright (C) 2020-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 @@ -24,6 +24,7 @@ layout(push_constant) uniform Push { vec4 OriginalSize; + vec4 SourceSize; vec4 OutputSize; uint FrameCount; float GAMMA_INPUT; @@ -32,9 +33,8 @@ layout(push_constant) uniform Push float iscan; float intres; float iscans; - float downsample_levelx; - float downsample_levely; float prescalex; + float prescaley; } params; @@ -45,7 +45,7 @@ layout(std140, set = 0, binding = 0) uniform UBO } global; -#pragma parameter GAMMA_INPUT "Gamma Input" 2.4 1.0 5.0 0.05 +#pragma parameter GAMMA_INPUT "Gamma Input" 1.80 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 @@ -53,7 +53,7 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter inter " Interlace Trigger Resolution :" 400.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 +#pragma parameter interm " Interlace Mode: OFF, Normal 1-3, Interpolation 4" 4.0 0.0 4.0 1.0 #define interm params.interm // interlace mode #pragma parameter iscan " Interlacing Scanline Effect" 0.20 0.0 1.0 0.05 @@ -62,13 +62,13 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter intres " Internal Resolution Y: 224p/240p, 1.5...y-dowsample" 0.0 0.0 6.0 0.5 // Joint parameter with main pass, values must match #define intres params.intres // interlace resolution -#pragma parameter downsample_levelx " Downsampling-X (High-res content, pre-scalers)" 0.0 0.0 3.0 0.05 -#define downsample_levelx params.downsample_levelx // downsample level +#pragma parameter prescalex " Prescale-X Factor (for xBR...pre-shader...)" 1.0 1.0 5.0 0.25 // Joint parameter with main pass, values must match +#define prescalex params.prescalex // prescale-x factor -#pragma parameter downsample_levely " Downsampling-Y (High-res content, pre-scalers)" 0.0 0.0 3.0 0.05 -#define downsample_levely params.downsample_levely // downsample level +#pragma parameter prescaley " Prescale-Y Factor (for xBR...pre-shader...)" 1.0 1.0 5.0 0.25 // Joint parameter with main pass, values must match +#define prescaley params.prescaley // prescale-y factor -#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.40 0.0 1.0 0.05 +#pragma parameter iscans " Interlacing (Scanline) Saturation" 0.25 0.0 1.0 0.05 #define iscans params.iscans // interlace saturation #pragma stage vertex @@ -79,13 +79,13 @@ layout(location = 0) out vec2 vTexCoord; void main() { gl_Position = global.MVP * Position; - vTexCoord = TexCoord * 1.000001; + 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 PrePass; +layout(set = 0, binding = 2) uniform sampler2D Source; #define COMPAT_TEXTURE(c,d) texture(c,d) @@ -97,38 +97,10 @@ vec3 plant (vec3 tar, float r) } -vec3 fetch_pixel(vec2 coord) -{ - vec2 dx = vec2(global.SourceSize.z, 0.0) * downsample_levelx; - vec2 dy = vec2(0.0, global.SourceSize.w) * downsample_levely; - vec2 d1 = dx + dy; - vec2 d2 = dx - dy; - - float sum = 15.0; - vec3 result = 3.0*COMPAT_TEXTURE(PrePass, coord ).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord + dx).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord - dx).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord + dy).rgb + - 2.0*COMPAT_TEXTURE(PrePass, coord - dy).rgb + - COMPAT_TEXTURE(PrePass, coord + d1).rgb + - COMPAT_TEXTURE(PrePass, coord - d1).rgb + - COMPAT_TEXTURE(PrePass, coord + d2).rgb + - COMPAT_TEXTURE(PrePass, coord - d2).rgb; - - return result/sum; -} - - void main() { - vec3 c1 = COMPAT_TEXTURE(PrePass, vTexCoord).rgb; - vec3 c2 = COMPAT_TEXTURE(PrePass, vTexCoord + vec2(0.0, params.OriginalSize.w)).rgb; - - if ((downsample_levelx + downsample_levely) > 0.025) - { - c1 = fetch_pixel(vTexCoord); - c2 = fetch_pixel(vTexCoord + vec2(0.0, params.OriginalSize.w)); - } + vec3 c1 = COMPAT_TEXTURE(Source, vTexCoord).rgb; + vec3 c2 = COMPAT_TEXTURE(Source, vTexCoord + vec2(0.0, params.SourceSize.w)).rgb; vec3 c = c1; @@ -148,7 +120,7 @@ void main() if (inter <= params.OriginalSize.y/yres_div && interm > 0.5 && intres != 1.0 && intres != 0.5) { - intera = 0.5; + intera = 0.25; float line_no = clamp(floor(mod(params.OriginalSize.y*vTexCoord.y, 2.0)), 0.0, 1.0); float frame_no = clamp(floor(mod(float(params.FrameCount),2.0)), 0.0, 1.0); float ii = abs(line_no-frame_no); @@ -159,10 +131,8 @@ void main() r = clamp(max(m1*ii, (1.0-iscan)*min(m1,m2)), 0.0, 1.0); 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 == 4.0) c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); - if (interm == 5.0) { c = mix(c2, c1, 0.5); c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b));} + if (interm == 4.0) { c = plant(mix(c, c*c, 0.5*iscans), max(max(c.r,c.g),c.b)); intera = 0.45; } } c = pow(c, vec3(gamma_in)); diff --git a/crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang b/crt/shaders/guest/hd/pre-shaders-afterglow.slang similarity index 68% rename from crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang rename to crt/shaders/guest/hd/pre-shaders-afterglow.slang index e065f38..caa24f5 100644 --- a/crt/shaders/guest/crt-gdv-new/pre-shaders-afterglow.slang +++ b/crt/shaders/guest/hd/pre-shaders-afterglow.slang @@ -1,9 +1,9 @@ #version 450 /* - CRT GDV Afterglow, color altering + CRT Advanced Afterglow, color altering - Copyright (C) 2019-2021 guest(r) and Dr. Venom + Copyright (C) 2019-2022 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 @@ -34,39 +34,58 @@ layout(push_constant) uniform Push float WP; float wp_saturation; float AS, sat; + float BP; + float vigstr; + float vigdef; + float sega_fix; + float pre_bb; + float contr; } params; -#pragma parameter AS " Afterglow Strength" 0.07 0.0 0.50 0.01 +#pragma parameter AS " Afterglow Strength" 0.20 0.0 0.60 0.01 #define AS params.AS -#pragma parameter sat " Afterglow saturation" 0.10 0.0 1.0 0.01 +#pragma parameter sat " Afterglow saturation" 0.50 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 +#pragma parameter CS " Display Gamut: sRGB, Modern, DCI, Adobe, Rec.2020" 0.0 0.0 4.0 1.0 + +#pragma parameter CP " CRT Profile: EBU | P22 | SMPTE-C | Philips | Trin." 0.0 -1.0 5.0 1.0 + +#define CP params.CP +#define CS params.CS + +#pragma parameter TNTC " LUT Colors: Trin.1 | Trin.2 | Nec Mult. | NTSC" 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 +#define LUTLOW 5.0 // "Fix LUT Dark - Range" from 0.0 to 50.0 - RGB singletons -#pragma parameter LUTBR " Fix LUT Brightness" 1.0 0.0 1.0 0.10 -#define LUTBR params.LUTBR +#define LUTBR 1.0 // "Fix LUT Brightness" from 0.0 to 1.0 -#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 + +#pragma parameter wp_saturation " Saturation Adjustment" 1.0 0.0 2.0 0.05 + +#pragma parameter pre_bb " Brightness Adjustment" 1.0 0.0 2.0 0.01 + +#pragma parameter contr " Contrast Adjustment" 0.0 -1.0 1.0 0.05 + +#pragma parameter sega_fix " Sega Brightness Fix" 0.0 0.0 1.0 1.0 + +#pragma parameter BP " Raise Black Level" 0.0 -100.0 25.0 1.0 + +#pragma parameter vigstr " Vignette Strength" 0.0 0.0 2.0 0.05 + +#pragma parameter vigdef " Vignette Size" 1.0 0.5 3.0 0.10 #define WP params.WP #define wp_saturation params.wp_saturation +#define BP params.BP layout(std140, set = 0, binding = 0) uniform UBO { @@ -148,11 +167,18 @@ mat3( -0.498611, 0.041555, 1.056972 ); +const mat3 ToModern = +mat3( + 2.791723, -0.894766, 0.041678, +-1.173165, 1.815586, -0.130886, +-0.440973, 0.032000, 1.002034 +); + const mat3 ToDCI = mat3( - 2.725394, -0.795168, 0.041242, --1.018003, 1.689732, -0.087639, --0.440163, 0.022647, 1.100929 + 2.493497, -0.829489, 0.035846, +-0.931384, 1.762664, -0.076172, +-0.402711, 0.023625, 0.956885 ); const mat3 ToAdobe = @@ -178,9 +204,9 @@ const mat3 D65_to_D55 = mat3 ( const mat3 D65_to_D93 = mat3 ( - 0.3683017655, 0.1899055978, 0.0172641453, - 0.3555467892, 0.7110935785, 0.1185155964, - 0.2475020592, 0.0990008237, 1.3035108450); + 0.3412754080, 0.1759701322, 0.0159972847, + 0.3646170520, 0.7292341040, 0.1215390173, + 0.2369894093, 0.0947957637, 1.2481442225); vec3 fix_lut(vec3 lutcolor, vec3 ref) @@ -193,6 +219,29 @@ vec3 fix_lut(vec3 lutcolor, vec3 ref) } +float vignette(vec2 pos) { + vec2 b = vec2(params.vigdef, params.vigdef) * vec2(1.0, params.OriginalSize.x/params.OriginalSize.y) * 0.125; + pos = clamp(pos, 0.0, 1.0); + pos = abs(2.0*(pos - 0.5)); + vec2 res = mix(0.0.xx, 1.0.xx, smoothstep(1.0.xx, 1.0.xx-b, sqrt(pos))); + res = pow(res, 0.70.xx); + return max(mix(1.0, sqrt(res.x*res.y), params.vigstr), 0.0); +} + + +vec3 plant (vec3 tar, float r) +{ + float t = max(max(tar.r,tar.g),tar.b) + 0.00001; + return tar * r / t; +} + +float contrast(float x) +{ + float y = 2.0*x-1.0; + y = (sin(y*1.57079632679)+1.0)*0.5; + return mix(x, y, params.contr); +} + void main() { vec4 imgColor = COMPAT_TEXTURE(StockPass, vTexCoord.xy); @@ -202,9 +251,14 @@ void main() float l = length(aftglow.rgb); aftglow.rgb = AS*w*normalize(pow(aftglow.rgb + 0.01, vec3(sat)))*l; + float bp = w * BP/255.0; + + if (params.sega_fix > 0.5) imgColor.rgb = imgColor.rgb * (255.0 / 239.0); + + imgColor.rgb = min(imgColor.rgb, 1.0); vec3 color = imgColor.rgb; - + if (int(TNTC) == 0) { color.rgb = imgColor.rgb; @@ -247,10 +301,6 @@ void main() 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)); @@ -261,10 +311,11 @@ void main() 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; } + if (CS == 0.0) { p = 2.2; m_out = ToSRGB; } else + if (CS == 1.0) { p = 2.2; m_out = ToModern; } else + if (CS == 2.0) { p = 2.6; m_out = ToDCI; } else + if (CS == 3.0) { p = 2.2; m_out = ToAdobe; } else + if (CS == 4.0) { p = 2.4; m_out = ToREC; } color = pow(c, vec3(p)); @@ -286,11 +337,13 @@ void main() 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 scolor1 = plant(pow(color, vec3(wp_saturation)), max(max(color.r,color.g),color.b)); + float luma = dot(color, vec3(0.299, 0.587, 0.114)); vec3 scolor2 = mix(vec3(luma), color, wp_saturation); color = (wp_saturation > 1.0) ? scolor1 : scolor2; - + + color = plant(color, contrast(max(max(color.r,color.g),color.b))); + p = 2.2; color = pow(color, vec3(p)); @@ -309,8 +362,13 @@ void main() color = mix(color, comp, m); color = pow(max(color, 0.0), vec3(1.0/p)); + + if (BP > -0.5) color = color + aftglow.rgb + bp; else + { + color = max(color + BP/255.0, 0.0) / (1.0 + BP/255.0*step(- BP/255.0, max(max(color.r,color.g),color.b))) + aftglow.rgb; + } - color = color + aftglow.rgb; + color = min(color * params.pre_bb, 1.0); - FragColor = vec4(color, 1.0); + FragColor = vec4(color, vignette(vTexCoord.xy)); } \ No newline at end of file diff --git a/crt/shaders/guest/linearize.slang b/crt/shaders/guest/hd/stock.slang similarity index 56% rename from crt/shaders/guest/linearize.slang rename to crt/shaders/guest/hd/stock.slang index ca8c99d..bf3cf6b 100644 --- a/crt/shaders/guest/linearize.slang +++ b/crt/shaders/guest/hd/stock.slang @@ -2,12 +2,12 @@ layout(push_constant) uniform Push { - float GAMMA_INPUT; + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; } params; -#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 { mat4 MVP; @@ -27,13 +27,9 @@ void main() #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D AfterglowPass; - - -#define COMPAT_TEXTURE(c,d) texture(c,d) +layout(set = 0, binding = 2) uniform sampler2D Source; void main() { - float gamma_in = 1.0/GAMMA_INPUT; - FragColor = vec4(pow(vec3(COMPAT_TEXTURE(AfterglowPass, vTexCoord)), vec3(GAMMA_INPUT)), gamma_in); + FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0); } \ No newline at end of file diff --git a/crt/shaders/guest/lut/README b/crt/shaders/guest/lut/README deleted file mode 100644 index 35c3fb0..0000000 --- a/crt/shaders/guest/lut/README +++ /dev/null @@ -1 +0,0 @@ -LUT's kindly provided by torridgristle. \ No newline at end of file diff --git a/crt/shaders/guest/lut/custom_lut.png b/crt/shaders/guest/lut/custom_lut.png deleted file mode 100644 index 97dd8d169773c18e7a740c750322f89c5a90594c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14233 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 diff --git a/crt/shaders/guest/lut/lut.slang b/crt/shaders/guest/lut/lut.slang deleted file mode 100644 index 1a30a34..0000000 --- a/crt/shaders/guest/lut/lut.slang +++ /dev/null @@ -1,112 +0,0 @@ -#version 450 - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - float TNTC; -} params; - -#pragma parameter TNTC "LUT Colors" 0.0 0.0 3.0 1.0 -#define TNTC params.TNTC - -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 SamplerLUT1; -layout(set = 0, binding = 4) uniform sampler2D SamplerLUT2; -layout(set = 0, binding = 5) uniform sampler2D SamplerLUT3; - -#define LUT_Size 32.0 -#define COMPAT_TEXTURE(c,d) texture(c,d) - -const mat3 D65_to_XYZ = mat3 ( - 0.4306190, 0.2220379, 0.0201853, - 0.3415419, 0.7066384, 0.1295504, - 0.1783091, 0.0713236, 0.9390944); - -const mat3 XYZ_to_D50 = mat3 ( - 2.9603944, -0.9787684, 0.0844874, - -1.4678519, 1.9161415, -0.2545973, - -0.4685105, 0.0334540, 1.4216174); - - -// This shouldn't be necessary but it seems some undefined values can -// creep in and each GPU vendor handles that differently. This keeps -// all values within a safe range -vec4 mixfix(vec4 a, vec4 b, float c) -{ - return (a.z < 1.0) ? mix(a, b, c) : a; -} - -void main() -{ - vec4 imgColor = COMPAT_TEXTURE(Source, vTexCoord.xy); - if (int(TNTC) == 0) - { - FragColor = imgColor; - return; - } - else - { - float red = ( imgColor.r * (LUT_Size - 1.0) + 0.499999 ) / (LUT_Size * LUT_Size); - float green = ( imgColor.g * (LUT_Size - 1.0) + 0.499999 ) / LUT_Size; - float blue1 = (floor( imgColor.b * (LUT_Size - 1.0) ) / LUT_Size) + red; - float blue2 = (ceil( imgColor.b * (LUT_Size - 1.0) ) / LUT_Size) + red; - float mixer = clamp(max((imgColor.b - blue1) / (blue2 - blue1), 0.0), 0.0, 32.0); - vec4 color1, color2, res; - if (int(TNTC) == 1) - { - color1 = COMPAT_TEXTURE( SamplerLUT1, vec2( blue1, green )); - color2 = COMPAT_TEXTURE( SamplerLUT1, vec2( blue2, green )); - res = mixfix(color1, color2, mixer); - float mx = max(res.r,max(res.g,res.b)); - float l = mix(length(imgColor.rgb), length(res.rgb), max(mx-0.5,0.0)); - res.rgb = mix(imgColor.rgb, res.rgb, clamp(25.0*(mx-0.02),0.0,1.0)); - res.rgb = normalize(res.rgb+1e-10)*l; - vec3 cooler = D65_to_XYZ*res.rgb; - cooler = XYZ_to_D50*cooler; - res.rgb = mix(res.rgb, cooler, 0.25); - } - else if (int(TNTC) == 2) - { - color1 = COMPAT_TEXTURE( SamplerLUT2, vec2( blue1, green )); - color2 = COMPAT_TEXTURE( SamplerLUT2, vec2( blue2, green )); - res = mixfix(color1, color2, mixer); - float l = mix(length(imgColor.rgb), length(res.rgb), 0.4); - res.rgb = normalize(res.rgb + 1e-10)*l; - } - else if (int(TNTC) == 3) - { - color1 = COMPAT_TEXTURE( SamplerLUT3, vec2( blue1, green )); - color2 = COMPAT_TEXTURE( SamplerLUT3, vec2( blue2, green )); - res = mixfix(color1, color2, mixer); - res.rgb = pow(res.rgb, vec3(1.0/1.20)); - float mx = max(res.r,max(res.g,res.b)); - res.rgb = mix(imgColor.rgb, res.rgb, clamp(25.0*(mx-0.05),0.0,1.0)); - float l = length(imgColor.rgb); - res.rgb = normalize(res.rgb + 1e-10)*l; - } - - FragColor = vec4(mix(imgColor.rgb, res.rgb, min(TNTC,1.0)),1.0); - } -} diff --git a/crt/shaders/guest/lut/other1.png b/crt/shaders/guest/lut/other1.png deleted file mode 100644 index dc09353eba85cc2154d1e664c27d388fefbdf5a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11494 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�>k>Vz zAb4=R%?Msa1KP7+-1r#QAwVcY%;5nP^DGYl?w#mTQlR!&ba4Elh47zCZ(%8x3=r#Q zVq!Hsb6^u;6T54FCOQvWMG|?g)I3jR&IBwt>DQdvJ^F4|F-aY~2=i*duA@W>q_n4y zLd)&T+I(L!{3tP+n9*RWC%sD^#0VU3C0IQ1zxyv@5_#W17ck2iHT-+~{(b(O;X~7q z5(wv17)=7^K5&O$kVyS#_>i;W%A3w!!gR|4pHYNrPcRTa?@5gUU{c`wUC&%(<0sqYS z8sVe=Grm58|G(Kll;PL06sn1PrO|rCud|$BTrQo4PlBO$9o{N0@_mso$??l9>3)U7 zzS6d&-@e{R^2TQZhAT+Y_qANKCoo(zcYbJtMO^#Mq^=HL6O}^nhVFBCs?8umxt7d5 zNPZM_CKzsG36061(;ZP8eK^5GH(J?j>I0+2hp*)j5971oQjh zc6J&*^<7FFU`#OnixR9H#2XE6Hqh^FbQ*Y0dCE_bT|A@u^XjKyiE~TT+(3DjhJ)%} z$zCR3**Hhy>{oc%5L9B$b0nH09l#`Y+^UTB5r$UWfkOg6E}ni6m<1wr)jJ;0Y-m=U z=3c)g{_*qTtMS**hW}#oQ+s?|qxPe!?$3&E8g%4|694!Bevr7_-p8-61|{*;?)iTr zy}1O4`0O9#8))^7?~zpMM+4EDZ3YUM2HM|0IX*q~`-(%)OPanZde9x59AG0lEWrT1 zsa&>;#`jhK;(P7eD3il^s;*TmL77^{rq0+o#o!EYa*mDnQUz|D*XV(Rfb(Y}RfW;fq;1;WrESga6{RE zY02H&@uM4v!O!9U+!7;d-;ed%;YSAfas2g5;_Exx@9||uFy{C?^t6NT#J?>Nm>S^s z3kavrAj$auTs7#W^Id!ka#387&tdrD;99~4jP_IgZxWzOI`U}|00D4sT^W9%>Lnx= z!H<=VPF{`Q;r_-)`(Ypkno+Ge;SkURnq}yIW<^{gN|3k!zi6Ph^*K{&qO{AYP5;af z45{pOGyKf%G#+I}AVHAO@)>pP>=z#nCm4F{oH|UibO=W0gdjP=fDIk`hrL1MdLH2k zy@dG*8so+{`4Pd`kHsK~JWM1guOO=S<+||)IXq*z-SwcuyK)Z9AljL0W)!PD zAx)wJ&EVsL?_3ta0|DS$XqtInc8-K?3Js0}D+1FP?2rF}2nON|4em@eT>O3FIT&3` z_Lk!S^WY>2#*bbliP5&br)&@bI68sh&T1(0z4%#ci?2;ab|GqL_TK`z=}o^HY;^6{#OP{rBs$&_&^0<_D#z2}UF5RcOFbn*d+|ZD_Wepn1q?fd2LH z_4DKBR~X|T-;KWz0U!vk%&-Ue1SdZ3pGi3WUpNSC*zCeN1Je4^G@vC+#uMOs>onyA zZufL@JH7_z^%|P+6!Z@yMuJg7yP36;@`qN8U>u?GBM-gmQhLkU9}Bq*^f~oW2Z6z& zUhSS>KP51{pIbjnVkW*L(?do~!}-a8J2fOoutX;Y9Qy~|!agnU01pV zMdtWioGVNCoM2RtP!2gvUBGF8?j(-58OH&*{<8uE%-n=VWG5J${Issdr{1!ExVTZp zH1`d&TqYwVEHRY*DM>_t_G9Lt@zeIpr7Xm^AVZEO)iC$zABKOT%U zbhC+{vcKj8!)VMC4mqfleY<^Li)+v%5R5XH%ZvM#m7)CW;`c(6@lQ?yFnBw~iTDsOpWAaOlLBBlabT`>)iC`miYS> zUvnp)-DLbS1JMWu0Q2fb>!jYrKR_@@P+>q99udz@K4`#=QkiSO;rB_*1ScRL3W6;V z40O9!+C3TyWpD%jrho8KLno!(>d*Ezc08vDBqcfw0-OjqU(W=PuO2-=Nl>@!=m`3& zNUa;$3BW?*Q!S=r73R{IU>G<#h9(6w!b$Cq?4{4!?tdmGGjXH*e_Icy*)hg&JOBUy M07*qoM6N<$g1fAclK=n! diff --git a/crt/shaders/guest/lut/sony_trinitron1.png b/crt/shaders/guest/lut/sony_trinitron1.png deleted file mode 100644 index bf1fb5bf8765ac2e4d940384fe7341c70d5aae50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10273 zcmWlf`#;l<7suzG%UU7VSP~L)H;N4<-B9G3OeB;{sKjiv`rIsr*25g`@3=M4Qu_WY1#t)g98_{ueZIZU!pHZmjB4{~9E1 zOX*OsqC4&mUXhA=YbH1!xpK))GI7+yWa%*H=;@B5aNuSa!<^ia_1muN+GB9^d1fbG zG$e9hD@8bD__rRKDH8g#>T>oX&-asA;o|ZQnp50n@0ma7CZmlte)=iKt=8AB{7>KF z1cluyY)gm0J*0X0=}#(bGzI;07E0bS&^T8i6Cd@jJIgd-F*LFH28LO%b@l$0s)U-S zqN+l+uB{vwvqMGjvRpvLCy73;Li;5UyzZrNAtU`GSy_t2E5ZfO7YU0fNo0gF4fXll z`GM01R56bX%J{J5U6h%7i`6=C85O}qbvwVsmgWVoGpY^b>AVYHr6{|Q8sy&WlqfV3 z^%-Q7OLlK6)kl~&AKx_Y4H_e~ z{&N@1W!yM*iqxQNsde%48rFgR%OiB^DIJ1;B7`BV)M$hlyAa4@AC&SxIRPqj4Rg~$ zY@WO5?e6T(`6##ugCE9vU@k=t;osM6CNT=Qgb!r+!jI>=@T&eeX56Ni64u&oBctlM zITDkRgcPT_8YgtK&~@zr2l5G0J)cc^19>8Lh_>y>A2S6NuT7!qqUgtt?cv+{h*HNI zOrUxvLkHWpEcSrA5OmuDfyX#ilq@{@dDSCT^|Po`q^{p~0n?wM@}at^+B5sk#o@+l z#syi)EJ)gWgiFo0mUV~)I5G^F&t}rr4gPJ}U=O@VasIwen~A^tW&}RQO`R+ieV-}j z3ew!LA##P^-rm%#dMI(^A0pQ<7%Te3`h(EuiST@OE zpD)2fj^l5a7|Q(EUVXm2S`Z!laqBR&S^Z$N+>-8Nj(Il@uZ4NU1i~|s!U%6H^HX}S zDTxaOfca1XZ?BNSjd&#?>8|J4yktE;mU|l%Fl(w8BV4cQ%_`#_>@;9Y=>|OXV)jnZ zo3Q&U6*dDdWxgd1jmr){eRL=OtLXPq$nnZ4-aIv~0VY_Q%TSyavB`)KZFJt~bbrxP zSY}R{v|AT1{r#csCct$I+}*M{fzoQ;*Qa|bd(&x{P2&N*9ne_#?vIk_nt>Qj_yPyn zw5<+dyJ7H{rdilP&mo@`57I8`+&eui8h{Az0wOx)uz~~o;LL%|y4fqHE)F9nbu&@j z(XE_T$+}asMb&$n&)hS@B&ibs;Lbf0WDQ9gk)jE?1ADD-w^TIcTtc1o{1AK?jgb0S zhyAyv){A~k;rDA@+YwV=`#coIDpgH4w>lEYGCdE)GABrs>xblW$L(_vfZ80A0{OA8 zHsY^5!((oa$*|AvB8r_l$fn4&E@JIEq8osh1}Bj{<~t7;>{$FbqL*e`6;*{-l~{uk zDt)642XyTdG)$MKnRx=vUG$-Qa-6v*HPj2p6sLh$P+T7z=N<(h|)j` z`9PKC`IV;Bc#I-gxQF0MrB^l(?>1nHLztD2K)rrN?Cd-vhQ5{Atz7MhYQsTLri8Ve zCChFKZu=QidJsK#lfYDKz0enDeKH+6arI#Er>@bHEzn;itKgs`?Osyl=Z(?!&h{xK zpDOD~^+iwNLBYRw&U81j#=PgU_U=ED-iP(n?E)E>qUS!Dr1VM%z6J+p`b4mmEfbgt zhw-1m>{R&l{Pg44nSQ7EWCm}BH{qM0k9k=sK{>b@hfv2I>a0ZU%U70=mv|I`XMnzk z1suaNUn&4|@$@QId+O(9AMMXy5a@xuk8^SL<-HKdkxUT6aQtEl-rn!;#*Dw9P)ZIK zL)?97ZgXCS$I+TxlMc;ZmPglGe$Jv5k4c{vS@Y${A`nI}znNHaS^aIz7lO7c9I_Cs ziB7e|nCwXMp7W6>c$^hNR9gM)y+^9y=#%UCRKwNA>wOf`YJB;TOQ+lBDHX)D;ZGsQ`)S*(3SaLhpmYT?Wk~5dR;OmvC z>yZ`4Iwog2TErioTKX^~*#87u@{hPH<_LSmKc#bVb~j0C1bJ~WPI=7`j?jxRE9GU+ zj%cMD76aF~E^-ytb-$pzW;F$tn(OOmjc2lTuj6S_Viu|sx1^D1$y=!&5{asaiRx$c zC6r|Z6UfX(iRIsj&DeN(?WDC@vBID(=^MU21gsjl=Zr7sncA@Ut<${`ay`@RGF*0B zc&3H}-@aL-EIdhwUN`Jsn?y0I_AqEgS0AFktz=@ypfE6}o#6{SrezcO6U@!zXm%CE zXTBNJMJz#%Fun$$UCxwFkp_TJ zx5fwaNY5&KRP&s^48{uF!F@7WNYhX-PZ+;XkJt8jK?y$hRWP^nF+hEnLhB@a6Quxt z+!!|Zx~-3fS;u?w_QAmVv6Zl}GkU$AM~a0d%zmCzmR`a&m7d@{frK>S{vB5> z61>+Fw%qf=Q(4G^J8y|?Z2D%TM!iT_V>O%F0@v8SaN}~9tmA62#*(gcRo~@^DOMH3&av82jQow?(QWYI{cqTNJXh<_BLdWe zo#d!uHFTFC-VeA5!lSN*Teza5g6CCZn4F|fsOUN7OFWkMHdpipyoiR7I-uUZ?;ddq zj*6PQ0ku0}AKuyiDyKy_=TSA1d17#kE1HengE5*@aZi{`SIYsCuBE`x&}Ug7n7B z{-9g_Ks{CPHtEV}Om2_z?h}$YSHY1Nsphe-TViHKFR8w(NafLLZAH-o;I;)|uy&di zL^upqe0Y2RS>llWoN1QnS4(>A4sfPu?>C2OHs91bZx`uQ{FfmOc?q)%9k(hfwbpkh zdMsPPOiY}pS|M!ic83$)KD-~7<5LkG;rzdRevI(#9Ly+kySJT`<)>ba@R8Q_CiQVu+Y+2C ze8S!HUpK6v?!{;|1$NSH1?#E7f8CVrKC(Rl&r6`fL*H*Peq{40}a zb$nbXr8KZ*WGi~H0`)u9$fgX&x4s>DaPNhhVZ_a);f^wZ8r7(#KqhOq*a2mHwSD#2 zmlb+aUknu8SWRvneBmL@xTIl8$Hn8v#rhxiZx|5nW6cj#A2qJ3Ld+3P#X+|vwO}I6 zNM^umif&0?CU;@BTDZz;p6u~RX{C6Az!_h2Og){~8ZAI{{I6G+DmJRZX`%JlNe zedL*RZXLG;j0pJ4{X*eN2|YzGQ{etT;EX2 z;F#y=K&2(Dc541kX$9=7S;IVt#{~lH4{>^^LrczW4lV$@qF%TPZ(Q!UR20eg?d^z@|8wjb=488*1tbO9?tM~N z^WI6M1o>@P6Ox)x$2*Bzk5Rs-yxteAPzK5v&vShR7{-w6^_j;THPn`m;2K+Z>$q<$ zagEJXf653~rkrp9di(8F zmi~mtk6xEC=2o%@$9}|_uZOcZM*tU$P=;CQv9EO2lnq@XP_9et0}6m{EBIVB{3men z!c*!VY%XFNPjnPMM~~co;sGSwwF-x+fRQ20Z!<(fb7^@>z!N=#J;9Q6fJixRztr`LDOFFrzw8?b%_6@6#zWFohsf@Yt|I zF>`oNPApFKPnG!ftnnA>Z)XREi6pR&G~h4cGq(J~sdN@a&}|(4aut8!6s`xr9ZHf4 zKHT|1s4P15b#oTFPDnhC?b#sL*Rn>cZP97T2FJmDQF#e)`)fEmC6b#I4|ecWl9Y#& z*V7i5MzM_aPtlCz!~N3qq65FWU!8*g9y2~xWJAAZp>`FMPCnpJH!1hGqx~TjI1iBF z!a?pA1M>CubR+@Dv4WMSFmca^W8fR%InDxgY)J9O@At$>0|>60*3XthGh5J! zgIA}@RQzfARpk|M#n6`#qwggq{PlZH%wZtrqtIo{F$#T;x=Q-%Pcis}z{_3=NoY1EV`Yd?z z%14uR@^*!NV0-+Y35xt^TxLg$bh=u@E~w>KJp?yQ{o_s3{lm{sX9^{(_v>9rjCy_c zMPNEX2Y1&*(O)xcJw!Ae+!3`+UDy{2Sw^UvMLs$6bb)bb7kr&Mi=J7W&C63Xxn^&V z22g6wU3(rUovRm7q)$W*IT;x7rlz^H=LxonQg%;?KRsAvU?IY$nC$DmDD*^tJ-tL} z?J1q3XF&)tw!Y8in5;3c$LAOgTf%LT(RjPw*4`uif#d2evJvVF{{e1<+Mr^+nTaNJ zCNiq;BYp$L)D!-8B3lbJ=$1kVW{cNz!d1|{slmIqv1Yy3W*4G#_S<6xY5RSvYRsAN zXIAr4OnU;0z>*+?3<#m=mKd6voubL}w(8(T$W$jiH1hztJDguNk=zI1RR4G6^SH-}cMYF^WO zlu8_Jp7g5k6m1s}HuDtVJQ_`v+EObI4s0|xuqjpoS*5mSzekw9bopJ5otzrIqWnbk zlK0a9-T(w%G6$>qKT#q-rurP5qk8SbsHx3RWBL%4`WWp2aUOX}!r^A&e%fB%5Z{K> zLsKH4c4D;u@~;mS0USB{U|y%u4R=kyH$%KztMq{F56Te?)lESfUxO?sze~xXkJg23 z*>slW%VIcaKh7zjHS{hdnNO%WL+6lZ<1CiD^{wawsN?5u^sQZfE=-X1zQ!%u%-pOp zOGscY7{Lc)iC#l8c7(8l5fvdSPw3~xa*R8Y?U1wNf6`yQI-qo2vR8TZ87CFE!ZSiS zeapydy{&sL?E@@tq*}=JBpBYrrQ1Ab+(JGPoBth{k4q!NF?~?Tetm_;|KmLRqz~?ys@COQknjW zpafD&!8;hIJNukyed+uCH35_D!H3PIA7&L4J`|F35TU9Q9SwHft-0K@jzFkgxSAfX zr3b}}gSZm(v0pKJ5H)dd`8^A|J1wEy=kSr1jT7)K^iRMv8^EQV$;Q@Ev77uCXQX_m z^jLG-^K?=;T-nqm5ma3~1yWeCfZ{#Dp--eL$B62Alp%u;MgBpY$lxxl!FeX0YgQA- z7l`;|52k#~usGyt58iqwroWn>c^YR`BclDYu4${vkaz`^CLilo0$Ce zED>>WwO9&y+!k?L$UHJxLO0AS*`=}Lu}%uM=0xGYap^uN>0evYZ^eVOXd$>mNURaF=>+yb1L!x^SVSQk{}2e|NVzLcS2a zjMKIR1p6_ct1}${&;Rnv3Hw591(kmYflz_8+7V(94lnT>(~G%Hhl{Bld(KCVsgoTr zL6#K$^onJ_L3wqJYTO{*^vnK~@y}XJ*HK^~Lt9}@9`n-*#4i&)SJ^nKQcjgV6DlCi zpzlX}h-q30cirU(a&O`0SCxC$-S?V*Ckde&5mx}=bFo!QRfV^~sR*|p!18ZmvYZw_09{9Oa*az`mh$+-MC`sGX~OU-Ve6U8u6Iq-K5N|%U6_t9RE zjonLZQV@1Cy+SrTr^obpC5ISCYBObshN)*3aBU13%;RorVXszPQiaDGj%fU|fk=mM zQ+Q6~u2ht4+XYVY!yQ1TiIab)zC;uW-7cPXrkX zk#UU5Fj%hT#dDkG&7G6eo7fMZIwwi_HtVzHi|7mIE+mdYZPs968GBR3Ym!v-yg+eS zrp-weG_!2?ZO0vJIBX3(G+96FW^w4>mz|f&ZS&K_3;8|~b>m#y-3Hrue2W;4?+F{& z_$8*>;$m6mVU=Lf!%VV~*I5aukV|^aOLFn*@g{X}_?yEhmW7e!L8f1dwdjY&+bpz$ zABd^dy2+2Qj-2Z0-R)}+DkUze0#C~Z{U5)hYmqyu|I8r8un@qxO70C;W8J)Dg{TPR zylpykyup~f;q@*#XBX22-r%-qoCj;P>4T!gCk%pu)#1)Z;d35vH;dw!VP9^yG5i6R;?APc1N^Od48~D{l%x4aWE1gcn(nW8i_&a?uVs}x)v4v!d!dOz) z8tA>4S=)_1UyeNg#cj+h=mnrz_KX*)KO7xf03SDPlF5v|-dFS)GHEWF4Qj}rX$N*7 z3DWRh4`5b$2KZ?&#=eNJAddaa(mk^i`5osBC){B$c*BStK=EG7$SlTOWpL7Qpr_Oa zzkpoiKk@#GZhYV-fZIljI~Clx8mqm#-kS|$>I4OS_>4L|b?;{Z5aUM`9WBs~`FV{_ z8&R@-7mm~TvoEzz*woalkAQ8vq&WH^W%q1V91i42MVK;!Xr>KHK*Zk%NMri-vfDED z$vxQ~xB5ByW9YpP_}K6Iihpn4)Qo?JDMenFDZ9eOG|KJ>x~K5ZBT%V}dkSJym-<=k zN#=JE#bNzP<$i^mG%MOG9cJ3K?eOvBbm3wK*CciXCG(uSrH;(@;6(1v_dtKF!mz^- zPpsI59Chf=eZQ^qSPQx48t zq~cEm!i)!Z_u7;?(8{JT*Ap?*{qM?-)I$q;S}m);$h<#$RuogHgw|8ibwc|E;}9Po z)v9A}DjBy9S!UuB2O_$t$phyH)(nj~PU2nR*3+-29tN!97G<-nuZwwHQ|_aQf{&-J zc_}Sbr_OH5Ks@CCTw9&1SI)C)`2H+^C*)6ahj8CP?PK~zO-X|B`I~OfuEqv3&w`ph z1=ins{8|LT347sJb^(8aLj|u?gHO9VP7{qf_k)!viH4XBDS;bo`Xot z7bWtYFc{W>)aqrG$3M^~x=7j~x=S=f`WKB3EftuOB;T?~0=@&ON_s><$Xl+FS zqr|qdtzdE1H}_IGepBclpL;0O)-LYNeOi2*k_w*W^O1C|8w1!rheHeqOO;DJk1g9T ztlB-u|C6nQjd2|2ai1p+?4N^yb?ymZbhoFHc#Ta*f%U@6QJ47P9XNZ0|1z%%y6L32w1W2Xp{PUg}-?mQh+ZLher+ z`?+`&nZlUZ1KLF21fi?W2)mC=>7MHrd*|D>H)z;*6@+4Z z$;R*P4ddx6b8%m|-Xn6W&25s&6z>2`-k!wPMZqua(yku`^c0Aoc;Dqx7tEy{9hbo9 zXezD8eEyMlrClk^LIefyB%4CIU@-a$uz~+zAZz9k~u79F9KSU^D>q_rn+|3m|Jtswbd_*jTEUj-C z7Br{xQfq9ASW2x62%mUPUzZ`37uc~C%LU-DO;f9bEGkE z{ylmAOc7XRmh4I`Vx4`>aS^PbOZ?@x(1RSo!=2D{24khb>-1vwy!7K055;XJ>}2qt zb=_1y#ukmqMpuXli#@pdGKVxsJ*E_>n=KXfrQi2~M?q}R94j1P1D6O&GZ%OFF>t4q z)X=m}Gj!*r4Y#Na83IEd3@>wd#X)pDTX(aWobb=YCx}bS6`Qnauxs&X95Z{hJD9_N{pl}bFYwU`42T7CJ8@u zgGj6E)U9*M&}`<*>W09a`Eg@r8z#Yf0@kVUVK|bFGc+{!nfL7pS2}%2ymdwsCjoyC z**=la;2elsU%)UCNu+CgKk>z_L`NCYcc}=$TTC{nWUrcwcG3y7;(}Jlw-Xh_%UyoF zx7`;`@dHKWrc{)^=aG1|`Y}^>HS4o1@}rEDm*W?5KeU7ni%X@V)LAh2l-OlJn6eL9 zcx{I_ueHlnoxU-CR18`cK>T_Ge*fk&gu9e4dc<>4%7BBpH9F9FdBpOiS0^n$OA1no zEE|tu)G=|TrevWEu)&var5}dgN8eQA3r{FT#~!Mc7Ty*kdm-qHGsk3Sr3(N*#!thl z`#WjXL))yhK$|g$g%%yxRz{hju9xnv++XD)VGtkob!Oa{T_FC)geab*nLGQbT5NsF zLp=>m7A4?bT*1?G-k+9_+G;cFW`w;P+S8A)Jv%~|5M^H5dz;lL)+1inH1JJB+co9f z;@#WnCtSC_9(Vr&$aK`XS5Uu{pl_2pT6m4AwcPYM@@+q|=^eWVvs!1E9Kh-^>jbw{ z+YJ06*K}EiOz0ISz#49Myy~OS{^=syge?+Cb*$Rv6=COQk^Z;^VL(afjv;!JJFjP* zXf-rv-sd^cC^Sx6TQC3C*QEGhRA^lPEUrw=3wnHF8GBcQ$_$%}D`9Jr!2exDrB{oC z?6vx*HX^^@y-5PTvLsiTV7vZCzvhJ3JQofh4$s{Nr+g~zSnzU;dh=QH(Ps#pKI~y? zIkbVTuA$=duolTX*@Hl-5az$H=B1MT2S`P>t?YM1C zl~|i3F~~fB4`!=T@%B*X8oi$hE_dCBl`X#TK~*HW8e!(&OL^skuQMy^rf+DNADa5` zMr6yI7sHuWlet~e220RcPV5&5X-Hc~l=5klCpdw|W1DJdftniJ07hA+;nKuiQEe&h zpDZ_n{0Jyy6|RWXS(B$2ytS`pA2H2CM0N7Xlk4M0Wq}ZMg!o5W*xC7Yku+KptH+1d zRV2X;l0RZ7YlfyBV1lmPf62%-_Fu9A4O=k12)>v;vsIBFi`M42Nc3~aSK-k=Lm84Z zHOW!DzQG0#FWFBKMRqf!o_WyYyL>MkLzTVOX6p(p^o?Gp*C`$aZ)tJ`;bO=|39qux zG(MOizgGf-#yIqG7v%R*WK{K=P2K+1boKj&m|5Fb=>5ktEQbiP%eGkwTqvd>t1g3o zTa0!|WA~)#4$gKEqQ>4nv<-X*5mtM+XQ7yb2vzvST!8Hv6wQ-cmSDDx&U!zXk1-c< zSNroZ6$k0^+to#qaU{Oj6VC852o=eQ%cE*PsoQ_T^A9^q8E+l1RO z365Qoidqjd$W9{kNy&6EK!dCF!I&0b@{-2+h1I77a zN_fMQ6~W0J40y)Bi}Rz%uwTk)AC(i}?7rnZTsZjq~?@;0ECF%Z^X`ABKFE3%C{Fc-TY5}AnXAxFNYmuX&4ymx zN0xHoA!d^X4A9wrI0#Nt+KuV74M{h&j5ztpcoLl){Xte~s>Pfjw&C)qnp)Erf1T7-3j>fg*QmF;;-crB1Be;`4^N8&@$4Xeji*VMk06@@E@fI&{bH62)~e z)L+n3_@n@G@iF<5CDb5s@oI_a*JD}A&uQb1M*z*oK~?^EW6C;u7^r2qU@TmenBKH= z97>tOT@LbdkB-lZ4Az4yPGDpwo~->;fjmb31+`6cX@&Uqi?n^u+C2?D%%2n<6HKR$ z0Mq;a5$t^i7|Kegm-sla4z{ko^I28thY^^U=bby7AycCe3o@SlWmcDafW0~PdR4mv-aE+gms`;$ml9nB|H&W{@6i{2r}#f} z@qh|E(^mn6Tz#~i;2tqdFs#a5VIjt`dBpc70-u#}=G9b)Z2*{Tj$Bn3MZKc2R_=3o zu>#F#OApJluUVT9$&f zF4`)PSo6tjE9A6>=)QEp&&}HjU*QKj;5oiGIP#RFuQ{jaR>l< diff --git a/crt/shaders/guest/lut/sony_trinitron2.png b/crt/shaders/guest/lut/sony_trinitron2.png deleted file mode 100644 index b4422ba98b5bcf82c730dc3afce8ae80d8d8e42d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17309 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