update patchy-ntsc shaders and presets (#637)

This commit is contained in:
hunterk 2024-09-17 11:29:08 -05:00 committed by GitHub
parent edd2dd3f77
commit 5ab3c76109
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1587 additions and 565 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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);
}
}
}

View File

@ -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 {

View File

@ -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);
}
}
}

View File

@ -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) {

View File

@ -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 //
//////////////////

View File

@ -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__

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
// 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;
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
// 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<F<1. If I=1, 0<F<1. If I=2, 0<F<2.
do {
if(satLevelI < 0)
satLevelI = 0;
else
satLevelI++;
float satYRemaining = luma - saturatedLevels[satLevelI];
satLevelF = satYRemaining / (saturatedLevels[satLevelI + 1] - saturatedLevels[satLevelI]);
} while(satLevelI < 2 && satLevelF > 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);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
// 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<F<1. If I=1, 0<F<1. If I=2, 0<F<2.
do {
if(satLevelI < 0)
satLevelI = 0;
else
satLevelI++;
float satYRemaining = luma - saturatedLevels[satLevelI];
satLevelF = satYRemaining / (saturatedLevels[satLevelI + 1] - saturatedLevels[satLevelI]);
} while(satLevelI < 2 && satLevelF > 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);
}
}

View File

@ -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"

View File

@ -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);
}