shader_type canvas_item; // 输入参数 uniform int PaletteCount; uniform vec4 BasicColor[8] : source_color; uniform vec4 ModulateColor[8] : source_color; uniform float Tolerance[8]; uniform int BlendMode[8]; //#define DEBUG #ifdef DEBUG uniform sampler2D BlendTexture : source_color; #endif // ################################################################################################ // ################################################################################################ // ################################################################################################ // ClusteringColor by CIEDE2000 // https://www.shadertoy.com/view/msXyz8 const float epsilon = 0.00001; float my_sin(float x) { return sin(radians(x)); } float my_cos(float x) { return cos(radians(x)); } float my_atan(float y, float x) { float v = degrees(atan(y, x)); return (v < 0.0) ? v + 360.0 : v; } float get_h(float a, float b) { bool a_and_b_are_zeros = (abs(a) < epsilon)&&(abs(b) < epsilon); return a_and_b_are_zeros ? 0.0 : my_atan(b, a); } float get_delta_h(float C1, float C2, float h1, float h2) { float diff = h2 - h1; return (C1 * C2 < epsilon) ? 0.0 : (abs(diff) <= 180.0) ? diff : (diff > 180.0) ? diff - 360.0 : diff + 360.0; } float get_h_bar(float C1, float C2, float h1, float h2) { float dist = abs(h1 - h2); float sum = h1 + h2; return (C1 * C2 < epsilon) ? h1 + h2 : (dist <= 180.0) ? 0.5 * sum : (sum < 360.0) ? 0.5 * (sum + 360.0) : 0.5 * (sum - 360.0); } float calculate_CIEDE2000(vec3 Lab1, vec3 Lab2) { float L1 = Lab1.x; float a1 = Lab1.y; float b1 = Lab1.z; float L2 = Lab2.x; float a2 = Lab2.y; float b2 = Lab2.z; float C1_ab = sqrt(a1 * a1 + b1 * b1); float C2_ab = sqrt(a2 * a2 + b2 * b2); float C_ab_bar = 0.5 * (C1_ab + C2_ab); float G = 0.5 * (1.0 - sqrt(pow(C_ab_bar, 7.0) / (pow(C_ab_bar, 7.0) + pow(25.0, 7.0)))); float a_1 = (1.0 + G) * a1; float a_2 = (1.0 + G) * a2; float C1 = sqrt(a_1 * a_1 + b1 * b1); float C2 = sqrt(a_2 * a_2 + b2 * b2); float h1 = get_h(a_1, b1); float h2 = get_h(a_2, b2); float delta_L = L2 - L1; float delta_C = C2 - C1; float delta_h = get_delta_h(C1, C2, h1, h2); float delta_H = 2.0 * sqrt(C1 * C2) * my_sin(0.5 * delta_h); float L_bar = 0.5 * (L1 + L2); float C_bar = 0.5 * (C1 + C2); float h_bar = get_h_bar(C1, C2, h1, h2); float T = 1.0 - 0.17 * my_cos(h_bar - 30.0) + 0.24 * my_cos(2.0 * h_bar) + 0.32 * my_cos(3.0 * h_bar + 6.0) - 0.20 * my_cos(4.0 * h_bar - 63.0); float delta_theta = 30.0 * exp(-((h_bar - 275.0) / 25.0) * ((h_bar - 275.0) / 25.0)); float R_C = 2.0 * sqrt(pow(C_bar, 7.0) / (pow(C_bar, 7.0) + pow(25.0, 7.0))); float S_L = 1.0 + (0.015 * (L_bar - 50.0) * (L_bar - 50.0)) / sqrt(20.0 + (L_bar - 50.0) * (L_bar - 50.0)); float S_C = 1.0 + 0.045 * C_bar; float S_H = 1.0 + 0.015 * C_bar * T; float R_T = -my_sin(2.0 * delta_theta) * R_C; const float k_L = 1.0; const float k_C = 1.0; const float k_H = 1.0; float deltaL = delta_L / (k_L * S_L); float deltaC = delta_C / (k_C * S_C); float deltaH = delta_H / (k_H * S_H); float delta_E_squared = deltaL * deltaL + deltaC * deltaC + deltaH * deltaH + R_T * deltaC * deltaH; return sqrt(delta_E_squared); } //--- RGB2Lab vec3 rgb2xyz(vec3 c) { vec3 tmp; tmp.x = (c.r > 0.04045) ? pow((c.r + 0.055) / 1.055, 2.4) : c.r / 12.92; tmp.y = (c.g > 0.04045) ? pow((c.g + 0.055) / 1.055, 2.4) : c.g / 12.92; tmp.z = (c.b > 0.04045) ? pow((c.b + 0.055) / 1.055, 2.4) : c.b / 12.92; return 100.0 * tmp * mat3(vec3(0.4124, 0.3576, 0.1805), vec3(0.2126, 0.7152, 0.0722), vec3(0.0193, 0.1192, 0.9505)); } vec3 xyz2lab(vec3 c) { vec3 n = c / vec3(95.047, 100.0, 108.883); vec3 v; v.x = (n.x > 0.008856) ? pow(n.x, 1.0 / 3.0) : (7.787 * n.x) + (16.0 / 116.0); v.y = (n.y > 0.008856) ? pow(n.y, 1.0 / 3.0) : (7.787 * n.y) + (16.0 / 116.0); v.z = (n.z > 0.008856) ? pow(n.z, 1.0 / 3.0) : (7.787 * n.z) + (16.0 / 116.0); return vec3((116.0 * v.y) - 16.0, 500.0 * (v.x - v.y), 200.0 * (v.y - v.z)); } vec3 rgb2lab(vec3 c) { vec3 lab = xyz2lab(rgb2xyz(c)); return vec3(lab.x / 100.0, 0.5 + 0.5 * (lab.y / 127.0), 0.5 + 0.5 * (lab.z / 127.0)); } float colorCompare(vec3 rgb1, vec3 rgb2) { vec3 lab1 = rgb2lab(rgb1); vec3 lab2 = rgb2lab(rgb2); return calculate_CIEDE2000(lab1, lab2); } // ################################################################################################ // ################################################################################################ // ################################################################################################ // blends modes // https://en.wikipedia.org/wiki/Blend_modes // pdf page 389 blend mode: https://developer.adobe.com/document-services/docs/assets/5b15559b96303194340b99820d3a70fa/PDF_ISO_32000-2.pdf // functions #define VEC3(C, FUNC) vec3(FUNC(C.r), FUNC(C.g), FUNC(C.b)) #define MAKE_VEC3(BASE, BLEND, FUNC) vec3(FUNC(BASE.r, BLEND.r), FUNC(BASE.g, BLEND.g), FUNC(BASE.b, BLEND.b)) #define CMIN(C) min(C.r, min(C.g, C.b)) #define CMAX(C) max(C.r, max(C.g, C.b)) float unionAlpha(float b, float s) { return b + s * (1.0 - b); } float getLum(vec3 c) { return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; } // Luminosity vec3 clipColor(vec3 c) { float l = getLum(c); float n = CMIN(c); float x = CMAX(c); if (n < 0.0) return l + (((c - l) * l) / (l - n)); if (x > 1.0) return l + (((c - l) * (1.0 - l)) / (l - n)); return c; } vec3 setLum(vec3 c, float l) { float d = l - getLum(c); c += d; return clipColor(c); } float getSat(vec3 c) { return CMAX(c) - CMIN(c); } // Saturation vec3 setSat(vec3 C, float s) { // subscripts for Cmin, Cmid, and Cmax int indices[3]; if (C.x <= C.y) { if (C.y <= C.z) indices = int[3](0, 1, 2); // x <= y <= z else if (C.x <= C.z) indices = int[3](0, 2, 1); // x <= z < y else indices = int[3](2, 0, 1); // z < x <= y } else { if (C.x <= C.z) indices = int[3](1, 0, 2); // y < x <= z else if (C.y <= C.z)indices = int[3](1, 2, 0); // y <= z < x else indices = int[3](2, 1, 0); // z < y < x } // set Saturation if (C[indices[2]] > C[indices[0]]) { C[indices[1]] = (((C[indices[1]] - C[indices[0]]) * s) / (C[indices[2]] - C[indices[0]])); C[indices[2]] = s; } else { C[indices[1]] = 0.0; C[indices[2]] = 0.0; } C[indices[0]] = 0.0; return C; } // 正常 vec3 normal( vec3 Cb, vec3 Cs ) { return Cs; } // 正片叠底 float _multiply( float Cb, float Cs ) { return Cb * Cs; } vec3 multiply( vec3 Cb, vec3 Cs ) { return Cb * Cs; } // 滤色 float _screen( float Cb, float Cs ) { return Cb + Cs - Cb * Cs; } vec3 screen( vec3 Cb, vec3 Cs ) { return Cb + Cs - Cb * Cs; } // 变暗 vec3 darken( vec3 Cb, vec3 Cs ) { return min(Cb, Cs); } // 变亮 vec3 lighten( vec3 Cb, vec3 Cs ) { return max(Cb, Cs); } // 颜色减淡 float _colorDodge( float Cb, float Cs ) { if(Cb <= 0.0) return 0.0; else if (Cb >= (1.0 - Cs)) return 1.0; else return Cb / (1.0 - Cs); } vec3 colorDodge( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _colorDodge); } // 颜色加深 float _colorBurn(float Cb, float Cs) { if (Cb >= 1.0) return 1.0; if ((1.0 - Cb) >= Cs) return 0.0; else return 1.0 - ((1.0 - Cb) / Cs); } vec3 colorBurn( vec3 Cb, vec3 Cs) { return MAKE_VEC3(Cb, Cs, _colorBurn); } // 强光 float _hardLight( float Cb, float Cs ) { return (Cs <= 0.5) ? _multiply(Cb, 2.0 * Cs): _screen(Cb, 2.0 * Cs - 1.0); } vec3 hardLight( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _hardLight); } // 柔光 float _softLight( float Cb, float Cs ) { if(Cs <= 0.5) return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb); else { float d = (Cb <= 0.25) ? ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb : sqrt(Cb); return Cb + (2.0 * Cs - 1.0) * (d - Cb); } } vec3 softLight( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _softLight); } // 叠加 vec3 overlay( vec3 Cb, vec3 Cs ) { return hardLight(Cs, Cb); } // 差值 vec3 difference( vec3 Cb, vec3 Cs ) { return abs(Cb - Cs); } // 排除 vec3 exclusion( vec3 Cb, vec3 Cs ) { return Cb + Cs - 2.0 * Cb * Cs; } // 色相 vec3 hue( vec3 Cb, vec3 Cs ) { return setLum(setSat(Cs, getSat(Cb)), getLum(Cb)); } // 颜色 vec3 color( vec3 Cb, vec3 Cs ) { return setLum(Cs, getLum(Cb)); } // 饱和度 vec3 saturation( vec3 Cb, vec3 Cs ) { return setLum(setSat(Cb, getSat(Cs)), getLum(Cb)); } // 明度 vec3 luminosity( vec3 Cb, vec3 Cs ) { return setLum(Cb, getLum(Cs)); } // 剩下的不在pdf标准中 // 线性加深 float _linearBurn(float Cb, float Cs) { return max(0, Cb + Cs - 1.0); } vec3 linearBurn( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _linearBurn); } // 深色 vec3 darkerColor( vec3 Cb, vec3 Cs ) { return getLum(Cb) < getLum(Cs)? Cb: Cs; } // 线性减淡(添加) float _linearDodge(float Cb, float Cs) { return min(1.0, Cb + Cs); } vec3 linearDodge( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _linearDodge); } // 浅色 vec3 lighterColor( vec3 Cb, vec3 Cs ) { return getLum(Cb) > getLum(Cs)? Cb: Cs; } // 亮光 float _vividLight( float Cb, float Cs ) { return (Cs <= 0.5) ? _colorBurn(Cb, 2.0 * Cs): _colorDodge(Cb, 2.0 * (Cs - 0.5)); } vec3 vividLight( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _vividLight); } // 线性光 float _linearLight( float Cb, float Cs ) { return (Cs <= 0.5) ? _linearBurn(Cb, 2.0 * Cs): _linearDodge(Cb, 2.0 * (Cs - 0.5)); } vec3 linearLight( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _linearLight); } // 点光 float _pinLight( float Cb, float Cs ) { return (Cs <= 0.5) ? min(Cb,2.0 * Cs) : max(Cb,2.0 * (Cs - 0.5)); } vec3 pinLight( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _pinLight); } // 实色混合 float _hardMix( float Cb, float Cs ) { return (Cb + Cs >= 1.0) ? 1.0 : 0.0; } vec3 hardMix( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _hardMix); } // 减去 vec3 subtract( vec3 Cb, vec3 Cs ) { return max(vec3(0.0), Cb - Cs); } // 划分 float _divide( float Cb, float Cs ) { return Cs > 0.0 ? min(1.0, Cb / Cs) : 1.0; } vec3 divide( vec3 Cb, vec3 Cs ) { return MAKE_VEC3(Cb, Cs, _divide); } vec3 blendFunctions( vec3 Cb, vec3 Cs, int id ) { if(id==0) return normal(Cb,Cs); // 正常 if(id==1) return darken(Cb,Cs); // 变暗 if(id==2) return multiply(Cb,Cs); // 正片叠底 if(id==3) return colorBurn(Cb,Cs); // 颜色加深 if(id==4) return linearBurn(Cb,Cs); // 线性加深 if(id==5) return darkerColor(Cb,Cs); // 深色 if(id==6) return lighten(Cb,Cs); // 变亮 if(id==7) return screen(Cb,Cs); // 滤色 if(id==8) return colorDodge(Cb,Cs); // 颜色减淡 if(id==9) return linearDodge(Cb,Cs); // 线性减淡(添加) if(id==10) return lighterColor(Cb,Cs); // 浅色 if(id==11) return overlay(Cb,Cs); // 叠加 if(id==12) return softLight(Cb,Cs); // 柔光 if(id==13) return hardLight(Cb,Cs); // 强光 if(id==14) return vividLight(Cb,Cs); // 亮光 if(id==15) return linearLight(Cb,Cs); // 线性光 if(id==16) return pinLight(Cb,Cs); // 点光 if(id==17) return hardMix(Cb,Cs); // 实色混合 if(id==18) return difference(Cb,Cs); // 差值 if(id==19) return exclusion(Cb,Cs); // 排除 if(id==20) return subtract(Cb,Cs); // 减去 if(id==21) return divide(Cb,Cs); // 划分 if(id==22) return hue(Cb,Cs); // 色相 if(id==23) return saturation(Cb,Cs); // 饱和度 if(id==24) return color(Cb,Cs); // 颜色 if(id==25) return luminosity(Cb,Cs); // 明度 return vec3(0.0); } // ################################################################################################ // ################################################################################################ // ################################################################################################ // gd shader script void fragment() { COLOR = texture(TEXTURE, UV); for (int i =0; i< PaletteCount; i++) { vec4 modulate_color = ModulateColor[i]; vec4 basic_color = BasicColor[i]; int blend_mode = BlendMode[i]; float tolerance = Tolerance[i]; // 将容差转换为0-1范围的阈值 float tolerance_factor = tolerance / 100.0; // Backdrop colour 背景色 vec3 Cb = COLOR.rgb; float Ab = COLOR.a; // Source colour 混合色 #ifdef DEBUG vec4 source_colour = texture(BlendTexture, UV); vec3 Cs = source_colour.rgb; float As = source_colour.a; #else vec4 source_colour = modulate_color; vec3 Cs = source_colour.rgb; float As = Ab > 0.0? source_colour.a: 0.0; #endif // Result colour float Ar = unionAlpha(Ab, As); vec3 Cr = (1.0 - As / Ar) * Cb + As / Ar * ((1.0 - Ab) * Cs + Ab * blendFunctions(Cb, Cs, blend_mode)); // 混合原始颜色和调制后的颜色 if (tolerance_factor >= 1.0) COLOR = vec4(Cr, Ar); else { // 计算颜色差异 float dis = colorCompare(Cb, basic_color.rgb); if (dis < tolerance_factor) COLOR = vec4(Cr, Ar); } } }