From 4c489de948bf7c1e124d672bb3e5e9c2f7edce41 Mon Sep 17 00:00:00 2001 From: Monroe88 Date: Sun, 7 May 2017 01:49:26 -0500 Subject: [PATCH 1/2] (nes-color-decoder) Backport changes from common-shaders --- misc/nes-color-decoder-alt.slang | 144 ------------------------------ misc/nes-color-decoder-sony.slang | 144 ------------------------------ misc/nes-color-decoder.slang | 138 ++++++++++++++++++++++++++-- 3 files changed, 131 insertions(+), 295 deletions(-) delete mode 100644 misc/nes-color-decoder-alt.slang delete mode 100644 misc/nes-color-decoder-sony.slang diff --git a/misc/nes-color-decoder-alt.slang b/misc/nes-color-decoder-alt.slang deleted file mode 100644 index fe182fd..0000000 --- a/misc/nes-color-decoder-alt.slang +++ /dev/null @@ -1,144 +0,0 @@ -#version 450 - -/* - NES NTSC Color Decoder shader - Ported from Bisqwit's C++ NES Palette Generator - https://forums.nesdev.com/viewtopic.php?p=85060#p85060 - - Use with Nestopia or FCEUmm libretro cores with the palette set to 'raw'. -*/ - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 OutputSize; - vec4 OriginalSize; - vec4 SourceSize; - uint FrameCount; -} global; - -layout(push_constant) uniform Push -{ - float nes_saturation; - float nes_hue; - float nes_contrast; - float nes_brightness; - float nes_gamma; -} params; - -#pragma parameter nes_saturation "Saturation" 1.0 0.0 5.0 0.05 -#pragma parameter nes_hue "Hue" 0.0 -360.0 360.0 1.0 -#pragma parameter nes_contrast "Contrast" 1.0 0.0 2.0 0.05 -#pragma parameter nes_brightness "Brightness" 1.0 0.0 2.0 0.05 -#pragma parameter nes_gamma "Gamma" 1.8 1.0 2.5 0.05 - -#define saturation params.nes_saturation -#define hue params.nes_hue -#define contrast params.nes_contrast -#define brightness params.nes_brightness -#define gamma params.nes_gamma - -bool wave (int p, int color) -{ - return ((color + p + 8) % 12 < 6); -} - -float gammafix (float f) -{ - return f < 0.0 ? 0.0 : pow(f, 2.2 / gamma); -} - -vec3 MakeRGBColor(int emphasis, int level, int color) -{ - float y = 0.0; - float i = 0.0; - float q = 0.0; - - float r = 0.0; - float g = 0.0; - float b = 0.0; - - // Color 0xE and 0xF are black - level = (color < 14) ? level : 1; - - // Voltage levels, relative to synch voltage - float black = 0.518; - float white = 1.962; - float attenuation = 0.746; - const float levels[8] = float[] ( 0.350 , 0.518, 0.962, 1.550, - 1.094, 1.506, 1.962, 1.962); - - float low = levels[level + 4 * int(color == 0)]; - float high = levels[level + 4 * int(color < 13)]; - - // Calculate the luma and chroma by emulating the relevant circuits: - for(int p = 0; p < 12; p++) // 12 clock cycles per pixel. - { - // NES NTSC modulator (square wave between two voltage levels): - float spot = wave(p, color) ? high : low; - - // De-emphasis bits attenuate a part of the signal: - if ((bool(emphasis & 1) && wave(p, 12)) || - (bool(emphasis & 2) && wave(p, 4)) || - (bool(emphasis & 4) && wave(p, 8))) - { - spot *= attenuation; - } - - // Normalize: - float v = (spot - black) / (white - black); - - // Ideal TV NTSC demodulator: - // Apply contrast/brightness - v = (v - 0.5) * contrast + 0.5; - v *= (brightness / 12.0); - - float hue_tweak = hue * 12.0 / 360.0; - - y += v; - i += v * cos((3.141592653 / 6.0) * (p + hue_tweak) ); - q += v * sin((3.141592653 / 6.0) * (p + hue_tweak) ); - - } - - i *= saturation; - q *= saturation; - - // Convert YIQ into RGB according to a commonly used conversion matrix. - r = clamp((1.0 * gammafix(y + 0.956 * i + 0.621 * q)), 0, 1.0); - g = clamp((1.0 * gammafix(y + -0.272 * i + -0.647 * q)), 0, 1.0); - b = clamp((1.0 * gammafix(y + -1.105 * i + 1.702 * q)), 0, 1.0); - - return vec3(r,g,b); -} - -#pragma stage vertex -layout(location = 0) in vec4 Position; -layout(location = 1) in vec2 TexCoord; -layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out float colorPhase; - -void main() -{ - gl_Position = global.MVP * Position; - vTexCoord = TexCoord; -} - -#pragma stage fragment -layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in float colorPhase; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -void main() -{ - vec4 c = texture(Source, vTexCoord.xy); - - // Extract the chroma, level, and emphasis from the normalized RGB triplet - int color = int(floor((c.r * 15.0) + 0.5)); - int level = int(floor((c.g * 3.0) + 0.5)); - int emphasis = int(floor((c.b * 7.0) + 0.5)); - - vec3 out_color = MakeRGBColor(emphasis, level, color); - FragColor = vec4(out_color, 1.0); -} diff --git a/misc/nes-color-decoder-sony.slang b/misc/nes-color-decoder-sony.slang deleted file mode 100644 index e121e70..0000000 --- a/misc/nes-color-decoder-sony.slang +++ /dev/null @@ -1,144 +0,0 @@ -#version 450 - -/* - NES NTSC Color Decoder shader - Ported from Bisqwit's C++ NES Palette Generator - https://forums.nesdev.com/viewtopic.php?p=85060#p85060 - - Use with Nestopia or FCEUmm libretro cores with the palette set to 'raw'. -*/ - -layout(std140, set = 0, binding = 0) uniform UBO -{ - mat4 MVP; - vec4 OutputSize; - vec4 OriginalSize; - vec4 SourceSize; - uint FrameCount; -} global; - -layout(push_constant) uniform Push -{ - float nes_saturation; - float nes_hue; - float nes_contrast; - float nes_brightness; - float nes_gamma; -} params; - -#pragma parameter nes_saturation "Saturation" 1.0 0.0 5.0 0.05 -#pragma parameter nes_hue "Hue" 0.0 -360.0 360.0 1.0 -#pragma parameter nes_contrast "Contrast" 1.0 0.0 2.0 0.05 -#pragma parameter nes_brightness "Brightness" 1.0 0.0 2.0 0.05 -#pragma parameter nes_gamma "Gamma" 1.8 1.0 2.5 0.05 - -#define saturation params.nes_saturation -#define hue params.nes_hue -#define contrast params.nes_contrast -#define brightness params.nes_brightness -#define gamma params.nes_gamma - -bool wave (int p, int color) -{ - return ((color + p + 8) % 12 < 6); -} - -float gammafix (float f) -{ - return f < 0.0 ? 0.0 : pow(f, 2.2 / gamma); -} - -vec3 MakeRGBColor(int emphasis, int level, int color) -{ - float y = 0.0; - float i = 0.0; - float q = 0.0; - - float r = 0.0; - float g = 0.0; - float b = 0.0; - - // Color 0xE and 0xF are black - level = (color < 14) ? level : 1; - - // Voltage levels, relative to synch voltage - float black = 0.518; - float white = 1.962; - float attenuation = 0.746; - const float levels[8] = float[] ( 0.350 , 0.518, 0.962, 1.550, - 1.094, 1.506, 1.962, 1.962); - - float low = levels[level + 4 * int(color == 0)]; - float high = levels[level + 4 * int(color < 13)]; - - // Calculate the luma and chroma by emulating the relevant circuits: - for(int p = 0; p < 12; p++) // 12 clock cycles per pixel. - { - // NES NTSC modulator (square wave between two voltage levels): - float spot = wave(p, color) ? high : low; - - // De-emphasis bits attenuate a part of the signal: - if ((bool(emphasis & 1) && wave(p, 12)) || - (bool(emphasis & 2) && wave(p, 4)) || - (bool(emphasis & 4) && wave(p, 8))) - { - spot *= attenuation; - } - - // Normalize: - float v = (spot - black) / (white - black); - - // Ideal TV NTSC demodulator: - // Apply contrast/brightness - v = (v - 0.5) * contrast + 0.5; - v *= (brightness / 12.0); - - float hue_tweak = hue * 12.0 / 360.0; - - y += v; - i += v * cos((3.141592653 / 6.0) * (p + hue_tweak) ); - q += v * sin((3.141592653 / 6.0) * (p + hue_tweak) ); - - } - - i *= saturation; - q *= saturation; - - // Convert YIQ into RGB according to the Sony CXA2025AS US decoder matrix. - r = clamp((1.0 * gammafix(y + 1.630 * i + 0.317 * q)), 0, 1.0); - g = clamp((1.0 * gammafix(y + -0.378 * i + -0.466 * q)), 0, 1.0); - b = clamp((1.0 * gammafix(y + -1.089 * i + 1.677 * q)), 0, 1.0); - - return vec3(r,g,b); -} - -#pragma stage vertex -layout(location = 0) in vec4 Position; -layout(location = 1) in vec2 TexCoord; -layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out float colorPhase; - -void main() -{ - gl_Position = global.MVP * Position; - vTexCoord = TexCoord; -} - -#pragma stage fragment -layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in float colorPhase; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - -void main() -{ - vec4 c = texture(Source, vTexCoord.xy); - - // Extract the chroma, level, and emphasis from the normalized RGB triplet - int color = int(floor((c.r * 15.0) + 0.5)); - int level = int(floor((c.g * 3.0) + 0.5)); - int emphasis = int(floor((c.b * 7.0) + 0.5)); - - vec3 out_color = MakeRGBColor(emphasis, level, color); - FragColor = vec4(out_color, 1.0); -} diff --git a/misc/nes-color-decoder.slang b/misc/nes-color-decoder.slang index 1e61963..eedb696 100644 --- a/misc/nes-color-decoder.slang +++ b/misc/nes-color-decoder.slang @@ -5,6 +5,9 @@ Ported from Bisqwit's C++ NES Palette Generator https://forums.nesdev.com/viewtopic.php?p=85060#p85060 + Hue Preserve Clip functions ported from Drag's Palette Generator + http://drag.wootest.net/misc/palgen.html + Use with Nestopia or FCEUmm libretro cores with the palette set to 'raw'. */ @@ -24,6 +27,8 @@ layout(push_constant) uniform Push float nes_contrast; float nes_brightness; float nes_gamma; + float nes_sony_matrix; + float nes_clip_method; } params; #pragma parameter nes_saturation "Saturation" 1.0 0.0 5.0 0.05 @@ -31,12 +36,19 @@ layout(push_constant) uniform Push #pragma parameter nes_contrast "Contrast" 1.0 0.0 2.0 0.05 #pragma parameter nes_brightness "Brightness" 1.0 0.0 2.0 0.05 #pragma parameter nes_gamma "Gamma" 1.8 1.0 2.5 0.05 +#pragma parameter nes_sony_matrix "Sony CXA2025AS US colors" 0.0 0.0 1.0 1.0 +#pragma parameter nes_clip_method "Palette clipping method" 0.0 0.0 2.0 1.0 #define saturation params.nes_saturation #define hue params.nes_hue #define contrast params.nes_contrast #define brightness params.nes_brightness #define gamma params.nes_gamma +#define nes_sony_matrix params.nes_sony_matrix +#define nes_clip_method params.nes_clip_method + +//comment the define out to use the "common" conversion matrix instead of the FCC sanctioned one +#define USE_FCC_MATRIX bool wave (int p, int color) { @@ -48,6 +60,65 @@ float gammafix (float f) return f < 0.0 ? 0.0 : pow(f, 2.2 / gamma); } +vec3 huePreserveClipDarken(float r, float g, float b) +{ + float ratio = 1.0; + if ((r > 1.0) || (g > 1.0) || (b > 1.0)) + { + float max = r; + if (g > max) + max = g; + if (b > max) + max = b; + ratio = 1.0 / max; + } + + r *= ratio; + g *= ratio; + b *= ratio; + + r = clamp(r, 0.0, 1.0); + g = clamp(g, 0.0, 1.0); + b = clamp(b, 0.0, 1.0); + + return vec3(r, g, b); +} + +vec3 huePreserveClipDesaturate(float r, float g, float b) +{ + float l = (.299 * r) + (0.587 * g) + (0.114 * b); + bool ovr = false; + float ratio = 1.0; + + if ((r > 1.0) || (g > 1.0) || (b > 1.0)) + { + ovr = true; + float max = r; + if (g > max) max = g; + if (b > max) max = b; + ratio = 1.0 / max; + } + + if (ovr) + { + r -= 1.0; + g -= 1.0; + b -= 1.0; + r *= ratio; + g *= ratio; + b *= ratio; + r += 1.0; + g += 1.0; + b += 1.0; + } + + r = clamp(r, 0.0, 1.0); + g = clamp(g, 0.0, 1.0); + b = clamp(b, 0.0, 1.0); + + return vec3(r, g, b); +} + vec3 MakeRGBColor(int emphasis, int level, int color) { float y = 0.0; @@ -58,6 +129,8 @@ vec3 MakeRGBColor(int emphasis, int level, int color) float g = 0.0; float b = 0.0; + float yiq2rgb[6]; + // Color 0xE and 0xF are black level = (color < 14) ? level : 1; @@ -104,19 +177,71 @@ vec3 MakeRGBColor(int emphasis, int level, int color) i *= saturation; q *= saturation; - // Convert YIQ into RGB according to FCC-sanctioned conversion matrix. - r = clamp((1.0 * gammafix(y + 0.946882 * i + 0.623557 * q)), 0, 1.0); - g = clamp((1.0 * gammafix(y + -0.274788 * i + -0.635691 * q)), 0, 1.0); - b = clamp((1.0 * gammafix(y + -1.108545 * i + 1.709007 * q)), 0, 1.0); + if (nes_sony_matrix > 0.5) + { + // Sony CXA2025AS US conversion matrix + yiq2rgb[0] = 1.630; + yiq2rgb[1] = 0.317; + yiq2rgb[2] = -0.378; + yiq2rgb[3] = -0.466; + yiq2rgb[4] = -1.089; + yiq2rgb[5] = 1.677; + } + else + { +#ifdef USE_FCC_MATRIX + // FCC sanctioned conversion matrix + yiq2rgb[0] = 0.946882; + yiq2rgb[1] = 0.623557; + yiq2rgb[2] = -0.274788; + yiq2rgb[3] = -0.635691; + yiq2rgb[4] = -1.108545; + yiq2rgb[5] = 1.709007; +#else + // commonly used conversion matrix + yiq2rgb[0] = 0.956; + yiq2rgb[1] = 0.621; + yiq2rgb[2] = -0.272; + yiq2rgb[3] = -0.647; + yiq2rgb[4] = -1.105; + yiq2rgb[5] = 1.702; +#endif + } - return vec3(r,g,b); + // Convert YIQ into RGB according to selected conversion matrix + r = gammafix(y + yiq2rgb[0] * i + yiq2rgb[1] * q); + g = gammafix(y + yiq2rgb[2] * i + yiq2rgb[3] * q); + b = gammafix(y + yiq2rgb[4] * i + yiq2rgb[5] * q); + + vec3 corrected_rgb; + + // Apply desired clipping method to out-of-gamut colors. + if (nes_clip_method < 0.5) + { + //If a channel is out of range (> 1.0), it's simply clamped to 1.0. This may change hue, saturation, and/or lightness. + r = clamp(r, 0.0, 1.0); + g = clamp(g, 0.0, 1.0); + b = clamp(b, 0.0, 1.0); + corrected_rgb = vec3(r, g, b); + } + else if (nes_clip_method == 1.0) + { + //If any channels are out of range, the color is darkened until it is completely in range. + corrected_rgb = huePreserveClipDarken(r, g, b); + } + else if (nes_clip_method == 2.0) + { + //If any channels are out of range, the color is desaturated towards the luminance it would've had. + corrected_rgb = huePreserveClipDesaturate(r, g, b); + } + + return corrected_rgb; } #pragma stage vertex layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out float colorPhase; void main() { @@ -126,7 +251,6 @@ void main() #pragma stage fragment layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in float colorPhase; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; From cbcfab04ebc9c75c4b8f5664ee1ee2267aa26ecb Mon Sep 17 00:00:00 2001 From: Monroe88 Date: Sun, 7 May 2017 02:08:44 -0500 Subject: [PATCH 2/2] Backport colorimetry shader from common-shaders --- misc/colorimetry.slang | 256 ++++++++++++++++++ ...color-decoder+colorimetry+pixellate.slangp | 12 + 2 files changed, 268 insertions(+) create mode 100644 misc/colorimetry.slang create mode 100644 presets/nes-color-decoder+colorimetry+pixellate.slangp diff --git a/misc/colorimetry.slang b/misc/colorimetry.slang new file mode 100644 index 0000000..b86936b --- /dev/null +++ b/misc/colorimetry.slang @@ -0,0 +1,256 @@ +#version 450 + +/* + Colorimetry shader + Ported from Drag's NES Palette Generator + http://drag.wootest.net/misc/palgen.html +*/ + +layout(std140, set = 0, binding = 0) uniform UBO +{ + mat4 MVP; + vec4 OutputSize; + vec4 OriginalSize; + vec4 SourceSize; + uint FrameCount; +} global; + +layout(push_constant) uniform Push +{ + float color_mode; + float white_point_d93; + float clipping_method; +} params; + +#pragma parameter color_mode "Colorimetry mode" 0.0 0.0 2.0 1.0 +#pragma parameter white_point_d93 "Use D93 white point" 1.0 0.0 1.0 1.0 +#pragma parameter clipping_method "Color clipping method" 0.0 0.0 2.0 1.0 + +#define color_mode params.color_mode +#define white_point_d93 params.white_point_d93 +#define clipping_method params.clipping_method + +vec3 huePreserveClipDarken(float r, float g, float b) +{ + float ratio = 1.0; + if ((r > 1.0) || (g > 1.0) || (b > 1.0)) + { + float max = r; + if (g > max) + max = g; + if (b > max) + max = b; + ratio = 1.0 / max; + } + + r *= ratio; + g *= ratio; + b *= ratio; + + r = clamp(r, 0.0, 1.0); + g = clamp(g, 0.0, 1.0); + b = clamp(b, 0.0, 1.0); + + return vec3(r, g, b); +} + +vec3 huePreserveClipDesaturate(float r, float g, float b) +{ + float l = (.299 * r) + (0.587 * g) + (0.114 * b); + bool ovr = false; + float ratio = 1.0; + + if ((r > 1.0) || (g > 1.0) || (b > 1.0)) + { + ovr = true; + float max = r; + if (g > max) max = g; + if (b > max) max = b; + ratio = 1.0 / max; + } + + if (ovr) + { + r -= 1.0; + g -= 1.0; + b -= 1.0; + r *= ratio; + g *= ratio; + b *= ratio; + r += 1.0; + g += 1.0; + b += 1.0; + } + + r = clamp(r, 0.0, 1.0); + g = clamp(g, 0.0, 1.0); + b = clamp(b, 0.0, 1.0); + + return vec3(r, g, b); +} + +vec3 ApplyColorimetry(vec3 color) +{ + //http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html + //http://www.dr-lex.be/random/matrix_inv.html + // Convert the (x,y) values to X Y Z. + + float R = color.r; + float G = color.g; + float B = color.b; + + float Rx = 0.0; + float Ry = 0.0; + float Gx = 0.0; + float Gy = 0.0; + float Bx = 0.0; + float By = 0.0; + float Wx = 0.0; + float Wy = 0.0; + + if (white_point_d93 > 0.5) + { + Wx = 0.31; + Wy = 0.316; + } + else + { + Wx = 0.3127; + Wy = 0.329; + } + + if (color_mode < 0.5) + { + // FCC 1953 + // Original FCC standard for the color of the phosphors + Rx = 0.67; + Ry = 0.33; + Gx = 0.21; + Gy = 0.71; + Bx = 0.14; + By = 0.08; + } + else if (color_mode == 1.0) + { + // SMPTE C (1987) + // A newer standard for the color of the phospors. (Not used in Japan) + Rx = 0.63; + Ry = 0.34; + Gx = 0.31; + Gy = 0.595; + Bx = 0.155; + By = 0.07; + } + else + { + //sRGB (PC Monitors) + //The colorimetry used in PC monitors, like the one you're (probably) looking at right now. + Rx = 0.64; + Ry = 0.33; + Gx = 0.3; + Gy = 0.6; + Bx = 0.15; + By = 0.06; + } + + float Xr = Rx / Ry; + float Xg = Gx / Gy; + float Xb = Bx / By; + float Xw = Wx / Wy; + float Yr = 1.0; + float Yg = 1.0; + float Yb = 1.0; + float Yw = 1.0; + float Zr = (1.0 - Rx - Ry) / Ry; + float Zg = (1.0 - Gx - Gy) / Gy; + float Zb = (1.0 - Bx - By) / By; + float Zw = (1.0 - Wx - Wy) / Wy; + + // Get ready for a bunch of painful math. I need to invert a matrix, then multiply it by a vector. + // Determinant for inverse matrix + + float sDet = (Xr*((Zb*Yg)-(Zg*Yb)))-(Yr*((Zb*Xg)-(Zg*Xb)))+(Zr*((Yb*Xg)-(Yg*Xb))); + + float Sr = ((((Zb*Yg)-(Zg*Yb))/sDet)*Xw) + ((-((Zb*Xg)-(Zg*Xb))/sDet)*Yw) + ((((Yb*Xg)-(Yg*Xb))/sDet)*Zw); + float Sg = ((-((Zb*Yr)-(Zr*Yb))/sDet)*Xw) + ((((Zb*Xr)-(Zr*Xb))/sDet)*Yw) + ((-((Yb*Xr)-(Yr*Xb))/sDet)*Zw); + float Sb = ((((Zg*Yr)-(Zr*Yg))/sDet)*Xw) + ((-((Zg*Xr)-(Zr*Xg))/sDet)*Yw) + ((((Yg*Xr)-(Yr*Xg))/sDet)*Zw); + + // This should be the completed RGB -> XYZ matrix. + // Multiply each of the first three members by R, then add them together to get X + float convMatrix[9] = float[] (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + convMatrix[0] = Sr*Xr; + convMatrix[1] = Sg*Xg; + convMatrix[2] = Sb*Xb; + convMatrix[3] = Sr*Yr; + convMatrix[4] = Sg*Yg; + convMatrix[5] = Sb*Yb; + convMatrix[6] = Sr*Zr; + convMatrix[7] = Sg*Zg; + convMatrix[8] = Sb*Zb; + + // Convert RGB to XYZ using the matrix generated with the specified RGB and W points. + float X = (convMatrix[0] * R) + (convMatrix[1] * G) + (convMatrix[2] * B); + float Y = (convMatrix[3] * R) + (convMatrix[4] * G) + (convMatrix[5] * B); + float Z = (convMatrix[6] * R) + (convMatrix[7] * G) + (convMatrix[8] * B); + + // This is the conversion matrix for CIEXYZ -> sRGB. I nicked this from: + // http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html + // and I know it's right because when you use the sRGB colorimetry, this matrix produces identical results to + // just using the raw R, G, and B above. + float xyztorgb[9] = float[] (3.2404, -1.5371, -0.4985, -0.9693, 1.876, 0.0416, 0.0556, -0.204, 1.0572); + + // Convert back to RGB using the XYZ->sRGB matrix. + R = (xyztorgb[0]*X) + (xyztorgb[1]*Y) + (xyztorgb[2]*Z); + G = (xyztorgb[3]*X) + (xyztorgb[4]*Y) + (xyztorgb[5]*Z); + B = (xyztorgb[6]*X) + (xyztorgb[7]*Y) + (xyztorgb[8]*Z); + + vec3 corrected_rgb; + + // Apply desired clipping method to out-of-gamut colors. + if (clipping_method < 0.5) + { + //If a channel is out of range (> 1.0), it's simply clamped to 1.0. This may change hue, saturation, and/or lightness. + R = clamp(R, 0.0, 1.0); + G = clamp(G, 0.0, 1.0); + B = clamp(B, 0.0, 1.0); + corrected_rgb = vec3(R, G, B); + } + else if (clipping_method == 1.0) + { + //If any channels are out of range, the color is darkened until it is completely in range. + corrected_rgb = huePreserveClipDarken(R, G, B); + } + else if (clipping_method == 2.0) + { + //If any channels are out of range, the color is desaturated towards the luminance it would've had. + corrected_rgb = huePreserveClipDesaturate(R, G, B); + } + + return corrected_rgb; +} + +#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() +{ + vec4 c = texture(Source, vTexCoord.xy); + vec3 rgb = vec3(c.r, c.g, c.b); + + vec3 out_color = ApplyColorimetry(rgb); + FragColor = vec4(out_color, 1.0); +} diff --git a/presets/nes-color-decoder+colorimetry+pixellate.slangp b/presets/nes-color-decoder+colorimetry+pixellate.slangp new file mode 100644 index 0000000..b3a4cb3 --- /dev/null +++ b/presets/nes-color-decoder+colorimetry+pixellate.slangp @@ -0,0 +1,12 @@ +shaders = "3" +shader0 = ../misc/nes-color-decoder.slang +shader1 = ../misc/colorimetry.slang +shader2 = ../retro/shaders/pixellate.slang + +filter_linear0 = "false" +scale_type0 = "source" +scale0 = "1.000000" + +filter_linear1 = "false" +scale_type1 = "source" +scale1 = "1.000000"