From 5ab3c761094f1751c24b948e5ca29d23a39e884d Mon Sep 17 00:00:00 2001 From: hunterk Date: Tue, 17 Sep 2024 11:29:08 -0500 Subject: [PATCH] update patchy-ntsc shaders and presets (#637) --- .../patchy-mesen-raw-palette.slangp | 43 +- ntsc/patchy-blastem.slangp | 42 +- ntsc/patchy-genplusgx.slangp | 44 +- .../patchy-ntsc.slangp => patchy-snes.slangp} | 67 ++- .../afterglow0-update-pass1.slang | 37 ++ .../afterglow0-update-pass2.slang | 92 ++++ .../afterglow0-update-pass3.slang | 374 ++++++++++++++ .../afterglow0-update.slangp | 34 ++ .../patchy-ntsc/generationcommands.txt | 2 +- ntsc/shaders/patchy-ntsc/linear-to-srgb.slang | 31 +- ntsc/shaders/patchy-ntsc/patchy-color.slang | 31 +- ntsc/shaders/patchy-ntsc/patchy-color.slangp | 61 --- ...s2.slang => patchy-ntsc-combine-y-c.slang} | 39 +- ...ang => patchy-ntsc-decode-y-rmy-bmy.slang} | 11 +- ...ss1.slang => patchy-ntsc-encode-y-c.slang} | 32 +- ...tsc-pass5.slang => patchy-ntsc-eotf.slang} | 84 +++- .../patchy-ntsc/patchy-ntsc-inc-filters.inc | 456 ++++++++++++++---- .../patchy-ntsc/patchy-ntsc-inc-params.inc | 173 ++++--- .../patchy-ntsc/patchy-ntsc-noise.slang | 100 ++++ .../patchy-ntsc/patchy-ntsc-pass3.slang | 120 ----- .../patchy-ntsc-separate-y-c.slang | 138 ++++++ .../trilinearLUT-switchable.slangp | 28 -- ntsc/shaders/patchy-ntsc/trilinearLUT.slang | 113 ----- 23 files changed, 1587 insertions(+), 565 deletions(-) rename ntsc/{shaders/patchy-ntsc/patchy-ntsc.slangp => patchy-snes.slangp} (57%) create mode 100644 ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass1.slang create mode 100644 ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass2.slang create mode 100644 ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass3.slang create mode 100644 ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update.slangp delete mode 100644 ntsc/shaders/patchy-ntsc/patchy-color.slangp rename ntsc/shaders/patchy-ntsc/{patchy-ntsc-pass2.slang => patchy-ntsc-combine-y-c.slang} (68%) rename ntsc/shaders/patchy-ntsc/{patchy-ntsc-pass4.slang => patchy-ntsc-decode-y-rmy-bmy.slang} (87%) rename ntsc/shaders/patchy-ntsc/{patchy-ntsc-pass1.slang => patchy-ntsc-encode-y-c.slang} (90%) rename ntsc/shaders/patchy-ntsc/{patchy-ntsc-pass5.slang => patchy-ntsc-eotf.slang} (72%) create mode 100644 ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang delete mode 100644 ntsc/shaders/patchy-ntsc/patchy-ntsc-pass3.slang create mode 100644 ntsc/shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang delete mode 100644 ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slangp delete mode 100644 ntsc/shaders/patchy-ntsc/trilinearLUT.slang diff --git a/nes_raw_palette/patchy-mesen-raw-palette.slangp b/nes_raw_palette/patchy-mesen-raw-palette.slangp index 1782e6d1..bac6d4b7 100644 --- a/nes_raw_palette/patchy-mesen-raw-palette.slangp +++ b/nes_raw_palette/patchy-mesen-raw-palette.slangp @@ -1,6 +1,6 @@ -shaders = "7" +shaders = "8" feedback_pass = "0" -shader0 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-pass1.slang" +shader0 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang" filter_linear0 = "false" wrap_mode0 = "clamp_to_border" frame_count_mod0 = "1000" @@ -12,7 +12,7 @@ scale_type_x0 = "source" scale_x0 = "8.000000" scale_type_y0 = "source" scale_y0 = "1.000000" -shader1 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-pass2.slang" +shader1 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang" filter_linear1 = "false" wrap_mode1 = "clamp_to_border" frame_count_mod1 = "1000" @@ -24,7 +24,7 @@ scale_type_x1 = "source" scale_x1 = "1.000000" scale_type_y1 = "source" scale_y1 = "1.000000" -shader2 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-pass3.slang" +shader2 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang" filter_linear2 = "false" wrap_mode2 = "clamp_to_border" frame_count_mod2 = "1000" @@ -36,7 +36,7 @@ scale_type_x2 = "source" scale_x2 = "1.000000" scale_type_y2 = "source" scale_y2 = "1.000000" -shader3 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-pass4.slang" +shader3 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang" filter_linear3 = "false" wrap_mode3 = "clamp_to_border" frame_count_mod3 = "1000" @@ -48,7 +48,7 @@ scale_type_x3 = "source" scale_x3 = "1.000000" scale_type_y3 = "source" scale_y3 = "1.000000" -shader4 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-pass5.slang" +shader4 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang" filter_linear4 = "false" wrap_mode4 = "clamp_to_border" frame_count_mod4 = "1000" @@ -60,7 +60,7 @@ scale_type_x4 = "source" scale_x4 = "1.000000" scale_type_y4 = "source" scale_y4 = "1.000000" -shader5 = "../ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slang" +shader5 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-eotf.slang" filter_linear5 = "false" wrap_mode5 = "clamp_to_border" frame_count_mod5 = "1000" @@ -72,7 +72,7 @@ scale_type_x5 = "source" scale_x5 = "1.000000" scale_type_y5 = "source" scale_y5 = "1.000000" -shader6 = "../ntsc/shaders/patchy-ntsc/linear-to-srgb.slang" +shader6 = "../ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slang" filter_linear6 = "false" wrap_mode6 = "clamp_to_border" frame_count_mod6 = "1000" @@ -84,13 +84,26 @@ scale_type_x6 = "source" scale_x6 = "1.000000" scale_type_y6 = "source" scale_y6 = "1.000000" -pn_knob_contrast = "0.725000" -pn_rgb_smear_enable = "1.000000" -pn_width_uncropped = "256.000000" -pn_height_uncropped = "240.000000" -pn_nes_enable = "1.000000" -pn_nes_real_capture = "1.0" -pn_rgb_smear_rate = "1.15" +shader7 = "../ntsc/shaders/patchy-ntsc/linear-to-srgb.slang" +filter_linear7 = "false" +wrap_mode7 = "clamp_to_border" +frame_count_mod7 = "1000" +mipmap_input7 = "false" +alias7 = "" +float_framebuffer7 = "true" +srgb_framebuffer7 = "false" +scale_type_x7 = "source" +scale_x7 = "1.000000" +scale_type_y7 = "source" +scale_y7 = "1.000000" +pn_width_uncropped = "256.0" +pn_height_uncropped = "240.0" +pn_nes_enable = "1.0" +pn_scanline_dur = "47.7" +pn_noise_rand_offset = "273.000000" +pn_noise_min_amp = "0.300000" +pn_noise_counter = "120.000000" +pn_noise_severity = "0.500000" textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4;PhosphorSamplerLUT5;PhosphorSamplerLUT6" diff --git a/ntsc/patchy-blastem.slangp b/ntsc/patchy-blastem.slangp index f97cdc75..8c72aa96 100644 --- a/ntsc/patchy-blastem.slangp +++ b/ntsc/patchy-blastem.slangp @@ -1,6 +1,6 @@ -shaders = "7" +shaders = "8" feedback_pass = "0" -shader0 = "shaders/patchy-ntsc/patchy-ntsc-pass1.slang" +shader0 = "shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang" filter_linear0 = "false" wrap_mode0 = "clamp_to_border" frame_count_mod0 = "1000" @@ -12,7 +12,7 @@ scale_type_x0 = "source" scale_x0 = "8.000000" scale_type_y0 = "source" scale_y0 = "1.000000" -shader1 = "shaders/patchy-ntsc/patchy-ntsc-pass2.slang" +shader1 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang" filter_linear1 = "false" wrap_mode1 = "clamp_to_border" frame_count_mod1 = "1000" @@ -24,7 +24,7 @@ scale_type_x1 = "source" scale_x1 = "1.000000" scale_type_y1 = "source" scale_y1 = "1.000000" -shader2 = "shaders/patchy-ntsc/patchy-ntsc-pass3.slang" +shader2 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang" filter_linear2 = "false" wrap_mode2 = "clamp_to_border" frame_count_mod2 = "1000" @@ -36,7 +36,7 @@ scale_type_x2 = "source" scale_x2 = "1.000000" scale_type_y2 = "source" scale_y2 = "1.000000" -shader3 = "shaders/patchy-ntsc/patchy-ntsc-pass4.slang" +shader3 = "shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang" filter_linear3 = "false" wrap_mode3 = "clamp_to_border" frame_count_mod3 = "1000" @@ -48,7 +48,7 @@ scale_type_x3 = "source" scale_x3 = "1.000000" scale_type_y3 = "source" scale_y3 = "1.000000" -shader4 = "shaders/patchy-ntsc/patchy-ntsc-pass5.slang" +shader4 = "shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang" filter_linear4 = "false" wrap_mode4 = "clamp_to_border" frame_count_mod4 = "1000" @@ -60,7 +60,7 @@ scale_type_x4 = "source" scale_x4 = "1.000000" scale_type_y4 = "source" scale_y4 = "1.000000" -shader5 = "shaders/patchy-ntsc/trilinearLUT-switchable.slang" +shader5 = "shaders/patchy-ntsc/patchy-ntsc-eotf.slang" filter_linear5 = "false" wrap_mode5 = "clamp_to_border" frame_count_mod5 = "1000" @@ -72,7 +72,7 @@ scale_type_x5 = "source" scale_x5 = "1.000000" scale_type_y5 = "source" scale_y5 = "1.000000" -shader6 = "shaders/patchy-ntsc/linear-to-srgb.slang" +shader6 = "../ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slang" filter_linear6 = "false" wrap_mode6 = "clamp_to_border" frame_count_mod6 = "1000" @@ -84,15 +84,33 @@ scale_type_x6 = "source" scale_x6 = "1.000000" scale_type_y6 = "source" scale_y6 = "1.000000" -pn_rgb_smear_enable = "1.000000" +shader7 = "shaders/patchy-ntsc/linear-to-srgb.slang" +filter_linear7 = "false" +wrap_mode7 = "clamp_to_border" +frame_count_mod7 = "1000" +mipmap_input7 = "false" +alias7 = "" +float_framebuffer7 = "true" +srgb_framebuffer7 = "false" +scale_type_x7 = "source" +scale_x7 = "1.000000" +scale_type_y7 = "source" +scale_y7 = "1.000000" +pn_dual_width_mode = "1.0" pn_genesis_jailbar_enable = "1.000000" pn_genesis_jailbar_offset = "0.312500" pn_scanline_dur = "47.699997" +pn_noise_rand_offset = "273.000000" +pn_noise_min_amp = "0.300000" +pn_noise_counter = "120.000000" +pn_noise_severity = "0.500000" pn_color_init_offset = "0.700000" -pn_modulator_luma_filter_type = "2.000000" +pn_modulator_luma_filter_type = "0.000000" +pn_modulator_luma_filter_width = "3.0" +pn_modulator_luma_filter_level "2.7" pn_modulator_luma_res = "220.000000" -pn_rgb_smear_rate = "1.15" - +pn_modulator_luma_radius = "2.0" +pn_modulator_luma_sigma = "0.57" textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4;PhosphorSamplerLUT5;PhosphorSamplerLUT6" PhosphorSamplerLUT1 = "../ntsc/shaders/patchy-ntsc/P22_80s_D65.png" diff --git a/ntsc/patchy-genplusgx.slangp b/ntsc/patchy-genplusgx.slangp index ddac3f59..4fd56098 100644 --- a/ntsc/patchy-genplusgx.slangp +++ b/ntsc/patchy-genplusgx.slangp @@ -1,6 +1,6 @@ -shaders = "7" +shaders = "8" feedback_pass = "0" -shader0 = "shaders/patchy-ntsc/patchy-ntsc-pass1.slang" +shader0 = "shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang" filter_linear0 = "false" wrap_mode0 = "clamp_to_border" frame_count_mod0 = "1000" @@ -12,7 +12,7 @@ scale_type_x0 = "source" scale_x0 = "8.000000" scale_type_y0 = "source" scale_y0 = "1.000000" -shader1 = "shaders/patchy-ntsc/patchy-ntsc-pass2.slang" +shader1 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang" filter_linear1 = "false" wrap_mode1 = "clamp_to_border" frame_count_mod1 = "1000" @@ -24,7 +24,7 @@ scale_type_x1 = "source" scale_x1 = "1.000000" scale_type_y1 = "source" scale_y1 = "1.000000" -shader2 = "shaders/patchy-ntsc/patchy-ntsc-pass3.slang" +shader2 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang" filter_linear2 = "false" wrap_mode2 = "clamp_to_border" frame_count_mod2 = "1000" @@ -36,7 +36,7 @@ scale_type_x2 = "source" scale_x2 = "1.000000" scale_type_y2 = "source" scale_y2 = "1.000000" -shader3 = "shaders/patchy-ntsc/patchy-ntsc-pass4.slang" +shader3 = "shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang" filter_linear3 = "false" wrap_mode3 = "clamp_to_border" frame_count_mod3 = "1000" @@ -48,7 +48,7 @@ scale_type_x3 = "source" scale_x3 = "1.000000" scale_type_y3 = "source" scale_y3 = "1.000000" -shader4 = "shaders/patchy-ntsc/patchy-ntsc-pass5.slang" +shader4 = "shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang" filter_linear4 = "false" wrap_mode4 = "clamp_to_border" frame_count_mod4 = "1000" @@ -60,7 +60,7 @@ scale_type_x4 = "source" scale_x4 = "1.000000" scale_type_y4 = "source" scale_y4 = "1.000000" -shader5 = "shaders/patchy-ntsc/trilinearLUT-switchable.slang" +shader5 = "shaders/patchy-ntsc/patchy-ntsc-eotf.slang" filter_linear5 = "false" wrap_mode5 = "clamp_to_border" frame_count_mod5 = "1000" @@ -72,7 +72,7 @@ scale_type_x5 = "source" scale_x5 = "1.000000" scale_type_y5 = "source" scale_y5 = "1.000000" -shader6 = "shaders/patchy-ntsc/linear-to-srgb.slang" +shader6 = "../ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slang" filter_linear6 = "false" wrap_mode6 = "clamp_to_border" frame_count_mod6 = "1000" @@ -84,16 +84,34 @@ scale_type_x6 = "source" scale_x6 = "1.000000" scale_type_y6 = "source" scale_y6 = "1.000000" -pn_rgb_smear_enable = "1.000000" -pn_genesis_palette = "1.0" +shader7 = "shaders/patchy-ntsc/linear-to-srgb.slang" +filter_linear7 = "false" +wrap_mode7 = "clamp_to_border" +frame_count_mod7 = "1000" +mipmap_input7 = "false" +alias7 = "" +float_framebuffer7 = "true" +srgb_framebuffer7 = "false" +scale_type_x7 = "source" +scale_x7 = "1.000000" +scale_type_y7 = "source" +scale_y7 = "1.000000" +pn_dual_width_mode = "1.0" pn_genesis_jailbar_enable = "1.000000" pn_genesis_jailbar_offset = "0.312500" pn_scanline_dur = "47.699997" +pn_noise_rand_offset = "273.000000" +pn_noise_min_amp = "0.300000" +pn_noise_counter = "120.000000" +pn_noise_severity = "0.500000" pn_color_init_offset = "0.700000" -pn_modulator_luma_filter_type = "2.000000" +pn_modulator_luma_filter_type = "0.000000" +pn_modulator_luma_filter_width = "3.0" +pn_modulator_luma_filter_level "2.7" pn_modulator_luma_res = "220.000000" -pn_rgb_smear_rate = "1.15" - +pn_modulator_luma_radius = "2.0" +pn_modulator_luma_sigma = "0.57" +pn_genesis_palette = "1.0" textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4;PhosphorSamplerLUT5;PhosphorSamplerLUT6" PhosphorSamplerLUT1 = "../ntsc/shaders/patchy-ntsc/P22_80s_D65.png" diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc.slangp b/ntsc/patchy-snes.slangp similarity index 57% rename from ntsc/shaders/patchy-ntsc/patchy-ntsc.slangp rename to ntsc/patchy-snes.slangp index 762ab754..16593d51 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc.slangp +++ b/ntsc/patchy-snes.slangp @@ -1,5 +1,6 @@ -shaders = "7" -shader0 = "patchy-ntsc-pass1.slang" +shaders = "8" +feedback_pass = "0" +shader0 = "shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang" filter_linear0 = "false" wrap_mode0 = "clamp_to_border" frame_count_mod0 = "1000" @@ -8,10 +9,10 @@ alias0 = "" float_framebuffer0 = "true" srgb_framebuffer0 = "false" scale_type_x0 = "source" -scale_x0 = "8.000000" +scale_x0 = "4.000000" scale_type_y0 = "source" scale_y0 = "1.000000" -shader1 = "patchy-ntsc-pass2.slang" +shader1 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang" filter_linear1 = "false" wrap_mode1 = "clamp_to_border" frame_count_mod1 = "1000" @@ -23,7 +24,7 @@ scale_type_x1 = "source" scale_x1 = "1.000000" scale_type_y1 = "source" scale_y1 = "1.000000" -shader2 = "patchy-ntsc-pass3.slang" +shader2 = "../ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang" filter_linear2 = "false" wrap_mode2 = "clamp_to_border" frame_count_mod2 = "1000" @@ -35,7 +36,7 @@ scale_type_x2 = "source" scale_x2 = "1.000000" scale_type_y2 = "source" scale_y2 = "1.000000" -shader3 = "patchy-ntsc-pass4.slang" +shader3 = "shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang" filter_linear3 = "false" wrap_mode3 = "clamp_to_border" frame_count_mod3 = "1000" @@ -47,7 +48,7 @@ scale_type_x3 = "source" scale_x3 = "1.000000" scale_type_y3 = "source" scale_y3 = "1.000000" -shader4 = "patchy-ntsc-pass5.slang" +shader4 = "shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang" filter_linear4 = "false" wrap_mode4 = "clamp_to_border" frame_count_mod4 = "1000" @@ -59,7 +60,7 @@ scale_type_x4 = "source" scale_x4 = "1.000000" scale_type_y4 = "source" scale_y4 = "1.000000" -shader5 = "../lut/trilinearLUT-switchable.slang" +shader5 = "shaders/patchy-ntsc/patchy-ntsc-eotf.slang" filter_linear5 = "false" wrap_mode5 = "clamp_to_border" frame_count_mod5 = "1000" @@ -71,7 +72,7 @@ scale_type_x5 = "source" scale_x5 = "1.000000" scale_type_y5 = "source" scale_y5 = "1.000000" -shader6 = "linear-to-srgb.slang" +shader6 = "../ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slang" filter_linear6 = "false" wrap_mode6 = "clamp_to_border" frame_count_mod6 = "1000" @@ -83,25 +84,55 @@ scale_type_x6 = "source" scale_x6 = "1.000000" scale_type_y6 = "source" scale_y6 = "1.000000" -textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4;PhosphorSamplerLUT5" -PhosphorSamplerLUT1 = "../lut/SMPTEC_TrinitronP22_LinearRBG_to_sRGB_LinearRGB.png" +shader7 = "shaders/patchy-ntsc/linear-to-srgb.slang" +filter_linear7 = "false" +wrap_mode7 = "clamp_to_border" +frame_count_mod7 = "1000" +mipmap_input7 = "false" +alias7 = "" +float_framebuffer7 = "true" +srgb_framebuffer7 = "false" +scale_type_x7 = "source" +scale_x7 = "1.000000" +scale_type_y7 = "source" +scale_y7 = "1.000000" +pn_signal_res = "4.0" +pn_width_uncropped = "256.000000" +pn_height_uncropped = "240.000000" +pn_rgb_blur_enable = "1.0" +pn_scanline_dur = "47.7" +pn_color_init_offset = "0.000000" +pn_noise_rand_offset = "273.000000" +pn_noise_min_amp = "0.300000" +pn_noise_counter = "120.000000" +pn_noise_severity = "0.500000" +pn_color_line_offset = "0.333333" +pn_color_screen_offset = "0.333333" +pn_color_screen_offset_modulo = "2.000000" +pn_modulator_luma_filter_type = "-1.0" +pn_modulator_chroma_filter_type = "-1.000000" +textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4;PhosphorSamplerLUT5;PhosphorSamplerLUT6" +PhosphorSamplerLUT1 = "shaders/patchy-ntsc/P22_80s_D65.png" PhosphorSamplerLUT1_linear = "false" PhosphorSamplerLUT1_wrap_mode = "clamp_to_border" PhosphorSamplerLUT1_mipmap = "false" -PhosphorSamplerLUT2 = "../lut/NTSCU_TrinitronP22_LinearRBG_to_sRGB_LinearRGB.png" +PhosphorSamplerLUT2 = "shaders/patchy-ntsc/P22_90s_D65.png" PhosphorSamplerLUT2_linear = "false" PhosphorSamplerLUT2_wrap_mode = "clamp_to_border" PhosphorSamplerLUT2_mipmap = "false" -PhosphorSamplerLUT3 = "../lut/NTSCJ_TrinitronP22_LinearRBG_to_sRGB_LinearRGB.png" +PhosphorSamplerLUT3 = "shaders/patchy-ntsc/P22_J_D65.png" PhosphorSamplerLUT3_linear = "false" PhosphorSamplerLUT3_wrap_mode = "clamp_to_border" PhosphorSamplerLUT3_mipmap = "false" -PhosphorSamplerLUT4 = "../lut/SMPTEC_P2280s_LinearRBG_to_sRGB_LinearRGB.png" +PhosphorSamplerLUT4 = "shaders/patchy-ntsc/TrinitronP22_D65.png" +PhosphorSamplerLUT4_linear = "false" PhosphorSamplerLUT4_wrap_mode = "clamp_to_border" PhosphorSamplerLUT4_mipmap = "false" -PhosphorSamplerLUT4_linear = "false" -PhosphorSamplerLUT5 = "../lut/SMPTEC_P2290s_LinearRBG_to_sRGB_LinearRGB.png" +PhosphorSamplerLUT5 = "shaders/patchy-ntsc/P22_J_D93.png" +PhosphorSamplerLUT5_linear = "false" PhosphorSamplerLUT5_wrap_mode = "clamp_to_border" PhosphorSamplerLUT5_mipmap = "false" -PhosphorSamplerLUT5_linear = "false" - +PhosphorSamplerLUT6 = "shaders/patchy-ntsc/TrinitronP22_D93.png" +PhosphorSamplerLUT6_linear = "false" +PhosphorSamplerLUT6_wrap_mode = "clamp_to_border" +PhosphorSamplerLUT6_mipmap = "false" diff --git a/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass1.slang b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass1.slang new file mode 100644 index 00000000..903097e7 --- /dev/null +++ b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass1.slang @@ -0,0 +1,37 @@ +#version 450 + +#pragma name AfterglowUpdPre + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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() +{ + FragColor = vec4(clamp(texture(Source, vTexCoord).rgb, 0, 1), 1.0); +} diff --git a/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass2.slang b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass2.slang new file mode 100644 index 00000000..351546f9 --- /dev/null +++ b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass2.slang @@ -0,0 +1,92 @@ +#version 450 + +/* + Phosphor Afterglow Shader pass 0 + + Copyright (C) 2020 - 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 + 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 PR_upd, PG_upd, PB_upd; +} params; + +#pragma parameter PR_upd "Afterglow Persistence Red" 0.32 0.0 0.50 0.01 +#pragma parameter PG_upd "Afterglow Persistence Green" 0.32 0.0 0.50 0.01 +#pragma parameter PB_upd "Afterglow Persistence Blue" 0.32 0.0 0.50 0.01 + +#define PR params.PR_upd +#define PG params.PG_upd +#define PB params.PB_upd + +#define COMPAT_TEXTURE(c,d) texture(c,d) + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; +} global; + +#pragma name AfterglowUpd + +#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 AfterglowUpdFeedback; + +#define TEX0 vTexCoord + + +void main() +{ + vec2 dx = vec2(params.OriginalSize.z, 0.0); + vec2 dy = vec2(0.0, params.OriginalSize.w); + + vec3 color0 = COMPAT_TEXTURE(Source, TEX0.xy).rgb; + vec3 color1 = COMPAT_TEXTURE(Source, TEX0.xy - dx).rgb; + vec3 color2 = COMPAT_TEXTURE(Source, TEX0.xy + dx).rgb; + vec3 color3 = COMPAT_TEXTURE(Source, TEX0.xy - dy).rgb; + vec3 color4 = COMPAT_TEXTURE(Source, TEX0.xy + dy).rgb; + +// vec3 color = (2.5 * color0 + color1 + color2 + color3 + color4)/6.5; + vec3 color = color0; + + vec3 accumulate = COMPAT_TEXTURE(AfterglowUpdFeedback, TEX0.xy).rgb; + + float w = 1.0; + if ((color0.r + color0.g + color0.b < 5.0/255.0)) { w = 0.0; } + + vec3 result = mix( max(mix(color, accumulate, 0.49 + vec3(PR, PG, PB))- 1.25/255.0, 0.0), color, w); + + FragColor = vec4(result, w); +} diff --git a/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass3.slang b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass3.slang new file mode 100644 index 00000000..a8a25713 --- /dev/null +++ b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update-pass3.slang @@ -0,0 +1,374 @@ +#version 450 + +/* + CRT Advanced Afterglow, color altering + + 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 + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + float TNTC; + float LS; + float LUTLOW, LUTBR; + float CP, CS; + float WP; + float wp_saturation; + float AS_upd, sat_upd; + float BP; + float vigstr; + float vigdef; + float sega_fix; + float pre_bb; + float contr; +} params; + +#pragma parameter AS_upd "Afterglow Strength" 0.20 0.0 0.60 0.01 +#define AS params.AS_upd + +#pragma parameter sat_upd "Afterglow saturation" 0.50 0.0 1.0 0.01 +#define sat params.sat_upd + +//#pragma parameter bogus_color "[ COLOR TWEAKS ]:" 0.0 0.0 1.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 16.0 64.0 16.0 +//#define LS params.LS +// +//#define LUTLOW 5.0 // "Fix LUT Dark - Range" from 0.0 to 50.0 - RGB singletons +// +//#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 -2.0 2.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 +{ + 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 AfterglowUpdPre; +layout(set = 0, binding = 3) uniform sampler2D AfterglowUpd; +//layout(set = 0, binding = 4) uniform sampler2D SamplerLUT1; +//layout(set = 0, binding = 5) uniform sampler2D SamplerLUT2; +//layout(set = 0, binding = 6) uniform sampler2D SamplerLUT3; +//layout(set = 0, binding = 7) uniform sampler2D SamplerLUT4; + +#define COMPAT_TEXTURE(c,d) texture(c,d) + + +// Color profile matrices + +//const mat3 Profile0 = +//mat3( +// 0.412391, 0.212639, 0.019331, +// 0.357584, 0.715169, 0.119195, +// 0.180481, 0.072192, 0.950532 +//); +// +//const mat3 Profile1 = +//mat3( +// 0.430554, 0.222004, 0.020182, +// 0.341550, 0.706655, 0.129553, +// 0.178352, 0.071341, 0.939322 +//); +// +//const mat3 Profile2 = +//mat3( +// 0.396686, 0.210299, 0.006131, +// 0.372504, 0.713766, 0.115356, +// 0.181266, 0.075936, 0.967571 +//); +// +//const mat3 Profile3 = +//mat3( +// 0.393521, 0.212376, 0.018739, +// 0.365258, 0.701060, 0.111934, +// 0.191677, 0.086564, 0.958385 +//); +// +//const mat3 Profile4 = +//mat3( +// 0.392258, 0.209410, 0.016061, +// 0.351135, 0.725680, 0.093636, +// 0.166603, 0.064910, 0.850324 +//); +// +//const mat3 Profile5 = +//mat3( +// 0.377923, 0.195679, 0.010514, +// 0.317366, 0.722319, 0.097826, +// 0.207738, 0.082002, 1.076960 +//); +// +//const mat3 ToSRGB = +//mat3( +// 3.240970, -0.969244, 0.055630, +//-1.537383, 1.875968, -0.203977, +//-0.498611, 0.041555, 1.056972 +//); +// +//const mat3 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.493497, -0.829489, 0.035846, +//-0.931384, 1.762664, -0.076172, +//-0.402711, 0.023625, 0.956885 +//); +// +//const mat3 ToAdobe = +//mat3( +// 2.041588, -0.969244, 0.013444, +//-0.565007, 1.875968, -0.11836, +//-0.344731, 0.041555, 1.015175 +//); +// +//const mat3 ToREC = +//mat3( +// 1.716651, -0.666684, 0.017640, +//-0.355671, 1.616481, -0.042771, +//-0.253366, 0.015769, 0.942103 +//); + +// Color temperature matrices + +//const mat3 D65_to_D55 = mat3 ( +// 0.4850339153, 0.2500956126, 0.0227359648, +// 0.3488957224, 0.6977914447, 0.1162985741, +// 0.1302823568, 0.0521129427, 0.6861537456); +// +// +//const mat3 D65_to_D93 = mat3 ( +// 0.3412754080, 0.1759701322, 0.0159972847, +// 0.3646170520, 0.7292341040, 0.1215390173, +// 0.2369894093, 0.0947957637, 1.2481442225); +// +// +//vec3 fix_lut(vec3 lutcolor, vec3 ref) +//{ +// float r = length(ref); +// float l = length(lutcolor); +// float m = max(max(ref.r,ref.g),ref.b); +// ref = normalize(lutcolor + 0.0000001) * mix(r, l, pow(m,1.25)); +// return mix(lutcolor, ref, LUTBR); +//} +// +// +//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) +//{ +// return max(mix(x, smoothstep(0.0, 1.0, x), params.contr),0.0); +//} + + +void main() +{ + vec4 imgColor = COMPAT_TEXTURE(AfterglowUpdPre, vTexCoord.xy); + vec4 aftglow = COMPAT_TEXTURE(AfterglowUpd, vTexCoord.xy); + + float w = 1.0-aftglow.w; + + float l = length(aftglow.rgb); + aftglow.rgb = AS*w*normalize(pow(aftglow.rgb + 0.01, vec3(sat)))*l; + + FragColor = vec4(imgColor.rgb + aftglow.rgb, 1.0); + +// 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; +// } +// else +// { +// float lutlow = LUTLOW/255.0; float invLS = 1.0/LS; +// vec3 lut_ref = imgColor.rgb + lutlow*(1.0 - pow(imgColor.rgb, 0.333.xxx)); +// float lutb = lut_ref.b * (1.0-0.5*invLS); +// lut_ref.rg = lut_ref.rg * (1.0-invLS) + 0.5*invLS; +// float tile1 = ceil (lutb * (LS-1.0)); +// float tile0 = max(tile1 - 1.0, 0.0); +// float f = fract(lutb * (LS-1.0)); if (f == 0.0) f = 1.0; +// vec2 coord0 = vec2(tile0 + lut_ref.r, lut_ref.g)*vec2(invLS, 1.0); +// vec2 coord1 = vec2(tile1 + lut_ref.r, lut_ref.g)*vec2(invLS, 1.0); +// vec4 color1, color2, res; +// +// if (int(TNTC) == 1) +// { +// color1 = COMPAT_TEXTURE(SamplerLUT1, coord0); +// color2 = COMPAT_TEXTURE(SamplerLUT1, coord1); +// res = mix(color1, color2, f); +// } +// else if (int(TNTC) == 2) +// { +// color1 = COMPAT_TEXTURE(SamplerLUT2, coord0); +// color2 = COMPAT_TEXTURE(SamplerLUT2, coord1); +// res = mix(color1, color2, f); +// } +// else if (int(TNTC) == 3) +// { +// color1 = COMPAT_TEXTURE(SamplerLUT3, coord0); +// color2 = COMPAT_TEXTURE(SamplerLUT3, coord1); +// res = mix(color1, color2, f); +// } +// else if (int(TNTC) == 4) +// { +// color1 = COMPAT_TEXTURE(SamplerLUT4, coord0); +// color2 = COMPAT_TEXTURE(SamplerLUT4, coord1); +// res = mix(color1, color2, f); +// } +// +// res.rgb = fix_lut (res.rgb, imgColor.rgb); +// +// color = mix(imgColor.rgb, res.rgb, min(TNTC,1.0)); +// } +// +// vec3 c = clamp(color, 0.0, 1.0); +// +// float p; +// mat3 m_out; +// +// if (CS == 0.0) { p = 2.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)); +// +// mat3 m_in = Profile0; +// +// if (CP == 0.0) { m_in = Profile0; } else +// if (CP == 1.0) { m_in = Profile1; } else +// if (CP == 2.0) { m_in = Profile2; } else +// if (CP == 3.0) { m_in = Profile3; } else +// if (CP == 4.0) { m_in = Profile4; } else +// if (CP == 5.0) { m_in = Profile5; } +// +// color = m_in*color; +// color = m_out*color; +// +// color = clamp(color, 0.0, 1.0); +// +// color = pow(color, vec3(1.0/p)); +// +// if (CP == -1.0) color = c; +// +// vec3 scolor1 = 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 = clamp(color, 0.0, 1.0); +// color = pow(color, vec3(p)); +// +// vec3 warmer = D65_to_D55*color; +// warmer = ToSRGB*warmer; +// +// vec3 cooler = D65_to_D93*color; +// cooler = ToSRGB*cooler; +// +// float m = abs(WP)/100.0; +// +// vec3 comp = (WP < 0.0) ? cooler : warmer; +// +// color = mix(color, comp, m); +// color = pow(max(color, 0.0), vec3(1.0/p)); +// +// 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 = min(color * params.pre_bb, 1.0); +// +// FragColor = vec4(color, vignette(vTexCoord.xy)); +} diff --git a/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update.slangp b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update.slangp new file mode 100644 index 00000000..b4ee6fa1 --- /dev/null +++ b/ntsc/shaders/patchy-ntsc/afterglow0-update/afterglow0-update.slangp @@ -0,0 +1,34 @@ +shaders = "3" +shader0 = "afterglow0-update-pass1.slang" +filter_linear0 = "false" +wrap_mode0 = "clamp_to_border" +mipmap_input0 = "false" +alias0 = "" +float_framebuffer0 = "false" +srgb_framebuffer0 = "false" +scale_type_x0 = "source" +scale_x0 = "1.000000" +scale_type_y0 = "source" +scale_y0 = "1.000000" +shader1 = "afterglow0-update-pass2.slang" +filter_linear1 = "false" +wrap_mode1 = "clamp_to_border" +mipmap_input1 = "false" +alias1 = "" +float_framebuffer1 = "false" +srgb_framebuffer1 = "false" +scale_type_x1 = "source" +scale_x1 = "1.000000" +scale_type_y1 = "source" +scale_y1 = "1.000000" +shader2 = "afterglow0-update-pass3.slang" +filter_linear2 = "false" +wrap_mode2 = "clamp_to_border" +mipmap_input2 = "false" +alias2 = "" +float_framebuffer2 = "false" +srgb_framebuffer2 = "false" +scale_type_x2 = "source" +scale_x2 = "1.000000" +scale_type_y2 = "source" +scale_y2 = "1.000000" diff --git a/ntsc/shaders/patchy-ntsc/generationcommands.txt b/ntsc/shaders/patchy-ntsc/generationcommands.txt index ea47c17e..c91d63a0 100644 --- a/ntsc/shaders/patchy-ntsc/generationcommands.txt +++ b/ntsc/shaders/patchy-ntsc/generationcommands.txt @@ -1,2 +1,2 @@ -./gamutthingy -i 64.png -o P22.png -g linear -s ntscjp22trinitron -d srgb --map-mode compress --gma vpr --safe-zone-type const-detail --remap-factor 0.4 --remap-limit 0.9 --knee soft --knee-factor 0.4 --di false --sc false +./gamutthingy -i 64.png -o P22.png -g linear -s ntscjp22trinitron -d srgb --map-mode compress --gma vpr --safe-zone-type const-detail --remap-factor 0.4 --remap-limit 0.9 --knee soft --knee-factor 0.4 --di false --sc true diff --git a/ntsc/shaders/patchy-ntsc/linear-to-srgb.slang b/ntsc/shaders/patchy-ntsc/linear-to-srgb.slang index 702833db..a99b2ae3 100644 --- a/ntsc/shaders/patchy-ntsc/linear-to-srgb.slang +++ b/ntsc/shaders/patchy-ntsc/linear-to-srgb.slang @@ -29,7 +29,36 @@ layout(std140, set = 0, binding = 0) uniform UBO mat4 MVP; } global; -#include "../../../../shaders_slang/include/colorspace-tools.h" + +// These two sRGB functions are taken from shaders_slang/include/colorspace-tools.h +// Colorspace Tools +// ported from Asmodean's PsxFX Shader Suite v2.00 +// License: GPL v2+ +vec3 linear_to_sRGB(vec3 color, float gamma) +{ + color = clamp(color, 0.0, 1.0); + color.r = (color.r <= 0.00313066844250063) ? + color.r * 12.92 : 1.055 * pow(color.r, 1.0 / gamma) - 0.055; + color.g = (color.g <= 0.00313066844250063) ? + color.g * 12.92 : 1.055 * pow(color.g, 1.0 / gamma) - 0.055; + color.b = (color.b <= 0.00313066844250063) ? + color.b * 12.92 : 1.055 * pow(color.b, 1.0 / gamma) - 0.055; + + return color.rgb; +} + +vec3 sRGB_to_linear(vec3 color, float gamma) +{ + color = clamp(color, 0.0, 1.0); + color.r = (color.r <= 0.04045) ? + color.r / 12.92 : pow((color.r + 0.055) / (1.055), gamma); + color.g = (color.g <= 0.04045) ? + color.g / 12.92 : pow((color.g + 0.055) / (1.055), gamma); + color.b = (color.b <= 0.04045) ? + color.b / 12.92 : pow((color.b + 0.055) / (1.055), gamma); + + return color.rgb; +} #pragma stage vertex layout(location = 0) in vec4 Position; diff --git a/ntsc/shaders/patchy-ntsc/patchy-color.slang b/ntsc/shaders/patchy-ntsc/patchy-color.slang index e0cdb4c8..6a02e9cc 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-color.slang +++ b/ntsc/shaders/patchy-ntsc/patchy-color.slang @@ -1072,8 +1072,35 @@ vec3 testPattern(vec2 pos) { } } -// For sRGB_to_linear -#include "../../../include/colorspace-tools.h" +// These two sRGB functions are taken from shaders_slang/include/colorspace-tools.h +// Colorspace Tools +// ported from Asmodean's PsxFX Shader Suite v2.00 +// License: GPL v2+ +vec3 linear_to_sRGB(vec3 color, float gamma) +{ + color = clamp(color, 0.0, 1.0); + color.r = (color.r <= 0.00313066844250063) ? + color.r * 12.92 : 1.055 * pow(color.r, 1.0 / gamma) - 0.055; + color.g = (color.g <= 0.00313066844250063) ? + color.g * 12.92 : 1.055 * pow(color.g, 1.0 / gamma) - 0.055; + color.b = (color.b <= 0.00313066844250063) ? + color.b * 12.92 : 1.055 * pow(color.b, 1.0 / gamma) - 0.055; + + return color.rgb; +} + +vec3 sRGB_to_linear(vec3 color, float gamma) +{ + color = clamp(color, 0.0, 1.0); + color.r = (color.r <= 0.04045) ? + color.r / 12.92 : pow((color.r + 0.055) / (1.055), gamma); + color.g = (color.g <= 0.04045) ? + color.g / 12.92 : pow((color.g + 0.055) / (1.055), gamma); + color.b = (color.b <= 0.04045) ? + color.b / 12.92 : pow((color.b + 0.055) / (1.055), gamma); + + return color.rgb; +} #pragma stage vertex layout(location = 0) in vec4 Position; diff --git a/ntsc/shaders/patchy-ntsc/patchy-color.slangp b/ntsc/shaders/patchy-ntsc/patchy-color.slangp deleted file mode 100644 index bc18caa1..00000000 --- a/ntsc/shaders/patchy-ntsc/patchy-color.slangp +++ /dev/null @@ -1,61 +0,0 @@ -shaders = "3" -feedback_pass = "0" -shader0 = "patchy-color.slang" -filter_linear0 = "false" -wrap_mode0 = "clamp_to_border" -mipmap_input0 = "false" -alias0 = "" -float_framebuffer0 = "false" -srgb_framebuffer0 = "false" -scale_type_x0 = "source" -scale_x0 = "1.000000" -scale_type_y0 = "source" -scale_y0 = "1.000000" -shader1 = "../lut/trilinearLUT-switchable.slang" -filter_linear1 = "false" -wrap_mode1 = "clamp_to_border" -mipmap_input1 = "false" -alias1 = "" -float_framebuffer1 = "true" -srgb_framebuffer1 = "false" -scale_type_x1 = "source" -scale_x1 = "1.000000" -scale_type_y1 = "source" -scale_y1 = "1.000000" -shader2 = "linear-to-srgb.slang" -filter_linear2 = "false" -wrap_mode2 = "clamp_to_border" -mipmap_input2 = "false" -alias2 = "" -float_framebuffer2 = "false" -srgb_framebuffer2 = "false" -scale_type_x2 = "source" -scale_x2 = "1.000000" -scale_type_y2 = "source" -scale_y2 = "1.000000" -pc_console = "1.000000" -pc_genesis_needfix = "1.000000" -pc_demodulator = "8.000000" -lut_chroma_adapt = "0.000000" -textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4;PhosphorSamplerLUT5" -PhosphorSamplerLUT1 = "../lut/SMPTEC_TrinitronP22_LinearRBG_to_sRGB_LinearRGB.png" -PhosphorSamplerLUT1_linear = "false" -PhosphorSamplerLUT1_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT1_mipmap = "false" -PhosphorSamplerLUT2 = "../lut/NTSCU_TrinitronP22_LinearRBG_to_sRGB_LinearRGB.png" -PhosphorSamplerLUT2_linear = "false" -PhosphorSamplerLUT2_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT2_mipmap = "false" -PhosphorSamplerLUT3 = "../lut/NTSCJ_TrinitronP22_LinearRBG_to_sRGB_LinearRGB.png" -PhosphorSamplerLUT3_linear = "false" -PhosphorSamplerLUT3_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT3_mipmap = "false" -PhosphorSamplerLUT4 = "../lut/SMPTEC_P2280s_LinearRBG_to_sRGB_LinearRGB.png" -PhosphorSamplerLUT4_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT4_mipmap = "false" -PhosphorSamplerLUT4_linear = "false" -PhosphorSamplerLUT5 = "../lut/SMPTEC_P2290s_LinearRBG_to_sRGB_LinearRGB.png" -PhosphorSamplerLUT5_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT5_mipmap = "false" -PhosphorSamplerLUT5_linear = "false" - diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass2.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang similarity index 68% rename from ntsc/shaders/patchy-ntsc/patchy-ntsc-pass2.slang rename to ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang index 07e3de65..49c04c93 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass2.slang +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-combine-y-c.slang @@ -47,28 +47,28 @@ layout(set = 0, binding = 2) uniform sampler2D Source; void main() { - if(global.pn_nes_enable > 0.5) { - FragColor = texture(Source, vTexCoord); + vec4 orig = texture(Source, vTexCoord); + if(global.pn_nes_enable > 0.5 || global.pn_connection_type > 1.5) { + // NES or No Artifacts + + FragColor = orig; } else { - float composite = 0.0; + // Composite or S-Video + + float luma = 0.0; + float chroma = 0.0; // Filter luma before combining with chroma. - composite += lowpassPickable(global.pn_modulator_luma_filter_type, + // Some S-Video mods might still have lowpassed luma. + luma = lowpassPickable(global.pn_modulator_luma_filter_type, global.pn_modulator_luma_filter_width, global.pn_modulator_luma_filter_level, global.pn_modulator_luma_sigma, global.pn_modulator_luma_radius, - global.pn_modulator_luma_res, - global.pn_modulator_luma_eq_f_lo, global.pn_modulator_luma_eq_f_hi, - global.pn_modulator_luma_eq_g_lo, global.pn_modulator_luma_eq_g_mid, global.pn_modulator_luma_eq_g_hi, - global.pn_modulator_luma_eq_dist, global.pn_modulator_luma_eq_off).r; + global.pn_modulator_luma_res).r; - composite += bandpassPickable(global.pn_modulator_chroma_filter_type, - global.pn_modulator_chroma_filter_width, global.pn_modulator_chroma_filter_level, global.pn_modulator_chroma_filter_level_diff, - global.pn_modulator_chroma_eq_f_lo, global.pn_modulator_chroma_eq_f_hi, - global.pn_modulator_chroma_eq_g_lo, global.pn_modulator_chroma_eq_g_mid, global.pn_modulator_chroma_eq_g_hi, - global.pn_modulator_chroma_eq_dist, global.pn_modulator_chroma_eq_off, 1 // 1 means chroma is in the G channel of the vector - ).g; + chroma = bandpassPickable(global.pn_modulator_chroma_filter_type, + global.pn_modulator_chroma_filter_width, global.pn_modulator_chroma_filter_level, global.pn_modulator_chroma_filter_level_diff).g; - if(global.pn_genesis_jailbar_enable > 0.5) { + if(global.pn_connection_type < 0.5 && global.pn_genesis_jailbar_enable > 0.5) { // Compensate for cropped overscan, assuming equal crop on both sides. // Current position in pixels, both x and y being decimal numbers. vec2 pixelCoord = realPixelCoord(); @@ -77,10 +77,15 @@ void main() // I don't even know what actually is causing the jailbars on the Model 1 Genesis, but this looks about right. // From what I've read, these jailbars are largely missing on the Genesis model 1 VA6 revision onward, and it can be fixed on earlier model 1s by adjusting the capacitors. // 0.0075 matched by eye, not precise. - composite += 0.0075 * global.pn_genesis_jailbar_amplitude * -cos(pi * pixelCoord.x + 2 * pi * global.pn_genesis_jailbar_offset); + chroma += 0.0075 * global.pn_genesis_jailbar_amplitude * -cos(pi * pixelCoord.x + 2 * pi * global.pn_genesis_jailbar_offset); } - FragColor = vec4(composite, 0, 0, 1); + if(global.pn_connection_type < 0.5) { // Composite + + FragColor = vec4(luma + chroma, 0, 0, 1); + } else { // S-Video + FragColor = vec4(chroma, luma, 0, 1); + } } } diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass4.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang similarity index 87% rename from ntsc/shaders/patchy-ntsc/patchy-ntsc-pass4.slang rename to ntsc/shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang index f53598cf..00a511eb 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass4.slang +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-decode-y-rmy-bmy.slang @@ -48,11 +48,18 @@ layout(set = 0, binding = 2) uniform sampler2D Source; void main() { float y = texture(Source, vTexCoord).r; + vec2 rmybmy; - // A slight lowpass on B-Y and R-Y after demodulating - vec2 rmybmy = lowpassGTU(240).gb; + if(global.pn_connection_type < 1.5) { // Composite and S-Video + // A slight lowpass on B-Y and R-Y after demodulating + rmybmy = uvFilterPickable(global.pn_demodulator_uv_filter_type); + } else { + // No Artifacts + rmybmy = texture(Source, vTexCoord).gb; + } vec3 rgb; + if(global.pn_nes_enable > 0.5 && global.pn_nes_real_capture > 0.5) { rgb = nesRealCaptureLookup(vec3(y, rmybmy), global.pn_knob_saturation); } else { diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass1.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang similarity index 90% rename from ntsc/shaders/patchy-ntsc/patchy-ntsc-pass1.slang rename to ntsc/shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang index 91645295..87d844e9 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass1.slang +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-encode-y-c.slang @@ -116,7 +116,7 @@ vec3 smpteBars(vec2 pos) { float wholeRamp = pos.x * 8; float flooredWholeRamp = floor(pos.x * 8); if(pos.y < 0.75) { - // Standard color bars + // Standard color bars. Top is 75%; bottom is 100%. int index = 7 - int(flooredWholeRamp + 0.5); return vec3((index & 2) >> 1, index >> 2, index & 1); } else if(pos.x > 0.875) { @@ -221,16 +221,27 @@ vec3 testPattern(vec2 pos) { void main() { - vec4 colorIn = texture(Source, vTexCoord); + vec4 colorIn; if(global.pn_test_pattern > 0.5) { colorIn.rgb = testPattern(vTexCoord); + } else if(global.pn_nes_enable < 0.5 && global.pn_rgb_blur_enable > 0.5) { + int xscale = int(getSizes().x / global.pn_width_uncropped + 0.5); + vec3 sum = vec3(0); + int size = int(xscale * SIGNAL_RES * global.pn_rgb_blur_amount + 0.5); + int start = (-size) / 2; // rounds up towards 0 + int end = start + size; + for(int i = start; i < end; i++) { + sum += texture(Source, vTexCoord - vec2(i * params.OutputSize.z, 0)).rgb; + } + colorIn.rgb = sum / size; + } else { + colorIn = texture(Source, vTexCoord); } // Compensate for cropped overscan, assuming equal crop on both sides. We don't know whether the user is cropping overscan or not. // Current position in pixels. X is a decimal number. Y is floored to an integer. - vec2 pixelCoord = vTexCoord * params.OutputSize.xy / vec2(SIGNAL_RES, 1) + (vec2(global.pn_width_uncropped, global.pn_height_uncropped) - params.OutputSize.xy / vec2(SIGNAL_RES, 1)) / 2; - pixelCoord.y = floor(pixelCoord.y); + vec2 pixelCoord = realPixelCoord(); if(global.pn_nes_enable > 0.5) { @@ -340,10 +351,17 @@ void main() vec3 yuvCol = yuvMat * colorIn.rgb; - float luma = yuvCol.r / global.pn_color_amplitude; - float chroma = (yuvCol.g * sin(phase) + yuvCol.b * cos(phase)); + if(global.pn_connection_type < 1.5) { + // Composite and S-Video - FragColor = vec4(luma, chroma, 0, 1); + float luma = yuvCol.r / global.pn_color_amplitude; + float chroma = (yuvCol.g * sin(phase) + yuvCol.b * cos(phase)); + + FragColor = vec4(luma, chroma, 0, 1); + } else if(global.pn_connection_type < 2.5) { + // No Artifacts + FragColor = vec4(yuvCol, 1); + } } } diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass5.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-eotf.slang similarity index 72% rename from ntsc/shaders/patchy-ntsc/patchy-ntsc-pass5.slang rename to ntsc/shaders/patchy-ntsc/patchy-ntsc-eotf.slang index 227d4a84..3ce0536d 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass5.slang +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-eotf.slang @@ -48,9 +48,6 @@ layout(set = 0, binding = 2) uniform sampler2D Source; // Includes lowpass and bandpass functions #include "patchy-ntsc-inc-filters.inc" -// Includes sRGB_to_linear -#include "../../../../shaders_slang/include/colorspace-tools.h" - // CRT EOTF Function // Taken from Grade //---------------------------------------------------------------------- @@ -123,6 +120,81 @@ vec3 EOTF_1886a_default_f3(vec3 color, float brightness, float contrast) { return color; } +vec3 smpteBarsSplit(vec2 pos) { + float wholeRamp = pos.x * 8; + float flooredWholeRamp = floor(pos.x * 8); + if(pos.y < 0.75) { + if(pos.y < 0.75 / 2) + return vec3(-43); + // Standard color bars. Top is 75%; bottom is 100%. + int index = 7 - int(flooredWholeRamp + 0.5); + return vec3((index & 2) >> 1, index >> 2, index & 1); + } else if(pos.x > 0.875) { + if(pos.y < (0.75 + 1.0) / 2.0) + return vec3(-43); + // Standard mini-pluge for syncing the black level + float partialRamp = wholeRamp - flooredWholeRamp; + float steppedPartialRamp = floor(partialRamp * 3); + return vec3((steppedPartialRamp - 1.0) * 0.075); + } else { + // Deeper pluge for setting a nonstandard brightness + float normPos = pos.x / 0.875; + float normY = (pos.y - 0.75) / 0.25; + + if(mod(normY, 0.25) < 0.125) + return vec3(-43); + + float steppedPos = floor(normPos * 21); + float finalRamp = (steppedPos - 10) / 10; + + if(normY < 0.25) { + return vec3(finalRamp, 0, 0); + } else if(normY < 0.5) { + return vec3(0, finalRamp, 0); + } else if(normY < 0.75) { + return vec3(0, 0, finalRamp); + } else { + return vec3(finalRamp); + } + } +} + +vec3 colorRampsSplit(vec2 pos) { + int x = int(pos.x * 17); + int y = int(pos.y * 15); + + if(x == 0 || x == 16 || (y & 1) == 1) { // Changed (y & 1) == 0 to (y & 1) == 1 + return vec3(-43); + } else { + // Red, green, blue, and white are grouped together on purpose. + vec3 primaries[] = { + vec3(1, 1, 1), + vec3(1, 0, 0), + vec3(0, 1, 0), + vec3(0, 0, 1), + vec3(0, 1, 1), + vec3(1, 0, 1), + vec3(1, 1, 0), + vec3(0, 0, 0) // Added an eighth as failsafe + }; + return mix(vec3(0), primaries[y >> 1], x / 15.0); + } +} + +vec3 testPatternSplit(vec2 pos) { + if(global.pn_test_pattern < 1.5) { + return smpteBarsSplit(pos); + } else if(global.pn_test_pattern < 2.5) { + return colorRampsSplit(pos); + } else if(global.pn_test_pattern < 3.5) { + return vec3(-43); + } else if(global.pn_test_pattern < 4.5) { + return vec3(-43); + } else if(global.pn_test_pattern < 5.5) { + return vec3(-43); + } +} + void main() { vec3 rgb; @@ -149,6 +221,12 @@ void main() rgb /= gammaInverseMaxPickable(); rgb = rgb * (1.0 + global.pn_knob_brightness) - global.pn_knob_brightness; + if(global.pn_test_pattern_split > 0.5 && global.pn_test_pattern > 0.5) { + vec3 rgb2 = testPatternSplit(vTexCoord); + if(rgb2 != vec3(-43)) + rgb = rgb2; + } + if(global.pn_gamma_type < 0.5) { FragColor = vec4(EOTF_1886a_f3(rgb, CRT_l, global.pn_knob_brightness, global.pn_knob_contrast), 1.0); } else if(global.pn_gamma_type < 1.5) { diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-filters.inc b/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-filters.inc index 52e492ee..1062ae57 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-filters.inc +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-filters.inc @@ -14,42 +14,94 @@ You should have received a copy of the GNU General Public License along with thi #ifndef __patchy_ntsc_inc_filters_inc__ #define __patchy_ntsc_inc_filters_inc__ - -#include "patchy-ntsc-inc-params.inc" - /////////////////////// // General functions // /////////////////////// +// NES simulation requires SIGNAL_RES to be exactly 8. Not 16. Not 4. Only 8. +// Some code involving NES has 8 hardcoded, and some code uses SIGNAL_RES instead. +#define SIGNAL_RES global.pn_signal_res +#define pi 3.14159265358 + +// The initial release of Patchy NTSC kept multiplying things by SIGNAL_RES without accounting for changes in resolution, colorburst rate, and active area duration. +// These macros combine all those corrections into one macro, based on the initial release's Sega Genesis settings. +// Always be careful about whether you use SIGNAL_RES or SIGNAL_RES_CORRECTION. +#define SETTINGS_CORRECTION (getSizes().x / 320.0 * 47.7 / global.pn_scanline_dur * 3.5795454 / global.pn_color_freq) +#define SIGNAL_RES_CORRECTION (SIGNAL_RES * SETTINGS_CORRECTION) + +// Get the cropped and uncropped sizes of the screen, taking into account the possibility that the resolution was multiplied by an exact integer. +vec4 getSizes() { + vec2 sizeUncropped = vec2(global.pn_width_uncropped, global.pn_height_uncropped); + vec2 sizeCropped = params.OutputSize.xy / vec2(SIGNAL_RES, 1); + + // Correct for integer-scaled resolutions. + if(global.pn_width_integer_mul > 0.5) + sizeUncropped.x *= floor(0.501 + sizeCropped.x / sizeUncropped.x); + if(global.pn_height_integer_mul > 0.5) + sizeUncropped.y *= floor(0.501 + sizeCropped.y / sizeUncropped.y); + + if(global.pn_dual_width_mode > 0.5) { + float smallWidthUncropped = global.pn_width_secondary_uncropped; + + // Correct for integer-scaled resolutions. + if(global.pn_width_integer_mul > 0.5) + smallWidthUncropped *= floor(0.501 + sizeCropped.x / smallWidthUncropped); + + // Use whichever resolution we're closer to, using the absolute value of the difference. + float smallDiff = abs(sizeCropped.x - smallWidthUncropped); + float largeDiff = abs(sizeCropped.x - sizeUncropped.x); + if(smallDiff < largeDiff) + sizeUncropped.x = smallWidthUncropped; + } + + return vec4(sizeUncropped, sizeCropped); +} + +// Find which scanline we're on, and what percent of the active area has been completed. +// This takes cropped overscan and changed resolution into account, under three assumptions: +// 1. An equal amount of overscan was cropped on the left and right sides. +// 2. The top and bottom sides either had an equal amount cropped or had exactly one more pixel cropped off the bottom than on the top. +// 3. The resulution, horizontal or vertical, might have been multiplied by an exact integer. (For example, in bsnes-mercury.) vec2 realPixelCoord() { - vec2 pixelCoord = vTexCoord * params.OutputSize.xy / vec2(SIGNAL_RES, 1) + (vec2(global.pn_width_uncropped, global.pn_height_uncropped) - params.OutputSize.xy / vec2(SIGNAL_RES, 1)) / 2; - pixelCoord.y = floor(pixelCoord.y); + vec4 sizes = getSizes(); + vec2 sizeUncropped = sizes.xy; + vec2 sizeCropped = sizes.zw; + vec2 pixelCoord = vTexCoord * sizeCropped + (sizeUncropped - sizeCropped) / 2.0; + pixelCoord.y = floor(pixelCoord.y + 0.01); return pixelCoord; } -int getPhaseIntNES() { +int getPhaseIntNES(int yOff) { vec2 pixelCoord = realPixelCoord(); - return int(0.1 + 8 * pixelCoord.x + 4 * pixelCoord.y + 4 * (int(params.FrameCount + 0.1) % int(2.1 + global.pn_nes_phase_mod)));; + return int(0.1 + 8 * pixelCoord.x + 4 * (pixelCoord.y + yOff) + 4 * (int(params.FrameCount + 0.1) % int(2.1 + global.pn_nes_phase_mod)));; } -float getPhase() { +int getPhaseIntNES() { + return getPhaseIntNES(0); +} + +float getPhase(int yOff) { if(global.pn_nes_enable > 0.5) { - int p = getPhaseIntNES(); + int p = getPhaseIntNES(yOff); return 2 * pi * (p + 3) / 12.0; } else { vec2 pixelCoord = realPixelCoord(); - float realOffX = pixelCoord.x / global.pn_width_uncropped; - return 2 * pi * (realOffX * global.pn_scanline_dur * global.pn_color_freq + global.pn_color_init_offset + global.pn_color_line_offset * pixelCoord.y + params.FrameCount * global.pn_color_screen_offset); + float realOffX = pixelCoord.x / getSizes().x; + return 2 * pi * (realOffX * global.pn_scanline_dur * global.pn_color_freq + global.pn_color_init_offset + global.pn_color_line_offset * (pixelCoord.y + yOff) + (global.pn_color_screen_offset_modulo < 1.5 ? params.FrameCount : params.FrameCount % int(global.pn_color_screen_offset_modulo + 0.5)) * global.pn_color_screen_offset); } } +float getPhase() { + return getPhase(0); +} + float getFreqFactor() { if(global.pn_nes_enable > 0.5) { // https://www.nesdev.org/wiki/NTSC_video as of August 15, 2024 // Each NES pixel is 186 nanoseconds. return 0.186 / 8.0 * 2 * pi; } else { - return global.pn_scanline_dur / global.pn_width_uncropped / SIGNAL_RES * 2 * pi; + return global.pn_scanline_dur / getSizes().x / SIGNAL_RES * 2 * pi; } } @@ -57,10 +109,40 @@ float getBurstRate() { if(global.pn_nes_enable > 0.5) { return 2 * pi / 12.0; } else { - return 2 * pi / SIGNAL_RES / global.pn_width_uncropped * global.pn_scanline_dur * global.pn_color_freq; + return 2 * pi / SIGNAL_RES / getSizes().x * global.pn_scanline_dur * global.pn_color_freq; } } +// These two sRGB functions are taken from shaders_slang/include/colorspace-tools.h +// Colorspace Tools +// ported from Asmodean's PsxFX Shader Suite v2.00 +// License: GPL v2+ +vec3 linear_to_sRGB(vec3 color, float gamma) +{ + color = clamp(color, 0.0, 1.0); + color.r = (color.r <= 0.00313066844250063) ? + color.r * 12.92 : 1.055 * pow(color.r, 1.0 / gamma) - 0.055; + color.g = (color.g <= 0.00313066844250063) ? + color.g * 12.92 : 1.055 * pow(color.g, 1.0 / gamma) - 0.055; + color.b = (color.b <= 0.00313066844250063) ? + color.b * 12.92 : 1.055 * pow(color.b, 1.0 / gamma) - 0.055; + + return color.rgb; +} + +vec3 sRGB_to_linear(vec3 color, float gamma) +{ + color = clamp(color, 0.0, 1.0); + color.r = (color.r <= 0.04045) ? + color.r / 12.92 : pow((color.r + 0.055) / (1.055), gamma); + color.g = (color.g <= 0.04045) ? + color.g / 12.92 : pow((color.g + 0.055) / (1.055), gamma); + color.b = (color.b <= 0.04045) ? + color.b / 12.92 : pow((color.b + 0.055) / (1.055), gamma); + + return color.rgb; +} + ////////////////////////////////// // Common functions for filters // ////////////////////////////////// @@ -78,6 +160,12 @@ float hamming(int samplePos, int width) { return pow(cos(pi * samplePos / width / 2), 2); } +// Blackman window +// https://www.analog.com/media/en/technical-documentation/dsp-book/dsp_book_Ch16.pdf +float blackman(int samplePos, int width) { + return 0.42 - 0.5 * cos(2 * pi * samplePos / width) + 0.08 * cos(4 * pi * samplePos / width); +} + ///////////////////////////////// // Three band equalizer filter // ///////////////////////////////// @@ -95,8 +183,8 @@ vec4 filterEQF(float f_lo_mhz, float f_hi_mhz, // https://www.nesdev.org/wiki/NTSC_video as of August 15, 2024 // Each NES pixel is 186 nanoseconds. -#define L_FREQ (global.pn_nes_enable > 0.5 ? 8 / 0.186 : SIGNAL_RES * global.pn_width_uncropped / global.pn_scanline_dur) -#define CRT_HRES (global.pn_width_uncropped * SIGNAL_RES) +#define L_FREQ (global.pn_nes_enable > 0.5 ? 8 / 0.186 : SIGNAL_RES * getSizes().x / global.pn_scanline_dur) +#define CRT_HRES (getSizes().x * SIGNAL_RES) #define MHz2L(MHz) (CRT_HRES * ((MHz)) / L_FREQ) #define HISTLEN 7 @@ -134,7 +222,7 @@ vec4 filterEQF(float f_lo_mhz, float f_hi_mhz, float r[3]; - int maxDistI = int(0.5 + maxDistFailsafe / 100.0 * SIGNAL_RES * global.pn_width_uncropped); + int maxDistI = int(0.5 + maxDistFailsafe / 100.0 * SIGNAL_RES * getSizes().x); overallOffset /= SIGNAL_RES; @@ -186,13 +274,13 @@ vec4 filterEQF(float f_lo_mhz, float f_hi_mhz, vec4 lowpass(float halfWidth, float level) { float freqFactor = getFreqFactor(); float levelFixed = level * freqFactor; - int halfWidthInt = int(floor(halfWidth * SIGNAL_RES + 0.5)); // Round to nearest integer. + int halfWidthInt = int(floor(halfWidth * SIGNAL_RES_CORRECTION + 0.5)); // Round to nearest integer. float totalMax = 0.; vec4 lowPassed = vec4(0); for(int i = -halfWidthInt; i <= halfWidthInt; i++) { vec4 samp = texture(Source, vTexCoord + vec2(i * params.OutputSize.z, 0)); - float factor = levelFixed * sinc(levelFixed * i) * hamming(i, halfWidthInt); + float factor = levelFixed * sinc(levelFixed * i) * blackman(i + halfWidthInt, halfWidthInt * 2); totalMax += factor; lowPassed += factor * samp; } @@ -203,8 +291,8 @@ vec4 lowpass(float halfWidth, float level) { vec4 lowpassGaussian(float sigma, float radius) { // Copied and pasted from Guest.r's gaussian blur shader - float SIGMA_H = sigma * SIGNAL_RES; - float SIZEH = radius * SIGNAL_RES; // 2.6 standard deviations = 99% + float SIGMA_H = sigma * SIGNAL_RES_CORRECTION; + float SIZEH = radius * SIGNAL_RES_CORRECTION; // 2.6 standard deviations = 99% float invsqrsigma = 1.0/(2.0*SIGMA_H*SIGMA_H); float f = fract(params.OutputSize.x * vTexCoord.x); @@ -248,8 +336,8 @@ vec4 lowpassGTU(float signalRes) { // Taken from GTU-Famicom by aliaspider float offset = fract((vTexCoord.x * params.OutputSize.x) - 0.5); - float range = ceil(0.5 + global.pn_width_uncropped * SIGNAL_RES / signalRes); - float Y = signalRes / global.pn_width_uncropped / SIGNAL_RES; // Remember to compensate for cropped overscan. The user might or might not be cropping it. + float range = ceil(0.5 + (global.pn_nes_enable < 0.5 ? params.OutputSize.x * SETTINGS_CORRECTION : getSizes().x * SIGNAL_RES) / signalRes); + float Y = signalRes * (global.pn_nes_enable < 0.5 ? params.OutputSize.z / SETTINGS_CORRECTION : 1.0 / getSizes().x / SIGNAL_RES); // Remember to compensate for cropped overscan. The user might or might not be cropping it. float X; vec4 c; vec4 combined = vec4(0); @@ -272,21 +360,69 @@ vec4 twoPointCombLuma() { if(global.pn_nes_enable > 0.5) { halfPhase = 6 * params.OutputSize.z; } else { - halfPhase = params.OutputSize.z * 0.5 / (global.pn_scanline_dur * global.pn_color_freq / global.pn_width_uncropped / SIGNAL_RES); + halfPhase = ceil(getSizes().x * SIGNAL_RES / global.pn_scanline_dur / global.pn_color_freq / 2.0) * params.OutputSize.z; } // vec2 coord = vTexCoord + vec2(floor(halfPhase / params.OutputSize.z / 2) * params.OutputSize.z, 0); // Make sure we step by a multiple of an input pixel. Stepping by a fraction doesn't filter as good. // return (texture(Source, coord) + texture(Source, coord - vec2(halfPhase, 0))) / 2; // return vec4(0); - return (texture(Source, vTexCoord) + texture(Source, vTexCoord - vec2(halfPhase, 0))) / 2; + return (texture(Source, vTexCoord + vec2(halfPhase / 2.0, 0)) + texture(Source, vTexCoord - vec2(halfPhase / 2.0, 0))) / 2; +} + +vec4 lowpassFullPhase(float width) { + float wavelength = global.pn_nes_enable < 0.5 ? getSizes().x * SIGNAL_RES / global.pn_scanline_dur / global.pn_color_freq : 12; + wavelength *= width; + + int start = int(ceil(-wavelength / 2.0) + 0.5); + float end = start + floor(wavelength + 0.5); + + vec4 sum = vec4(0); + vec4 samp; + int i; + for(i = start; i < end; i++) { + samp = texture(Source, vTexCoord + vec2(i * params.OutputSize.z, 0.0)); + sum += samp; + } +// samp = texture(Source, vTexCoord + vec2(i * params.OutputSize.z, 0.0)); +// sum += samp * (end - (i - 1)); + + return sum / (end - start); +} + +vec4 twoLineComb(bool chroma) { + float wavelength = global.pn_nes_enable < 0.5 ? getSizes().x * SIGNAL_RES / global.pn_scanline_dur / global.pn_color_freq : 12; + + float thisOffset = mod(getPhase() / 2 / pi, 1.0); + float prevOffset = mod(getPhase(-1) / 2 / pi + 0.5, 1.0); + + float diff = mod(thisOffset - prevOffset + 0.5, 1.0) - 0.5; + + vec4 thisLuma = texture(Source, vTexCoord); + vec4 prevLuma = texture(Source, vTexCoord + params.OutputSize.zw * vec2(diff * wavelength, -0.999)); + + return (thisLuma + prevLuma * (chroma ? -1 : 1)) / 2; +} + +vec4 threeLineComb(bool chroma) { + float wavelength = global.pn_nes_enable < 0.5 ? getSizes().x * SIGNAL_RES / global.pn_scanline_dur / global.pn_color_freq : 12; + + float thisOffset = mod(getPhase() / 2 / pi, 1.0); + float prevOffset = mod(getPhase(-1) / 2 / pi + 0.5, 1.0); + float nextOffset = mod(getPhase(1) / 2 / pi + 0.5, 1.0); + + float diffPrev = mod(thisOffset - prevOffset + 0.5, 1.0) - 0.5; + float diffNext = mod(thisOffset - nextOffset + 0.5, 1.0) - 0.5; + + vec4 thisLuma = texture(Source, vTexCoord); + vec4 prevLuma = texture(Source, vTexCoord + params.OutputSize.zw * vec2(diffPrev * wavelength, -0.999)); + vec4 nextLuma = texture(Source, vTexCoord + params.OutputSize.zw * vec2(diffNext * wavelength, 0.999)); + + return (thisLuma + (prevLuma + nextLuma) / 2.0 * (chroma ? -1 : 1)) / 2; } vec4 lowpassPickable(float type, float halfWidth, float level, // artifact-colors sinc window float gaussSigma, float gaussRadius, // guest(r) gaussian blur - float signalRes, // aliaspider gtu - float f_lo, float f_hi, - float g_lo, float g_mid, float g_hi, - float maxDistFailsafe, float overallOffset) // EMMIR NTSC-CRT + float signalRes) // aliaspider gtu) { if(type < -0.5) { return texture(Source, vTexCoord); // No low pass @@ -296,13 +432,19 @@ vec4 lowpassPickable(float type, return lowpassGaussian(gaussSigma, gaussRadius); } else if (type < 2.5) { return lowpassGTU(signalRes); + + // 3 onward are for decoding only } else if (type < 3.5) { - return filterEQF(f_lo, f_hi, g_lo, g_mid, g_hi, 0, maxDistFailsafe, overallOffset); - } else { + return lowpassFullPhase(1.0); + } else if (type < 4.5) { // This filter is only for the demodulator, so it's at the end. // The lowpass filter selection for the modulator only goes up to 3 to prevent // this one from being picked for that. return twoPointCombLuma(); + } else if(type < 5.5) { + return twoLineComb(false); + } else { + return threeLineComb(false); } } @@ -316,7 +458,7 @@ vec4 bandpass(float halfWidth, float levelLo, float levelHi) { float freqFactor = getFreqFactor(); float levelFixedLo = (levelLo) * freqFactor; float levelFixedHi = (levelHi) * freqFactor; - int halfWidthInt = int(floor(halfWidth * SIGNAL_RES + 0.5)); // Round to nearest integer. + int halfWidthInt = int(floor(halfWidth * SIGNAL_RES_CORRECTION + 0.5)); // Round to nearest integer. float burstRate = getBurstRate(); @@ -324,7 +466,7 @@ vec4 bandpass(float halfWidth, float levelLo, float levelHi) { float maxSum = 0.0; for(int i = -halfWidthInt; i <= halfWidthInt; i++) { vec4 samp = texture(Source, vTexCoord + vec2(i * params.OutputSize.z, 0)); - float window = hamming(i, halfWidthInt); + float window = blackman(i + halfWidthInt, halfWidthInt * 2); float factorLo = levelFixedLo * sinc(levelFixedLo * i); float factorHi = levelFixedHi * sinc(levelFixedHi * i); float factor = factorHi - factorLo; @@ -338,7 +480,7 @@ vec4 bandpass(float halfWidth, float levelLo, float levelHi) { vec4 twoPointCombChroma() { float halfPhase; if(global.pn_nes_enable < 0.5) { - halfPhase = params.OutputSize.z * SIGNAL_RES / 2 / (global.pn_scanline_dur * global.pn_color_freq / global.pn_width_uncropped); + halfPhase = params.OutputSize.z * SIGNAL_RES / 2 / (global.pn_scanline_dur * global.pn_color_freq / getSizes().x); } else { halfPhase = params.OutputSize.z * 6; } @@ -346,18 +488,20 @@ vec4 twoPointCombChroma() { } vec4 bandpassPickable(float type, - float halfWidth, float level, float levelDiff, - float f_lo, float f_hi, - float g_lo, float g_mid, float g_hi, float maxDistFailsafe, float overallOffset, int index) + float halfWidth, float level, float levelDiff) { if(type < -0.5) { return texture(Source, vTexCoord); } else if(type < 0.5) { return bandpass(halfWidth, level, levelDiff); + + // 1 is for decoding only } else if(type < 1.5) { - return filterEQF(f_lo, f_hi, g_lo, g_mid, g_hi, index, maxDistFailsafe, overallOffset) * 2; - } else { return twoPointCombChroma(); + } else if(type < 2.5) { + return twoLineComb(true); + } else { + return threeLineComb(true); } } @@ -480,17 +624,74 @@ mat3x2 yuvAxisPoints() { b_off, b_max); } -mat3x2 yuvAxisPointsSynced() { +mat3x2 yuvSyncRed(mat3x2 yuvAxisPts) { + float xr, yr, xg, yg, xb, yb; + xr = yuvAxisPts[0][1] * cos(yuvAxisPts[0][0]); + yr = yuvAxisPts[0][1] * sin(yuvAxisPts[0][0]); + xg = yuvAxisPts[1][1] * cos(yuvAxisPts[1][0]); + yg = yuvAxisPts[1][1] * sin(yuvAxisPts[1][0]); + xb = yuvAxisPts[2][1] * cos(yuvAxisPts[2][0]); + yb = yuvAxisPts[2][1] * sin(yuvAxisPts[2][0]); - // Do not automatically sync the SMPTE-C and Rec. 709 matrices. Just return the default. - // Automatic syncing had to be done for jungle chips because their defaults are not listed in their data sheets. + mat3 toRgb = mat3(1, 1, 1, + xr, xg, xb, + yr, yg, yb); - mat3x2 yuvAxisPts = yuvAxisPoints(); - - if(global.pn_demodulator_std < 1.5) { - return yuvAxisPts; - } - + mat3 fromRgb = inverse(toRgb); + mat3 fromRgbStd; + + // if(global.pn_modulator_std < 0.5) { + // Rec. 601 YUV matrix + // This is used in the Genesis/MegaDrive and SNES/SFC + fromRgbStd = mat3x3( + 0.299, -0.14713, 0.615, + 0.587, -0.28886, -0.51499, + 0.114, 0.436, -0.10001 + ); + // } else { + // // Rec. 709 YUV matrix + // // Don't know any specific consoles using this, but I assume this *eventually* became common. + // fromRgbStd = mat3(0.2126, -0.09991, 0.615, + // 0.7152, -0.33609, -0.55861, + // 0.0722, 0.436, -0.05639); + // } + + vec3 colorBar = vec3(1, 0, 0); // Red + vec3 yuvJungle = fromRgb * colorBar; + vec3 yuvStd = fromRgbStd * colorBar; + float resid = atan(yuvJungle.b, yuvJungle.g) - atan(yuvStd.b, yuvStd.g); + + for(int i = 0; i < 3; i++) { + yuvAxisPts[i][0] -= resid; + } + + xr = yuvAxisPts[0][1] * cos(yuvAxisPts[0][0]); + yr = yuvAxisPts[0][1] * sin(yuvAxisPts[0][0]); + xg = yuvAxisPts[1][1] * cos(yuvAxisPts[1][0]); + yg = yuvAxisPts[1][1] * sin(yuvAxisPts[1][0]); + xb = yuvAxisPts[2][1] * cos(yuvAxisPts[2][0]); + yb = yuvAxisPts[2][1] * sin(yuvAxisPts[2][0]); + + toRgb = mat3(1, 1, 1, + xr, xg, xb, + yr, yg, yb); + + fromRgb = inverse(toRgb); + + yuvJungle = fromRgb * colorBar; + yuvStd = fromRgbStd * colorBar; + float satJungle = sqrt(pow(yuvJungle.g, 2) + pow(yuvJungle.b, 2)); + float satStd = sqrt(pow(yuvStd.g, 2) + pow(yuvStd.b, 2)); + float prop = satJungle / satStd; // What saturation do we set to make this bar match in saturation? + + for(int i = 0; i < 3; i++) { + yuvAxisPts[i][1] *= prop; + } + + return yuvAxisPts; +} + +mat3x2 yuvSyncAvg(mat3x2 yuvAxisPts) { // if(global.eztvcol3_cmp_console < 1.5) { // // This code is made using the mathematical formulas outlined by Chthon at https://forums.libretro.com/t/dogways-grading-shader-slang/27148/561 // // The result is an RGB to RGB matrix. @@ -527,7 +728,7 @@ mat3x2 yuvAxisPointsSynced() { // 1 - (cr.r + wr + cr.g + wg), 1 - (cg.r + wr + cg.g + wg), 1 - (cb.r + wr + cb.g + wg) // ); // } else { - + // This simpler code generates a YUV to RGB matrix. float xr, yr, xg, yg, xb, yb; xr = yuvAxisPts[0][1] * cos(yuvAxisPts[0][0]); @@ -540,11 +741,11 @@ mat3x2 yuvAxisPointsSynced() { mat3 toRgb = mat3(1, 1, 1, xr, xg, xb, yr, yg, yb); - + mat3 fromRgb = inverse(toRgb); mat3 fromRgbStd; - -// if(global.pn_modulator_std < 0.5) { + + // if(global.pn_modulator_std < 0.5) { // Rec. 601 YUV matrix // This is used in the Genesis/MegaDrive and SNES/SFC fromRgbStd = mat3x3( @@ -552,20 +753,20 @@ mat3x2 yuvAxisPointsSynced() { 0.587, -0.28886, -0.51499, 0.114, 0.436, -0.10001 ); -// } else { -// // Rec. 709 YUV matrix -// // Don't know any specific consoles using this, but I assume this *eventually* became common. -// fromRgbStd = mat3(0.2126, -0.09991, 0.615, -// 0.7152, -0.33609, -0.55861, -// 0.0722, 0.436, -0.05639); -// } + // } else { + // // Rec. 709 YUV matrix + // // Don't know any specific consoles using this, but I assume this *eventually* became common. + // fromRgbStd = mat3(0.2126, -0.09991, 0.615, + // 0.7152, -0.33609, -0.55861, + // 0.0722, 0.436, -0.05639); + // } // Basic linear regression to match the hue rotation (tint) to standard color bars. // Simply average up the offsets, and that's the ideal hue rotation. // On US CRTs, many people probably rotated this further to get greener greens, but then, some green would become very cyan, and some reds and pinks would become very orange. // Other people on US CRTs, especially on the NES, probably rotated a little in the opposite direction to get browner browns. // A problem with this method is that this is not a perceptually uniform color space. I will address that sometime else. - + float residSum = 0; for(int i = 1; i <= 6; i++) { vec3 colorBar = vec3(i & 1, (i >> 1) & 1, i >> 2); @@ -578,13 +779,13 @@ mat3x2 yuvAxisPointsSynced() { resid += 2 * pi; residSum += resid; } - + float hueOff = -residSum / 6; - + for(int i = 0; i < 3; i++) yuvAxisPts[i][0] += hueOff; - - + + xr = yuvAxisPts[0][1] * cos(yuvAxisPts[0][0]); yr = yuvAxisPts[0][1] * sin(yuvAxisPts[0][0]); xg = yuvAxisPts[1][1] * cos(yuvAxisPts[1][0]); @@ -595,7 +796,7 @@ mat3x2 yuvAxisPointsSynced() { toRgb = mat3(1, 1, 1, xr, xg, xb, yr, yg, yb); - + fromRgb = inverse(toRgb); // Old least-squares linear regression to match the saturation to the color bars. @@ -603,29 +804,29 @@ mat3x2 yuvAxisPointsSynced() { // Meaning, R is the sum of squared differences in saturations. It's a subtraction, not a ratio. // Leaving (Saturation Factor) as a variable and simplifying down, the equation is in the format R = A(Saturation Factor)^2 + B(Saturation Factor) + C. // The value of (Saturation Factor) that minimizes R is in the center of the parabola, or -B/(2*A). The value C is never used, so we don't calculate it. - -// float Aval = 0; -// float Bval = 0; -// for(int i = 1; i <= 6; i++) { -// vec3 colorBar = vec3(i & 1, (i >> 1) & 1, i >> 2); -// vec3 yuvJungle = fromRgb * colorBar; -// vec3 yuvStd = fromRgbStd * colorBar; -// Aval += pow(yuvJungle.g, 2) + pow(yuvJungle.b, 2); -// Bval += 2 * sqrt(pow(yuvJungle.g, 2) + pow(yuvJungle.b, 2)) * -sqrt(pow(yuvStd.g, 2) + pow(yuvStd.b, 2)); -// } -// -// float satFix = -Bval / (2 * Aval); - -// toRgb[0] *= 1; // Have to add this line here because some GPU drivers force you to multiply all 3 columns together. -// toRgb[1] /= satFix; -// toRgb[2] /= satFix; + + // float Aval = 0; + // float Bval = 0; + // for(int i = 1; i <= 6; i++) { + // vec3 colorBar = vec3(i & 1, (i >> 1) & 1, i >> 2); + // vec3 yuvJungle = fromRgb * colorBar; + // vec3 yuvStd = fromRgbStd * colorBar; + // Aval += pow(yuvJungle.g, 2) + pow(yuvJungle.b, 2); + // Bval += 2 * sqrt(pow(yuvJungle.g, 2) + pow(yuvJungle.b, 2)) * -sqrt(pow(yuvStd.g, 2) + pow(yuvStd.b, 2)); + // } + // + // float satFix = -Bval / (2 * Aval); + + // toRgb[0] *= 1; // Have to add this line here because some GPU drivers force you to multiply all 3 columns together. + // toRgb[1] /= satFix; + // toRgb[2] /= satFix; // New geometric mean to match the saturation to the color bars. // Take the proportion of the change on each color bar. // Then, take the geometric mean of those six proportions. // This has a better result than the linear regression above because saturation is not linear. // A problem with this method is that this is not a perceptually uniform color space. I will address that sometime else. - + float prop = 1.0; for(int i = 1; i <= 6; i++) { vec3 colorBar = vec3(i & 1, (i >> 1) & 1, i >> 2); @@ -635,9 +836,9 @@ mat3x2 yuvAxisPointsSynced() { float satStd = sqrt(pow(yuvStd.g, 2) + pow(yuvStd.b, 2)); prop *= satJungle / satStd; // What saturation do we set to make this bar match in saturation? } - + prop = pow(prop, 1.0/6.0); // Geometric mean of those proportional errors. - + for(int i = 0; i < 3; i++) yuvAxisPts[i][1] *= prop; @@ -645,6 +846,83 @@ mat3x2 yuvAxisPointsSynced() { // } } +mat3x2 yuvSyncEyeballed(mat3x2 yuvAxisPts) { + float hue, sat; + + // DISCLAIMER: I do not own CRTs with these chips at the moment. + // I have set these settings on this same program by eye. + // Meant to be used with brightness 0.1 and + + if(global.pn_demodulator_std < 1.5) { + // Standard matrices that are already sync'd + hue = 0.0; + sat = 1.0; + } else if(global.pn_demodulator_std < 2.5) { + // CXA2025AS JP + hue = 0.0; + sat = 0.8; + } else if(global.pn_demodulator_std < 3.5) { + // CXA1464AS + hue = 0.0; + sat = 0.8; + } else if(global.pn_demodulator_std < 4.5) { + // AN5367FB + // Sync'd against the averaged matrix + yuvAxisPts = yuvSyncAvg(yuvAxisPts); + hue = -3.0; + sat = 0.9; + } else if(global.pn_demodulator_std < 5.5) { + // TA8867BN + // Sync'd against the averaged matrix + yuvAxisPts = yuvSyncAvg(yuvAxisPts); + hue = -5.0; + sat = 0.9; + } else if(global.pn_demodulator_std < 6.5) { + // TA8867AN + // Sync'd against the averaged matrix + yuvAxisPts = yuvSyncAvg(yuvAxisPts); + hue = -5.0; + sat = 0.9; + } else if(global.pn_demodulator_std < 7.5) { + // CXA2025AS US + hue = -12; + sat = 0.8; + } else if(global.pn_demodulator_std < 8.5) { + // CXA1465AS US + hue = -12; + sat = 0.8; + } else if(global.pn_demodulator_std < 9.5) { + // Custom. Let the user figure it out. + hue = 0.0; + sat = 1.0; + } + + for(int i = 0; i < 3; i++) { + yuvAxisPts[i][0] += hue / 180.0 * pi; + yuvAxisPts[i][1] *= sat; + } + + return yuvAxisPts; +} + +mat3x2 yuvAxisPointsSynced() { + + // Do not automatically sync the SMPTE-C and Rec. 709 matrices. Just return the default. + // Automatic syncing had to be done for jungle chips because their defaults are not listed in their data sheets. + + mat3x2 yuvAxisPts = yuvAxisPoints(); + + if(global.pn_demodulator_std < 1.5 || global.pn_demodulator_std_defaults < -0.5) { + return yuvAxisPts; + } else if(global.pn_demodulator_std_defaults < 0.5) { + return yuvSyncAvg(yuvAxisPts); + } else if(global.pn_demodulator_std_defaults < 1.5) { + return yuvSyncRed(yuvAxisPts); + } else { + return yuvSyncEyeballed(yuvAxisPts); + } +} + mat3 YBmyRmyToRGBMatrix() { mat3x2 yuvAxisPts = yuvAxisPointsSynced(); @@ -672,9 +950,7 @@ mat3 YBmyRmyToRGBMatrix() { vec2 uvDemodPickable(float type, float chroma, float phase, float tint) { mat3x2 axes = yuvAxisPointsSynced(); - if(type < -0.5) { - return 2 * chroma * vec2(axes[2][1] * sin(phase + axes[2][0] + tint), axes[0][1] * sin(phase + axes[0][0] + tint)); - } else if(type < 1.5) { // either 0 or 1 + if(type < 0.5 || type > 1.5) { return 2 * chroma * vec2(axes[2][1] * sin(phase + axes[2][0] + tint), axes[0][1] * sin(phase + axes[0][0] + tint)); } else { return 2 * chroma * vec2(axes[2][1] * sin(phase + pi / 2 + axes[2][0] + tint), axes[0][1] * sin(phase + pi / 2 + axes[0][0] + tint)); @@ -973,6 +1249,14 @@ vec3 nesRealCaptureLookup(vec3 yBmyRmy, float saturation) { 0.0722, 0.436, -0.05639) * RGB; } +vec2 uvFilterPickable(float type) { + if(type < -0.5) { + return texture(Source, vTexCoord).gb; + } else { + return lowpassFullPhase(global.pn_demodulator_uv_filter_width).gb; + } +} + ////////////////// // Inverse EOTF // ////////////////// diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-params.inc b/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-params.inc index e2ace8fe..14026fa9 100644 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-params.inc +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-inc-params.inc @@ -14,24 +14,26 @@ You should have received a copy of the GNU General Public License along with thi #ifndef __patchy_ntsc_inc_params_inc__ #define __patchy_ntsc_inc_params_inc__ -// NES simulation requires SIGNAL_RES to be exactly 8. Not 16. Not 4. Only 8. -// Some code involving NES has 8 hardcoded, and some code uses SIGNAL_RES instead. -#define SIGNAL_RES 8 -#define pi 3.14159265358 - layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; float pn_test_pattern, + pn_test_pattern_split, + pn_signal_res, pn_width_uncropped, pn_height_uncropped, + pn_width_integer_mul, + pn_height_integer_mul, + pn_dual_width_mode, + pn_width_secondary_uncropped, pn_scanline_dur, pn_color_amplitude, pn_color_freq, pn_color_init_offset, pn_color_line_offset, pn_color_screen_offset, + pn_color_screen_offset_modulo, pn_nes_enable, pn_nes_phase_mod, pn_nes_real_capture, @@ -40,6 +42,8 @@ layout(std140, set = 0, binding = 0) uniform UBO pn_genesis_jailbar_enable, pn_genesis_jailbar_offset, pn_genesis_jailbar_amplitude, + pn_rgb_blur_enable, + pn_rgb_blur_amount, pn_modulator_std, pn_modulator_luma_filter_type, pn_modulator_luma_filter_width, @@ -66,6 +70,7 @@ layout(std140, set = 0, binding = 0) uniform UBO pn_modulator_chroma_eq_off, pn_modulator_chroma_eq_dist, pn_demodulator_std, + pn_demodulator_std_defaults, pn_demodulator_r_off, pn_demodulator_r_amp, pn_demodulator_g_off, @@ -94,8 +99,15 @@ layout(std140, set = 0, binding = 0) uniform UBO pn_demodulator_chroma_eq_g_hi, pn_demodulator_chroma_eq_off, pn_demodulator_chroma_eq_dist, + pn_demodulator_uv_filter_type, pn_demodulator_uv_filter_width, - pn_demodulator_uv_filter_level, + pn_connection_type, + pn_noise_rand_offset, + pn_noise_min_rate, + pn_noise_max_rate, + pn_noise_counter, + pn_noise_severity, + pn_bad_rgb_gamma, pn_knob_contrast, pn_knob_brightness, pn_knob_saturation, @@ -114,10 +126,16 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter pn_comment_test_pattern "=== Test Patterns ===" 0 0 0 1 #pragma parameter pn_test_pattern "Test Pattern: Color Bars, Ramps, HSV, Focused HSV, NES Full" 0 0 5 1 +#pragma parameter pn_test_pattern_split "Test pattern split mode (if available)" 0 0 1 1 -#pragma parameter pn_comment_resolution "=== Auto detect cropped overscan ===" 0 0 0 1 -#pragma parameter pn_width_uncropped "Console Horizontal Resolution (pixels)" 320 128 720 1 -#pragma parameter pn_height_uncropped "Console Vertical Resolution (pixels)" 224 128 480 1 +#pragma parameter pn_comment_resolution "=== Auto detect cropped overscan and resolution changes ===" 0 0 0 1 +#pragma parameter pn_signal_res "SIGNAL_RES" 8 1 32 1 +#pragma parameter pn_width_uncropped "Console Horizontal Resolution (pixels) (rec. even numbers only)" 320 128 720 1 +#pragma parameter pn_height_uncropped "Console Vertical Resolution (pixels) (rec. 240 or 224 only)" 224 128 480 1 +#pragma parameter pn_width_integer_mul "Detect integer-multiplied width (such as bsnes-mercury)" 1 0 1 1 +#pragma parameter pn_height_integer_mul "Detect integer-multiplied height (such as interlaced video)" 1 0 1 1 +#pragma parameter pn_dual_width_mode "Detect two horizontal resolutions (such as Genesis 320 vs 256)" 0 0 1 1 +#pragma parameter pn_width_secondary_uncropped "Console Smaller Horizontal Resolution (pixels)" 256 128 720 1 #pragma parameter pn_comment_nes "=== NES Settings ===" 0 0 0 1 #pragma parameter pn_comment_nes_palette "== Note: Use Mesen (not FCEUmm) and change palette to Raw ==" 0 0 0 1 @@ -128,85 +146,108 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter pn_comment_genesis_jailbar "=== Genesis Settings ===" 0 0 0 1 #pragma parameter pn_genesis_palette "Genesis Plus GX color fix (not for BlastEm or other consoles)" 0 0 1 1 -#pragma parameter pn_genesis_jailbar_enable "Enable Genesis/MegaDrive jailbars on solid backgrounds" 0 0 1 1 +#pragma parameter pn_genesis_jailbar_enable "Enable Genesis jailbars on solid backgrounds (Composite only)" 0 0 1 1 #pragma parameter pn_genesis_jailbar_offset "Genesis Jailbars Offset" 0 0 1 0.0625 -#pragma parameter pn_genesis_jailbar_amplitude "Genesis Jailbars Severity" 1.0 0.1 3.0 0.1 +#pragma parameter pn_genesis_jailbar_amplitude "Genesis Jailbars Severity" 1.0 0.1 30.0 0.1 + +#pragma parameter pn_comment_snes_settings "=== SNES Settings ===" 0 0 0 1 +#pragma parameter pn_rgb_blur_enable "Enable RGB linear blur (such as 2-chip SNES)" 0 0 1 1 +#pragma parameter pn_rgb_blur_amount "RGB linear blur distance" 1.0 0.25 2.0 0.125 #pragma parameter pn_comment_color_carrier "=== Color carrier settings ===" 0 0 0 1 #pragma parameter pn_scanline_dur "Scanline Duration (uSec)" 47.6 1.0 300.0 0.1 -#pragma parameter pn_color_amplitude "Color Carrier Amplitude" 1.0 0.025 1.5 0.025 +#pragma parameter pn_color_amplitude "Color Carrier Amplitude" 1.0 0.025 10 0.025 #pragma parameter pn_color_freq "Color Carrier Frequency (MHz)" 3.5795454 3.47954 3.67954 0.00001 #pragma parameter pn_color_init_offset "Color Carrier Initial Offset" 0.1 0 1 0.02 #pragma parameter pn_color_line_offset "Color Carrier Per-Line Offset" 0 0 1 0.02 #pragma parameter pn_color_screen_offset "Color Carrier Per-Frame Offset" 0 0 1 0.02 +#pragma parameter pn_color_screen_offset_modulo "Color Carrier Frame Count Modulo (1 = infinity)" 1 1 10 1 + +#pragma parameter pn_comment_connection "=== Video cable type (including console and CRT mods) ===" 0 0 0 1 +#pragma parameter pn_connection_type "Cable: RF | Composite | S-Video mod | No Artifacts" 0 -1 2 1 +#pragma parameter pn_comment_nes_connections "= NES only supports composite due to hardware limitations =" 0 0 0 1 +#pragma parameter pn_comment_svideo_info "= With the S-Video mod, try reducing the console's filters =" 0 0 0 1 + +#pragma parameter pn_comment_noise "=== RF noise settings (requires Cable=-1) ===" 0 0 0 1 +#pragma parameter pn_noise_rand_offset "Noise random seed" 262 0 999 1 +#pragma parameter pn_noise_min_rate "Noise minimum rate" 50 1 400 2 +#pragma parameter pn_noise_max_rate "Noise maximum rate" 200 1 4000 2 +#pragma parameter pn_noise_counter "Amount of sine waves" 100 5 2000 5 +#pragma parameter pn_noise_severity "Noise overall severity factor" 1.0 0.0 10.0 0.025 #pragma parameter pn_comment_modulator_std "=== Console video standard (Most are Rec. 601) ===" 0 0 0 1 #pragma parameter pn_modulator_std "Modulator standard: Rec. 601 | Rec. 709" 0 0 1 1 #pragma parameter pn_comment_mod_luma_lowpass "=== (Not NES) Console's filtering luma/chroma before adding ===" 0 0 0 1 -#pragma parameter pn_modulator_luma_filter_type "Mod. Luma Lowpass Type" 0 -1 3 1 -#pragma parameter pn_modulator_luma_filter_width "Mod. Luma Lowpass (0) Sinc Half-Width" 9 0 30 0.25 -#pragma parameter pn_modulator_luma_filter_level "Mod. Luma Lowpass (0) Sinc Cutoff Frequency (MHz)" 3.05 0.5 40.0 0.025 -#pragma parameter pn_modulator_luma_radius "Mod. Luma Lowpass (1) Gaussian Blur Radius" 3.0 0.1 15.0 0.1 -#pragma parameter pn_modulator_luma_sigma "Mod. Luma Lowpass (1) Gaussian Blur Sigma" 1.5 0.1 15.0 0.01 -#pragma parameter pn_modulator_luma_res "Mod. Luma Lowpass (2) GTU Signal Res Y" 180 10 800 2 -#pragma parameter pn_modulator_luma_eq_f_lo "Mod. Luma 3 band eq (3) Low freq (MHz)" 1.5 0.025 5.5 0.025 -#pragma parameter pn_modulator_luma_eq_f_hi "Mod. Luma 3 band eq (3) Hi freq (MHz)" 3.0 0.025 5.5 0.025 -#pragma parameter pn_modulator_luma_eq_g_lo "Mod. Luma 3 band eq (3) Low gain" 1.0 0.025 1.0 0.025 -#pragma parameter pn_modulator_luma_eq_g_mid "Mod. Luma 3 band eq (3) Mid gain" 0.125 0.025 1.0 0.025 -#pragma parameter pn_modulator_luma_eq_g_hi "Mod. Luma 3 band eq (3) High gain" 0.15 0.025 1.0 0.025 -#pragma parameter pn_modulator_luma_eq_off "Mod. Luma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 -#pragma parameter pn_modulator_luma_eq_dist "Mod. Luma 3 band eq (3) max distance (% screen)" 20 5 40.0 0.25 +#pragma parameter pn_modulator_luma_filter_type "Mod. Luma Lowpass Type" 0 -1 2 1 +#pragma parameter pn_modulator_luma_filter_width "Mod. Luma Lowpass (Type 0) Sinc Half-Width" 3 0 30 0.25 +#pragma parameter pn_modulator_luma_filter_level "Mod. Luma Lowpass (Type 0) Sinc Cutoff Frequency (MHz)" 2.70 0.5 40.0 0.025 +#pragma parameter pn_modulator_luma_radius "Mod. Luma Lowpass (Type 1) Gaussian Blur Radius" 3.0 0.1 15.0 0.1 +#pragma parameter pn_modulator_luma_sigma "Mod. Luma Lowpass (Type 1) Gaussian Blur Sigma" 1.5 0.1 15.0 0.01 +#pragma parameter pn_modulator_luma_res "Mod. Luma Lowpass (Type 2) GTU Signal Res Y" 180 10 800 2 +//#pragma parameter pn_modulator_luma_eq_f_lo "Mod. Luma 3 band eq (3) Low freq (MHz)" 1.5 0.025 5.5 0.025 +//#pragma parameter pn_modulator_luma_eq_f_hi "Mod. Luma 3 band eq (3) Hi freq (MHz)" 3.0 0.025 5.5 0.025 +//#pragma parameter pn_modulator_luma_eq_g_lo "Mod. Luma 3 band eq (3) Low gain" 1.0 0.025 1.0 0.025 +//#pragma parameter pn_modulator_luma_eq_g_mid "Mod. Luma 3 band eq (3) Mid gain" 0.125 0.025 1.0 0.025 +//#pragma parameter pn_modulator_luma_eq_g_hi "Mod. Luma 3 band eq (3) High gain" 0.15 0.025 1.0 0.025 +//#pragma parameter pn_modulator_luma_eq_off "Mod. Luma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 +//#pragma parameter pn_modulator_luma_eq_dist "Mod. Luma 3 band eq (3) max distance (% screen)" 20 5 40.0 0.25 -#pragma parameter pn_modulator_chroma_filter_type "Mod. Chroma Bandpass Type" 0 -1 1 1 -#pragma parameter pn_modulator_chroma_filter_width "Mod. Chroma Bandpass (0) Half-Width" 5 0 30 0.25 -#pragma parameter pn_modulator_chroma_filter_level "Mod. Chroma Bandpass (0) Low Frequency (MHz)" 3.55 0.5 6.0 0.025 -#pragma parameter pn_modulator_chroma_filter_level_diff "Mod. Chroma Bandpass (0) High Frequency (MHz)" 3.6 0.5 6.0 0.025 -#pragma parameter pn_modulator_chroma_eq_f_lo "Mod. Chroma 3 band eq (1) Low freq (MHz)" 3.2 0.025 5.5 0.025 -#pragma parameter pn_modulator_chroma_eq_f_hi "Mod. Chroma 3 band eq (1) Hi freq (MHz)" 4.5 0.025 5.5 0.025 -#pragma parameter pn_modulator_chroma_eq_g_lo "Mod. Chroma 3 band eq (1) Low gain" 0.1 0.025 1.0 0.025 -#pragma parameter pn_modulator_chroma_eq_g_mid "Mod. Chroma 3 band eq (1) Mid gain" 1.0 0.025 1.0 0.025 -#pragma parameter pn_modulator_chroma_eq_g_hi "Mod. Chroma 3 band eq (1) High gain" 0.1 0.025 1.0 0.025 -#pragma parameter pn_modulator_chroma_eq_off "Mod. Chroma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 -#pragma parameter pn_modulator_chroma_eq_dist "Mod. Chroma 3 band eq (1) max distance (% screen)" 20 5 40.0 0.25 +#pragma parameter pn_modulator_chroma_filter_type "Mod. Chroma Bandpass Type" 0 -1 0 1 +#pragma parameter pn_modulator_chroma_filter_width "Mod. Chroma Bandpass (Type 0) Half-Width" 3 0 30 0.25 +#pragma parameter pn_modulator_chroma_filter_level "Mod. Chroma Bandpass (Type 0) Low Frequency (MHz)" 3.55 0.5 6.0 0.025 +#pragma parameter pn_modulator_chroma_filter_level_diff "Mod. Chroma Bandpass (Type 0) High Frequency (MHz)" 3.6 0.5 6.0 0.025 +//#pragma parameter pn_modulator_chroma_eq_f_lo "Mod. Chroma 3 band eq (1) Low freq (MHz)" 3.2 0.025 5.5 0.025 +//#pragma parameter pn_modulator_chroma_eq_f_hi "Mod. Chroma 3 band eq (1) Hi freq (MHz)" 4.5 0.025 5.5 0.025 +//#pragma parameter pn_modulator_chroma_eq_g_lo "Mod. Chroma 3 band eq (1) Low gain" 0.1 0.025 1.0 0.025 +//#pragma parameter pn_modulator_chroma_eq_g_mid "Mod. Chroma 3 band eq (1) Mid gain" 1.0 0.025 1.0 0.025 +//#pragma parameter pn_modulator_chroma_eq_g_hi "Mod. Chroma 3 band eq (1) High gain" 0.1 0.025 1.0 0.025 +//#pragma parameter pn_modulator_chroma_eq_off "Mod. Chroma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 +//#pragma parameter pn_modulator_chroma_eq_dist "Mod. Chroma 3 band eq (1) max distance (% screen)" 20 5 40.0 0.25 #pragma parameter pn_comment_demod_filter "=== CRT Chroma/Luma Separation (a.k.a. comb filter) ===" 0 0 0 1 -#pragma parameter pn_demodulator_luma_filter_type "Demodulator Luma Lowpass Type" 0 -1 4 1 -#pragma parameter pn_demodulator_luma_filter_width "Demod. Luma Lowpass (0) Sinc Half-Width" 5 0 30 0.25 -#pragma parameter pn_demodulator_luma_filter_level "Demod. Luma Lowpass (0) Sinc Cutoff Frequency (MHz)" 2.35 0.5 4.0 0.025 -#pragma parameter pn_demodulator_luma_radius "Demod. Luma Lowpass (1) Gaussian Blur Radius" 4.5 0.1 15.0 0.1 -#pragma parameter pn_demodulator_luma_sigma "Demod. Luma Lowpass (1) Gaussian Blur Sigma" 1.9 0.1 15.0 0.01 -#pragma parameter pn_demodulator_luma_res "Demod. Luma Lowpass (2) GTU Signal Res Y" 180 10 800 2 -#pragma parameter pn_demodulator_luma_eq_f_lo "Demod. Luma 3 band eq (3) Low freq (MHz)" 1.5 0.025 5.5 0.025 -#pragma parameter pn_demodulator_luma_eq_f_hi "Demod. Luma 3 band eq (3) Hi freq (MHz)" 3.0 0.025 5.5 0.025 -#pragma parameter pn_demodulator_luma_eq_g_lo "Demod. Luma 3 band eq (3) Low gain" 1.0 0.025 1.0 0.025 -#pragma parameter pn_demodulator_luma_eq_g_mid "Demod. Luma 3 band eq (3) Mid gain" 0.125 0.025 1.0 0.025 -#pragma parameter pn_demodulator_luma_eq_g_hi "Demod. Luma 3 band eq (3) High gain" 0.15 0.025 1.0 0.025 -#pragma parameter pn_demodulator_luma_eq_off "Demod. Luma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 -#pragma parameter pn_demodulator_luma_eq_dist "Demod. Luma 3 band eq (3) max distance (% screen)" 20 5 40.0 0.25 +#pragma parameter pn_demodulator_luma_filter_type "Demodulator Luma Filter Type" 0 -1 6 1 +#pragma parameter pn_demodulator_luma_filter_width "(Type 0) Lowpass Sinc Window Half-Width" 5 0 30 0.25 +#pragma parameter pn_demodulator_luma_filter_level "(Type 0) Lowpass Sinc Cutoff Frequency (MHz)" 2.15 0.5 4.0 0.025 +#pragma parameter pn_demodulator_luma_radius "(Type 1) Gaussian Blur Radius" 4.5 0.1 15.0 0.1 +#pragma parameter pn_demodulator_luma_sigma "(Type 1) Gaussian Blur Sigma" 1.9 0.1 15.0 0.01 +#pragma parameter pn_demodulator_luma_res "(Type 2) GTU Signal Res Y" 180 10 800 2 +#pragma parameter pn_demodulator_luma_avg_comment "(Type 3) (No settings) Full-phase Integral" 0 0 0 1 +#pragma parameter pn_demodulator_luma_echo_comment "(Type 4) (No settings) Half-phase two point avg." 0 0 0 1 +#pragma parameter pn_demodulator_luma_comb2_comment "(Type 5) (No settings) Two line comb filter" 0 0 0 1 +//#pragma parameter pn_demodulator_luma_eq_f_lo "Demod. Luma 3 band eq (3) Low freq (MHz)" 1.5 0.025 5.5 0.025 +//#pragma parameter pn_demodulator_luma_eq_f_hi "Demod. Luma 3 band eq (3) Hi freq (MHz)" 3.0 0.025 5.5 0.025 +//#pragma parameter pn_demodulator_luma_eq_g_lo "Demod. Luma 3 band eq (3) Low gain" 1.0 0.025 1.0 0.025 +//#pragma parameter pn_demodulator_luma_eq_g_mid "Demod. Luma 3 band eq (3) Mid gain" 0.125 0.025 1.0 0.025 +//#pragma parameter pn_demodulator_luma_eq_g_hi "Demod. Luma 3 band eq (3) High gain" 0.15 0.025 1.0 0.025 +//#pragma parameter pn_demodulator_luma_eq_off "Demod. Luma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 +//#pragma parameter pn_demodulator_luma_eq_dist "Demod. Luma 3 band eq (3) max distance (% screen)" 20 5 40.0 0.25 -#pragma parameter pn_demodulator_chroma_filter_type "Demod. Chroma Bandpass Type" 0 -1 2 1 -#pragma parameter pn_demodulator_chroma_filter_width "Demod. Chroma Bandpass (0) Half-Width" 5 0 30 0.25 -#pragma parameter pn_demodulator_chroma_filter_level "Demod. Chroma Bandpass (0) Low Frequency (MHz)" 3.55 0.5 6.0 0.025 -#pragma parameter pn_demodulator_chroma_filter_level_diff "Demod. Chroma Bandpass (0) High Frequency (MHz)" 3.6 0.5 6.0 0.025 -#pragma parameter pn_demodulator_chroma_eq_f_lo "Demod. Chroma 3 band eq (1) Low freq (MHz)" 3.2 0.025 5.5 0.025 -#pragma parameter pn_demodulator_chroma_eq_f_hi "Demod. Chroma 3 band eq (1) Hi freq (MHz)" 4.5 0.025 5.5 0.025 -#pragma parameter pn_demodulator_chroma_eq_g_lo "Demod. Chroma 3 band eq (1) Low gain" 0.1 0.025 1.0 0.025 -#pragma parameter pn_demodulator_chroma_eq_g_mid "Demod. Chroma 3 band eq (1) Mid gain" 1.0 0.025 1.0 0.025 -#pragma parameter pn_demodulator_chroma_eq_g_hi "Demod. Chroma 3 band eq (1) High gain" 0.1 0.025 1.0 0.025 -#pragma parameter pn_demodulator_chroma_eq_off "Demod. Chroma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 -#pragma parameter pn_demodulator_chroma_eq_dist "Demod. Chroma 3 band eq (1) max distance (% screen)" 20 5 40.0 0.25 +#pragma parameter pn_demodulator_chroma_filter_type "Demod. Chroma Filter Type" 0 -1 3 1 +#pragma parameter pn_demodulator_chroma_filter_width "Demod. Chroma Bandpass (Type 0) Half-Width" 5 0 30 0.25 +#pragma parameter pn_demodulator_chroma_filter_level "Demod. Chroma Bandpass (Type 0) Low Frequency (MHz)" 3.55 0.5 6.0 0.025 +#pragma parameter pn_demodulator_chroma_filter_level_diff "Demod. Chroma Bandpass (Type 0) High Frequency (MHz)" 3.6 0.5 6.0 0.025 +#pragma parameter pn_demodulator_chroma_filter_echo_comment "(Type 1) (No settings) Half-phase two point avg." 0 0 0 1 +#pragma parameter pn_demodulator_chroma_filter_comb2_comment "(Type 2) (No settings) Two line comb filter" 0 0 0 1 +//#pragma parameter pn_demodulator_chroma_eq_f_lo "Demod. Chroma 3 band eq (1) Low freq (MHz)" 3.2 0.025 5.5 0.025 +//#pragma parameter pn_demodulator_chroma_eq_f_hi "Demod. Chroma 3 band eq (1) Hi freq (MHz)" 4.5 0.025 5.5 0.025 +//#pragma parameter pn_demodulator_chroma_eq_g_lo "Demod. Chroma 3 band eq (1) Low gain" 0.1 0.025 1.0 0.025 +//#pragma parameter pn_demodulator_chroma_eq_g_mid "Demod. Chroma 3 band eq (1) Mid gain" 1.0 0.025 1.0 0.025 +//#pragma parameter pn_demodulator_chroma_eq_g_hi "Demod. Chroma 3 band eq (1) High gain" 0.1 0.025 1.0 0.025 +//#pragma parameter pn_demodulator_chroma_eq_off "Demod. Chroma 3 band eq (3) offset (pixels)" 0.5 0 4 0.0125 +//#pragma parameter pn_demodulator_chroma_eq_dist "Demod. Chroma 3 band eq (1) max distance (% screen)" 20 5 40.0 0.25 -//#pragma parameter pn_comment_uv_filter "=== CRT U and V lowpass - Little need to change this ===" 0 0 0 1 -//#pragma parameter pn_demodulator_uv_filter_width "Demodulator U and V Lowpass Half-Width" 6 0 30 0.25 -//#pragma parameter pn_demodulator_uv_filter_level "Demodulator U and V Lowpass Cutoff Frequency (MHz)" 4 0.5 40.0 0.025 +#pragma parameter pn_demodulator_uv_filter_type "B-Y R-Y Filter Type" 0 -1 0 1 +#pragma parameter pn_demodulator_uv_filter_width "B-Y R-Y Filter Width" 1 1 10 1 #pragma parameter pn_comment_demod_std "=== CRT Jungle Chip: US are reverse chrono order thru 1990s ===" 0 0 0 1 -#pragma parameter pn_demodulator_std "Demodulator: Rec. 601, Rec. 709, 2*JP, 5*US, Custom" 8 0 9 1 +#pragma parameter pn_demodulator_std "R-Y/G-Y/B-Y formula: Rec. 601, Rec. 709, 2*JP, 5*US, Custom" 8 0 9 1 +#pragma parameter pn_demodulator_std_defaults "Auto-sync Tint/Color: Off; Avg. Bars; Match red; Eyeballed" 2 -1 2 1 #pragma parameter pn_demodulator_r_off "Custom R-Y offset" 112 90 130 1 #pragma parameter pn_demodulator_r_amp "Custom R-Y gain" 0.84 0.0 1.00 0.01 #pragma parameter pn_demodulator_g_off "Custom G-Y offset" 235 200 300 1 #pragma parameter pn_demodulator_g_amp "Custom G-Y gain" 0.33 0.0 1.00 0.01 +#pragma parameter pn_commment_b "= (B-Y offset=0, gain=1.0) =" 0 0 0 1 #pragma parameter pn_comment_knobs "=== CRT End User Color Knobs ===" 0 0 0 1 #pragma parameter pn_knob_contrast "CRT Contrast (Picture)" 0.8 0 2 0.025 @@ -217,14 +258,14 @@ layout(std140, set = 0, binding = 0) uniform UBO #pragma parameter pn_comment_smearing "=== Red (and some blue) smearing to the right ===" 0 0 0 1 #pragma parameter pn_rgb_smear_enable "Enable Smearing (Should be off unless your CRT is wearing out)" 0 0 1 1 #pragma parameter pn_rgb_smear_clamp "RGB Clamp Level (any value above this becomes equal to this)" 1.5 1.0 3.0 0.05 -#pragma parameter pn_rgb_smear_rate "RGB Smear Level (after clamping, anything above this smears)" 1.0 1.0 3.0 0.05 +#pragma parameter pn_rgb_smear_rate "RGB Smear Level (after clamping, anything above this smears)" 1.15 1.0 3.0 0.05 #pragma parameter pn_rgb_smear_limit "Max Smear Detection Distance (% of screen)" 30 0 100 0.25 #pragma parameter pn_rgb_smear_res "Smear Resolution (Higher is more intense but more accurate)" 1 1 3 1 #pragma parameter pn_comment_phosphor "=== CRT Gamma and Phosphor Gamut ===" 0 0 0 1 #pragma parameter pn_gamma_type "Gamma: BT.1886 + Grade black lift fix | BT.1886 | Power | sRGB" 1 0 3 1 #pragma parameter pn_g_CRT_l "Black lift fix approximate gamma" 2.50 2.30 2.60 0.01 -#pragma parameter pn_power_gamma "Power gamma" 2.4 2.2 3.0 0.01 +#pragma parameter pn_power_gamma "Power gamma" 2.4 1.5 3.5 0.01 #endif // __patchy_ntsc_inc_params_inc__ diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang new file mode 100644 index 00000000..d7a7b0ec --- /dev/null +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-noise.slang @@ -0,0 +1,100 @@ +#version 450 + +/* +Patchy NTSC +Copyright (C) 2024 Patchy68k/PlainOldPants + +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, version 3. + +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, see . + +*/ + +// This pass filters the luma signal and adds the chroma signal to get the composite signal. + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + vec4 FinalViewportSize; +} params; + +// Includes constants, global, and all parameters +#include "patchy-ntsc-inc-params.inc" + +#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; + +// Includes lowpass and bandpass functions +#include "patchy-ntsc-inc-filters.inc" + +float getNoise() { + // Compensate for cropped overscan, assuming equal crop on both sides. + // Current position in pixels, both x and y being decimal numbers. + vec2 pixelCoord = realPixelCoord(); + float noise = 0.0; + + // Add RF noise + + float offsetXPart = pixelCoord.x * 47.7 / global.pn_scanline_dur * 256.0 / 341.25; + int offsetYPart = int(pixelCoord.y + 0.5); + int offsetFramePart = int(params.FrameCount + 0.5); + float offsetFull = offsetXPart + offsetYPart + 262 * offsetFramePart; + + // https://en.wikipedia.org/wiki/Linear_congruential_generator + const uint modulus = 0x7fffffff; // Use & instead of % for better performance + const uint multiplier = 48271; + const uint increment = 0; + const uint truncation = 8; + uint randOffset = int(global.pn_noise_rand_offset / 1000.0 * modulus + 0.5); +#define RNG (((randOffset = (randOffset * multiplier + increment) & modulus) >> truncation) / float(modulus >> truncation)) + + float minRate = global.pn_noise_min_rate; + float maxRate = global.pn_noise_max_rate; + + for(int i = 0; i < int(global.pn_noise_counter + 0.5); i++) { + float rate = RNG * (maxRate - minRate) + minRate; + + // WRONG way to do this part. If you do this, the float that we pass into the sin() function will get very big, and our noise will start disappearing over time and suddenly reappearing in a 1000-frame cycle. +// noise += (randoms[(i * 2 + randOffset) % 1000] * (1.0 - minAmp) + minAmp) * sin(offsetFull * rate); + + // New way to avoid getting too large of a number in the sin() function. +// float overAmt = mod(rate, 2 * pi); + float overAmt = rate; + float overAmtFrame = overAmt * 262 - floor(overAmt * (262 / (2 * pi))) * (2 * pi); +// float overAmtFrame = overAmt * 262; + + noise += sin(offsetXPart * rate + offsetYPart * overAmt + offsetFramePart * overAmtFrame) * global.pn_noise_severity / global.pn_noise_counter; + } + +#undef RNG + + return noise; +} + +void main() +{ + vec4 orig = texture(Source, vTexCoord); + if(global.pn_connection_type < -0.5) + orig.r += getNoise(); + + FragColor = orig; +} + diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass3.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass3.slang deleted file mode 100644 index 0c07a697..00000000 --- a/ntsc/shaders/patchy-ntsc/patchy-ntsc-pass3.slang +++ /dev/null @@ -1,120 +0,0 @@ -#version 450 - -/* -Patchy NTSC -Copyright (C) 2024 Patchy68k/PlainOldPants - -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, version 3. - -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, see . - -*/ - -// This pass separates luma and chroma from each other and demodulates chroma into B-Y and R-Y, using B-Y and R-Y offsets and multipliers taken from real jungle chips, taking into account the Tint knob/OSD setting. - -layout(push_constant) uniform Push -{ - vec4 SourceSize; - vec4 OriginalSize; - vec4 OutputSize; - uint FrameCount; - vec4 FinalViewportSize; -} params; - -// Includes constants, global, and all parameters -#include "patchy-ntsc-inc-params.inc" - -#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; - -// Includes lowpass and bandpass functions -#include "patchy-ntsc-inc-filters.inc" - -void main() -{ - float luma = 0.0; - float chroma = 0.0; - - float phase = getPhase(); - - // Filter the signal to isolate luma - luma += lowpassPickable(global.pn_demodulator_luma_filter_type, - global.pn_demodulator_luma_filter_width, global.pn_demodulator_luma_filter_level, - global.pn_demodulator_luma_radius, global.pn_demodulator_luma_sigma, - global.pn_demodulator_luma_res, - global.pn_demodulator_luma_eq_f_lo, global.pn_demodulator_luma_eq_f_hi, - global.pn_demodulator_luma_eq_g_lo, global.pn_demodulator_luma_eq_g_mid, global.pn_demodulator_luma_eq_g_hi, - global.pn_demodulator_luma_eq_dist, global.pn_demodulator_luma_eq_off).r; - - - // Filter the signal to isolate chroma - chroma += bandpassPickable(global.pn_demodulator_chroma_filter_type, - global.pn_demodulator_chroma_filter_width, global.pn_demodulator_chroma_filter_level, global.pn_demodulator_chroma_filter_level_diff, - global.pn_demodulator_chroma_eq_f_lo, global.pn_demodulator_chroma_eq_f_hi, - global.pn_demodulator_chroma_eq_g_lo, global.pn_demodulator_chroma_eq_g_mid, global.pn_demodulator_chroma_eq_g_hi, - global.pn_demodulator_chroma_eq_dist, global.pn_demodulator_chroma_eq_dist, 0 // 0 means the chroma signal is in the R channel of the vector - ).r; - - if(global.pn_nes_enable < 0.5) { - luma *= global.pn_color_amplitude; // Correction by the color carrier amplitude - } - - float tint = global.pn_knob_tint * pi / 180.0; - - if(global.pn_nes_enable > 0.5 && global.pn_nes_real_capture < 0.5) { - // From lidnariq's measurements at http://forums.nesdev.org/viewtopic.php?p=159266#p159266 - // 30 = colorburst max - // -23 = colorburst min - // 110 = white - // 0 = black - float nesAmp = (30.0 - -23.0) / 2.0 / (110.0 - 0.0); - float stdAmp = 0.2 * 100.0 / 110.0; // Standard colorburst amplitude in NTSC - chroma *= stdAmp / nesAmp; - float saturatedLevels[4] = {((0.228 + 0.616) / 2. - 0.312) / (1.100 - 0.312), ((0.312 + 0.840) / 2. - 0.312) / (1.100 - 0.312), ((0.552 + 1.100) / 2. - 0.312) / (1.100 - 0.312), ((0.880 + 1.100) / 2. - 0.312) / (1.100 - 0.312)}; // Calculated from DAC.slang from GTU-famicom - - int satLevelI = -1; // Integer part. Can be 0, 1, or 2. If the level is 3 or higher, it is represented as 2 with a fractional part greater than 1. - float satLevelF; // Fractional part. If I=0, -1 1.0); - - // Skew amounts taken from Drag's NES palette generator. - // I remember seeing these on the nesdev forums somewhere too. - // The change is done here instead of in the video signal for better precision. - float skew; - if(global.pn_nes_hue_skew < 0.5) { - // 2C02G - skew = -5.0 / 180.0 * pi; // -5 degrees - } else { - // 2C02E - skew = -2.5 / 180.0 * pi; // -2.5 degrees - } - - tint += (3.0 - (satLevelI + satLevelF)) * skew; - } - - vec2 bmyRmy = uvDemodPickable(global.pn_demodulator_chroma_filter_type, chroma, phase, tint); - - FragColor = vec4(luma, bmyRmy, 1); -} - diff --git a/ntsc/shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang b/ntsc/shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang new file mode 100644 index 00000000..8027e7c5 --- /dev/null +++ b/ntsc/shaders/patchy-ntsc/patchy-ntsc-separate-y-c.slang @@ -0,0 +1,138 @@ +#version 450 + +/* +Patchy NTSC +Copyright (C) 2024 Patchy68k/PlainOldPants + +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, version 3. + +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, see . + +*/ + +// This pass separates luma and chroma from each other and demodulates chroma into B-Y and R-Y, using B-Y and R-Y offsets and multipliers taken from real jungle chips, taking into account the Tint knob/OSD setting. + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; + vec4 FinalViewportSize; +} params; + +// Includes constants, global, and all parameters +#include "patchy-ntsc-inc-params.inc" + +#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; + +// Includes lowpass and bandpass functions +#include "patchy-ntsc-inc-filters.inc" + +void main() +{ + if(global.pn_connection_type > 1.5) { // No Artifacts + + mat3x2 yuvAxisPts = yuvAxisPointsSynced(); + + float xr, yr, xg, yg, xb, yb; + xr = yuvAxisPts[0][1] * cos(yuvAxisPts[0][0]); + yr = yuvAxisPts[0][1] * sin(yuvAxisPts[0][0]); + xg = yuvAxisPts[1][1] * cos(yuvAxisPts[1][0]); + yg = yuvAxisPts[1][1] * sin(yuvAxisPts[1][0]); + xb = yuvAxisPts[2][1] * cos(yuvAxisPts[2][0]); + yb = yuvAxisPts[2][1] * sin(yuvAxisPts[2][0]); + + // YUV to Y B-Y R-Y matrix + mat3 toYBmyRmy = mat3(1, 0, 0, + 0, xb, xr, + 0, yb, yr); + + FragColor = vec4(toYBmyRmy * texture(Source, vTexCoord).rgb, 1.0); + } else { + float luma = 0.0; + float chroma = 0.0; + + float phase = getPhase(); + + if(global.pn_connection_type < 0.5) { // Composite + // Filter the signal to isolate luma + luma += lowpassPickable(global.pn_demodulator_luma_filter_type, + global.pn_demodulator_luma_filter_width, global.pn_demodulator_luma_filter_level, + global.pn_demodulator_luma_radius, global.pn_demodulator_luma_sigma, + global.pn_demodulator_luma_res).r; + // Filter the signal to isolate chroma + chroma += bandpassPickable(global.pn_demodulator_chroma_filter_type, + global.pn_demodulator_chroma_filter_width, global.pn_demodulator_chroma_filter_level, global.pn_demodulator_chroma_filter_level_diff).r; + } else { + // S-Video + vec2 sig = texture(Source, vTexCoord).rg; + luma = sig.g; + chroma = sig.r; + } + + if(global.pn_nes_enable < 0.5) { // Composite & S-Video + luma *= global.pn_color_amplitude; // Correction by the color carrier amplitude + } + + float tint = global.pn_knob_tint * pi / 180.0; + + if(global.pn_nes_enable > 0.5 && global.pn_nes_real_capture < 0.5) { + // From lidnariq's measurements at http://forums.nesdev.org/viewtopic.php?p=159266#p159266 + // 30 = colorburst max + // -23 = colorburst min + // 110 = white + // 0 = black + float nesAmp = (30.0 - -23.0) / 2.0 / (110.0 - 0.0); + float stdAmp = 0.2 * 100.0 / 110.0; // Standard colorburst amplitude in NTSC + chroma *= stdAmp / nesAmp; + float saturatedLevels[4] = {((0.228 + 0.616) / 2. - 0.312) / (1.100 - 0.312), ((0.312 + 0.840) / 2. - 0.312) / (1.100 - 0.312), ((0.552 + 1.100) / 2. - 0.312) / (1.100 - 0.312), ((0.880 + 1.100) / 2. - 0.312) / (1.100 - 0.312)}; // Calculated from DAC.slang from GTU-famicom + + int satLevelI = -1; // Integer part. Can be 0, 1, or 2. If the level is 3 or higher, it is represented as 2 with a fractional part greater than 1. + float satLevelF; // Fractional part. If I=0, -1 1.0); + + // Skew amounts taken from Drag's NES palette generator. + // I remember seeing these on the nesdev forums somewhere too. + // The change is done here instead of in the video signal for better precision. + float skew; + if(global.pn_nes_hue_skew < 0.5) { + // 2C02G + skew = -5.0 / 180.0 * pi; // -5 degrees + } else { + // 2C02E + skew = -2.5 / 180.0 * pi; // -2.5 degrees + } + + tint += (3.0 - (satLevelI + satLevelF)) * skew; + } + + vec2 bmyRmy = uvDemodPickable(global.pn_demodulator_chroma_filter_type, chroma, phase, tint); + + FragColor = vec4(luma, bmyRmy, 1); + } +} + diff --git a/ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slangp b/ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slangp deleted file mode 100644 index 8672f01e..00000000 --- a/ntsc/shaders/patchy-ntsc/trilinearLUT-switchable.slangp +++ /dev/null @@ -1,28 +0,0 @@ -shaders = 1 - -shader0 = "trilinearLUT-switchable.slang" -filter_linear0 = false -float_framebuffer0 = true - -textures = "PhosphorSamplerLUT1;PhosphorSamplerLUT2;PhosphorSamplerLUT3;PhosphorSamplerLUT4" - -PhosphorSamplerLUT1 = "P22_80s_D65.png" -PhosphorSamplerLUT1_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT1_mipmap = "false" -PhosphorSamplerLUT1_linear = "false" - -PhosphorSamplerLUT2 = "P22_90s_D65.png" -PhosphorSamplerLUT2_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT2_mipmap = "false" -PhosphorSamplerLUT2_linear = "false" - -PhosphorSamplerLUT3 = "P22_J_D65.png" -PhosphorSamplerLUT3_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT3_mipmap = "false" -PhosphorSamplerLUT3_linear = "false" - -PhosphorSamplerLUT4 = "TrinitronP22_D65.png" -PhosphorSamplerLUT4_wrap_mode = "clamp_to_border" -PhosphorSamplerLUT4_mipmap = "false" -PhosphorSamplerLUT4_linear = "false" - diff --git a/ntsc/shaders/patchy-ntsc/trilinearLUT.slang b/ntsc/shaders/patchy-ntsc/trilinearLUT.slang deleted file mode 100644 index 3b06b9c0..00000000 --- a/ntsc/shaders/patchy-ntsc/trilinearLUT.slang +++ /dev/null @@ -1,113 +0,0 @@ -#version 450 - -/* - Trilinear LUT - Slightly more accurate, but slower, than reshade/shaders/LUT/LUT.slang - Compatible with the images in that directory, or this one. - Expects n^2 x n dimensions with green on the vertical axis, red on the minor horizontal axis, blue on the major horizontal axis. - Linear filtering for the LUT image should be FALSE. (Reshade's LUT expects true.) - - Copyright (C) 2023 ChthonVII - - 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; -} params; - -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; - 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 SamplerLUT; - -#define saturate(c) clamp(c, 0.0, 1.0) - -void main() -{ - vec2 LUT_Size = textureSize(SamplerLUT, 0); - vec3 imgColor = texture(Source, vTexCoord.xy).rgb; - - // find rgb values and their ceilings and floors on a scale on 0-63 - vec3 temp = imgColor.rgb * (vec3(LUT_Size.y - 1.0)); - vec3 floors = floor(imgColor.rgb * (vec3(LUT_Size.y - 1.0))); - vec3 ceils = ceil(imgColor.rgb * (vec3(LUT_Size.y - 1.0))); - - // how close are we to the ceiling on a scale of 0-1? - vec3 weights = saturate(temp - floors); - - // retroarch can't correctly sample a 1.0 coordinate - // so we are going to add a just-under-half-step offset to red and green, then increase their divisors by 1 - // This should get us a slightly lower coordinate within the same pixel - // This is where reshade's LUT is slightly inaccurate because it's taking a linearly filtered sample from the wrong spot. - floors = floors + vec3(0.4999, 0.4999, 0.0); - ceils = ceils + vec3(0.4999, 0.4999, 0.0); - floors = floors / vec3((LUT_Size.y * LUT_Size.y), LUT_Size.y, LUT_Size.y); - ceils = ceils / vec3((LUT_Size.y * LUT_Size.y), LUT_Size.y, LUT_Size.y); - - // this would be correct, if retroarch sampled correctly. - //floors = saturate(floors / vec3(((LUT_Size.y * LUT_Size.y) - 1.0), LUT_Size.y - 1.0, LUT_Size.y)); - //ceils = saturate(ceils / vec3(((LUT_Size.y * LUT_Size.y) - 1.0), LUT_Size.y - 1.0, LUT_Size.y)); - - // take 8 samples - vec3 RfGfBf = (texture(SamplerLUT, vec2(floors.b + floors.r, floors.g))).xyz; - vec3 RfGfBc = (texture(SamplerLUT, vec2(ceils.b + floors.r, floors.g))).xyz; - vec3 RfGcBf = (texture(SamplerLUT, vec2(floors.b + floors.r, ceils.g))).xyz; - vec3 RfGcBc = (texture(SamplerLUT, vec2(ceils.b + floors.r, ceils.g))).xyz; - vec3 RcGfBf = (texture(SamplerLUT, vec2(floors.b + ceils.r, floors.g))).xyz; - vec3 RcGfBc = (texture(SamplerLUT, vec2(ceils.b + ceils.r, floors.g))).xyz; - vec3 RcGcBf = (texture(SamplerLUT, vec2(floors.b + ceils.r, ceils.g))).xyz; - vec3 RcGcBc = (texture(SamplerLUT, vec2(ceils.b + ceils.r, ceils.g))).xyz; - - // merge down to 4 samples along blue axis - vec3 RfGf = saturate(mix(RfGfBf, RfGfBc, vec3(weights.b))); - vec3 RfGc = saturate(mix(RfGcBf, RfGcBc, vec3(weights.b))); - vec3 RcGf = saturate(mix(RcGfBf, RcGfBc, vec3(weights.b))); - vec3 RcGc = saturate(mix(RcGcBf, RcGcBc, vec3(weights.b))); - - // merge down to 2 samples along green axis - vec3 Rf = saturate(mix(RfGf, RfGc, vec3(weights.g))); - vec3 Rc = saturate(mix(RcGf, RcGc, vec3(weights.g))); - - // merge down to one color along red axis - vec3 finalcolor = saturate(mix(Rf, Rc, vec3(weights.r))); - - // output - FragColor = vec4(finalcolor.rgb, 1.0); - -}