Nodes/Jovi_GLSL/POSTERIZE (JOV_GL)
ComfyUI Node

POSTERIZE (JOV_GL)

Reduce the pixel color data range

By Amorano·Created 2 years ago·Updated 12 months ago· 20
POSTERIZE (JOV_GL)
  • image
  • RGBA
  • RGB
  • MASK
steps16
FRAGMENT// name: POSTERIZE // desc: Reduce the pixel color data range // category: COLOR #ifndef LIB_COLOR #define LIB_COLOR //------------------------------------------------------------------------------ // COLOR //------------------------------------------------------------------------------ #ifndef LIB_CONVERT #define LIB_CONVERT //------------------------------------------------------------------------------ // COLOR //------------------------------------------------------------------------------ #ifndef LIB_CONST #define LIB_CONST //------------------------------------------------------------------------------ // CONSTANT //------------------------------------------------------------------------------ #define M_EPSILON 1.0e-10 // zero value for float comparisons #define M_DEG2RAD 0.017453292519943 // Degree to radian conversion factor #define M_RAD2DEG 57.29577951308232 // Radian to degree conversion factor #define M_TAU 6.283185307179586 // TAU (2 * π) #define M_TAU_INV 0.159154943091895 // TAU Inverse (1 / TAU) #define M_PI 3.141592653589793 // π #define M_PI_INV 0.318309886183790 // π Inverse (1 / π) #define M_PI_2 1.570796326794896 // π divided by 2 (π / 2) #define M_PI_4 0.785398163397448 // π divided by 4 (π / 4) #define M_3PI_4 2.356194490192345 // 3 * π divided by 4 (3π / 4) #define M_PHI 1.618033988749895 // Golden ratio (φ) #define M_PHI_INV 0.618033988749895 // Inverse of golden ratio (1 / φ) #define M_PHI_SQ 2.618033988749895 // Square of the golden ratio (φ^2) #define M_PHI_SQRT5 0.723606797749979 // φ / √5 (useful for fibonacci spherical distribution) #define M_GOLD_ANG 2.399963229728653 // Golden angle in radians #define M_E 2.718281828459045 // Euler's number (base of natural logarithm) #define M_LOG2E 1.442695040888963 // Log base 2 of e #define M_LOG10E 0.434294481903252 // Log base 10 of e #define M_LN2 0.693147180559945 // Natural log of 2 #define M_LN10 2.302585092994046 // Natural log of 10 #define M_SQRT2 1.414213562373095 // Square root of 2 #define M_SQRT3 1.732050807568877 // Square root of 3 #define M_SQRT2_INV 0.707106781186547 // 1 divided by square root of 2 (1 / sqrt(2)) #define M_SQRT3_INV 0.577350269189626 // 1 divided by square root of 3 (1 / sqrt(3)) #define M_SQRT5 2.236067977499790 // Square root of 5 //------------------------------------------------------------------------------ // GENERAL //------------------------------------------------------------------------------ // useful for triangle interpolation vec3 barycentricCoords(vec2 p, vec2 a, vec2 b, vec2 c) { vec2 v0 = b - a; vec2 v1 = c - a; vec2 v2 = p - a; float d00 = dot(v0, v0); float d01 = dot(v0, v1); float d11 = dot(v1, v1); float d20 = dot(v2, v0); float d21 = dot(v2, v1); float denom = d00 * d11 - d01 * d01; vec3 result; result.y = (d11 * d20 - d01 * d21) / denom; result.z = (d00 * d21 - d01 * d20) / denom; result.x = 1.0 - result.y - result.z; return result; } #endif #define M_SRGB_ALPHA 0.055 #define M_SRGB_THRESH 0.04045 // LAB constants #define M_LAB_E 0.008856 // LAB epsilon #define M_LAB_K 903.3 // LAB kappa #define M_LAB_16_116 0.137931 // 16/116 // Additional illuminants #define M_D50 vec3(96.422, 100.0, 82.521) // D50 reference white #define M_D65 vec3(95.047, 100.0, 108.883) // D65 reference white #define M_D75 vec3(94.972, 100.0, 122.638) // D75 reference white // ============================================================================= // PROTOTYPES // ============================================================================= vec3 convert_rgb2hsv(vec3 rgb); vec3 convert_rgb2lab(vec3 rgb); vec3 convert_rgb2xyz(vec3 rgb); vec3 convert_hsv2rgb(vec3 hsv); vec3 convert_hsv2lab(vec3 hsv); vec3 convert_hsv2xyz(vec3 hsv); vec3 convert_lab2rgb(vec3 lab); vec3 convert_lab2hsv(vec3 lab); vec3 convert_lab2xyz(vec3 lab); vec3 convert_xyz2rgb(vec3 xyz); vec3 convert_xyz2hsv(vec3 xyz); vec3 convert_xyz2lab(vec3 xyz); vec3 convert_rgb2oklab(vec3 rgb); vec3 convert_oklab2rgb(vec3 lab); //------------------------------------------------------------------------------ // RGB //------------------------------------------------------------------------------ vec3 convert_rgb2hsv(vec3 rgb) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(rgb.bg, K.wz), vec4(rgb.gb, K.xy), step(rgb.b, rgb.g)); vec4 q = mix(vec4(p.xyw, rgb.r), vec4(rgb.r, p.yzx), step(p.x, rgb.r)); float d = q.x - min(q.w, q.y); return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + M_EPSILON)), d / (q.x + M_EPSILON), q.x); } vec3 convert_rgb2lab(vec3 rgb) { vec3 xyz = convert_rgb2xyz(rgb); return convert_xyz2lab(xyz); } vec3 convert_rgb2xyz(vec3 rgb) { vec3 tmp; tmp.x = (rgb.r > 0.04045) ? pow((rgb.r + 0.055) / 1.055, 2.4) : rgb.r / 12.92; tmp.y = (rgb.g > 0.04045) ? pow((rgb.g + 0.055) / 1.055, 2.4) : rgb.g / 12.92; tmp.z = (rgb.b > 0.04045) ? pow((rgb.b + 0.055) / 1.055, 2.4) : rgb.b / 12.92; return 100.0 * tmp * mat3( 0.4124, 0.3576, 0.1805, 0.2126, 0.7152, 0.0722, 0.0193, 0.1192, 0.9505 ); } //------------------------------------------------------------------------------ // HSV //------------------------------------------------------------------------------ vec3 convert_hsv2rgb(vec3 hsv) { hsv = vec3(hsv.x, clamp(hsv.yz, 0.0, 1.0)); vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(hsv.xxx + K.xyz) * 6.0 - K.www); return hsv.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsv.y); } vec3 convert_hsv2lab(vec3 hsv) { float H = hsv.x * 360.0; float S = hsv.y; float V = hsv.z; // Convert to LAB float L = V * 100.0; float C = S * L; float h = H * M_PI / 180.0; float a = C * cos(h); float b = C * sin(h); // Normalize LAB return vec3(L / 100.0, (a + 128.0) / 255.0, (b + 128.0) / 255.0); } vec3 convert_hsv2xyz(vec3 hsv) { vec3 rgb = convert_hsv2rgb(hsv); return convert_rgb2xyz(rgb); } //------------------------------------------------------------------------------ // LAB //------------------------------------------------------------------------------ vec3 convert_lab2rgb(vec3 lab) { vec3 xyz = convert_lab2xyz(lab); return convert_xyz2rgb(xyz); } vec3 convert_lab2hsv(vec3 lab) { vec3 rgb = convert_lab2rgb(lab); return convert_rgb2hsv(rgb); } vec3 convert_lab2xyz(vec3 lab) { float fy = (lab.x + 16.0) / 116.0; float fx = lab.y / 500.0 + fy; float fz = fy - lab.z / 200.0; vec3 f = vec3(fx, fy, fz); vec3 thresh = step(vec3(0.206897), f); vec3 xyz = mix( (f - vec3(16.0/116.0)) / 7.787, f * f * f, thresh ); return xyz * M_D65; } //------------------------------------------------------------------------------ // XYZ //------------------------------------------------------------------------------ vec3 convert_xyz2rgb(vec3 xyz) { vec3 v = xyz / M_D65; vec3 thresh = step(0.0031308, v); return mix( 12.92 * v, 1.055 * pow(v, vec3(1.0/2.4)) - 0.055, thresh ); } vec3 convert_xyz2hsv(vec3 xyz) { vec3 rgb = convert_xyz2rgb(xyz); return convert_rgb2hsv(rgb); } vec3 convert_xyz2lab(vec3 xyz) { vec3 n = xyz / M_D65; vec3 thresh = step(vec3(0.008856), n); vec3 v = mix( 7.787 * n + vec3(16.0/116.0), pow(n, vec3(1.0/3.0)), thresh ); return vec3( 116.0 * v.y - 16.0, 500.0 * (v.x - v.y), 200.0 * (v.y - v.z) ); } //------------------------------------------------------------------------------ // OKLAB //------------------------------------------------------------------------------ // RGB to Oklab (perceptually uniform color space) vec3 convert_rgb2oklab(vec3 rgb) { vec3 lms = rgb * mat3( 0.4122214708, 0.5363325363, 0.0514459929, 0.2119034982, 0.6806995451, 0.1073969566, 0.0883024619, 0.2817188376, 0.6299787005 ); lms = pow(lms, vec3(1.0/3.0)); return lms * mat3( 0.2104542553, 0.7936177850, -0.0040720468, 1.9779984951, -2.4285922050, 0.4505937099, 0.0259040371, 0.7827717662, -0.8086757660 ); } // Oklab to RGB vec3 convert_oklab2rgb(vec3 lab) { vec3 lms = lab * mat3( 1.0000000000, 0.3963377774, 0.2158037573, 1.0000000000, -0.1055613458, -0.0638541728, 1.0000000000, -0.0894841775, -1.2914855480 ); lms = lms * lms * lms; return lms * mat3( 4.0767416621, -3.3077115913, 0.2309699292, -1.2684380046, 2.6097574011, -0.3413193965, -0.0041960863, -0.7034186147, 1.7076147010 ); } #endif #define M_GAMMA 2.2 // Standard gamma correction value #define M_GAMMA_INV 0.4545 // 1.0 / 2.2 for inverse gamma #define M_LUMA_R 0.2126 // Rec. 709 luma coefficients for red #define M_LUMA_G 0.7152 // Rec. 709 luma coefficients for green #define M_LUMA_B 0.0722 // Rec. 709 luma coefficients for blue // ============================================================================= // PROTOTYPES // ============================================================================= vec3 color_complementary(vec3 rgb); vec3[3] color_triadic(vec3 rgb); vec3[3] color_splitComplementary(vec3 rgb, float angle); vec3[4] color_tetradic(vec3 rgb, float angle); vec3[5] color_analogous(vec3 rgb, float angle); vec3 color_duotone(vec3 rgb, vec3 dark, vec3 light); vec3 color_vibrance(vec3 rgb, float amount); vec3 color_levelAdjust(vec3 rgb, vec3 inBlack, vec3 inWhite, vec3 outBlack, vec3 outWhite); float color_perceivedBrightness(vec3 rgb); float color_colorfulness(vec3 rgb); bool color_isNeutral(vec3 rgb, float threshold); vec3 color_saturate(vec3 rgb, float adjustment); vec3 color_brighten(vec3 rgb, float adjustment); vec3 color_rotateHue(vec3 rgb, float angle); vec3 color_tint(vec3 base, vec3 tintColor, float amount); float color_luminance(vec3 rgb); float color_contrastRatio(vec3 rgb1, vec3 rgb2); float color_deltaE(vec3 lab1, vec3 lab2); vec3 color_temperature(float temperature); float color_estimateTemperature(vec3 rgb); vec3 color_adjustTemperature(vec3 rgb, float currentTemp, float targetTemp); vec3 color_posterize(vec3 rgb, float steps); vec3 color_colorize(vec3 rgb, vec3 tint, float strength); vec3 color_gammaAdjust(vec3 rgb, vec3 gamma); bool color_isColorBlindSafe(vec3 rgb1, vec3 rgb2); vec3 color_emphasizeForColorBlind(vec3 rgb); vec3 color_simulateProtanopia(vec3 rgb); vec3 color_simulateDeuteranopia(vec3 rgb); vec3 color_gradient3(vec3 color1, vec3 color2, vec3 color3, float t); vec3 color_smoothGradient(vec3 color1, vec3 color2, float t); vec3 color_radialGradient(vec3 center, vec3 edge, vec2 uv, vec2 center_pos); float color_checker(vec2 uv, float scale); float color_halftone(vec2 uv, float value, float frequency, float angle); vec3 color_toneSplit(vec3 rgb, vec3 shadows, vec3 midtones, vec3 highlights); vec3 color_monochromatic(vec3 rgb, float offset); vec3 color_weightedPalette(vec3 colors[4], vec4 weights); vec3 color_grade(vec3 rgb, vec3 lift, vec3 gamma, vec3 gain); //------------------------------------------------------------------------------ // COLOR HARMONY //------------------------------------------------------------------------------ vec3 color_complementary(vec3 rgb) { vec3 hsv = convert_rgb2hsv(rgb); hsv.x = fract(hsv.x + 0.5); // Rotate hue by 180 degrees return convert_hsv2rgb(hsv); } vec3[3] color_triadic(vec3 rgb) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[3]( rgb, convert_hsv2rgb(vec3(fract(hsv.x + 1.0/3.0), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x + 2.0/3.0), hsv.yz)) ); } vec3[3] color_splitComplementary(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[3]( rgb, convert_hsv2rgb(vec3(fract(hsv.x + 0.5 - angle), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x + 0.5 + angle), hsv.yz)) ); } vec3[4] color_tetradic(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[4]( rgb, convert_hsv2rgb(vec3(fract(hsv.x + 0.5), hsv.yz)), // Complement convert_hsv2rgb(vec3(fract(hsv.x + angle), hsv.yz)), // Third color convert_hsv2rgb(vec3(fract(hsv.x + angle + 0.5), hsv.yz)) // Fourth color ); } vec3[5] color_analogous(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[5]( convert_hsv2rgb(vec3(fract(hsv.x - angle*2.0), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x - angle), hsv.yz)), rgb, convert_hsv2rgb(vec3(fract(hsv.x + angle), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x + angle*2.0), hsv.yz)) ); } //------------------------------------------------------------------------------ // COLOR EFFECTS //------------------------------------------------------------------------------ vec3 color_duotone(vec3 rgb, vec3 dark, vec3 light) { float lum = color_luminance(rgb); return mix(dark, light, lum); } vec3 color_vibrance(vec3 rgb, float amount) { float mx = max(max(rgb.r, rgb.g), rgb.b); float avg = dot(rgb, vec3(1.0/3.0)); return mix(rgb, vec3(mx), (mx - avg) * (-amount * 3.0)); } vec3 color_levelAdjust(vec3 rgb, vec3 inBlack, vec3 inWhite, vec3 outBlack, vec3 outWhite) { return outBlack + (rgb - inBlack) * (outWhite - outBlack) / (inWhite - inBlack); } //------------------------------------------------------------------------------ // COLOR ANALYSIS //------------------------------------------------------------------------------ float color_perceivedBrightness(vec3 rgb) { // Uses perceived brightness formula return sqrt( rgb.r * rgb.r * 0.299 + rgb.g * rgb.g * 0.587 + rgb.b * rgb.b * 0.114 ); } float color_colorfulness(vec3 rgb) { vec3 hsv = convert_rgb2hsv(rgb); return hsv.y * hsv.z; } bool color_isNeutral(vec3 rgb, float threshold) { vec3 hsv = convert_rgb2hsv(rgb); return hsv.y < threshold; } //------------------------------------------------------------------------------ // GENERAL PURPOSE //------------------------------------------------------------------------------ vec3 color_saturate(vec3 rgb, float adjustment) { vec3 hsv = convert_rgb2hsv(rgb); hsv.y *= adjustment; return convert_hsv2rgb(hsv); } vec3 color_brighten(vec3 rgb, float adjustment) { vec3 hsv = convert_rgb2hsv(rgb); hsv.z = clamp(hsv.z * adjustment, 0.0, 1.0); return convert_hsv2rgb(hsv); } vec3 color_rotateHue(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); hsv.x = fract(hsv.x + angle); return convert_hsv2rgb(hsv); } // Tints color towards another color by amount (0-1) vec3 color_tint(vec3 base, vec3 tintColor, float amount) { return mix(base, tintColor * color_luminance(base), amount); } // Luminance calculation using Rec. 709 coefficients float color_luminance(vec3 rgb) { return dot(rgb, vec3(M_LUMA_R, M_LUMA_G, M_LUMA_B)); } // Contrast ratio calculation (WCAG) float color_contrastRatio(vec3 rgb1, vec3 rgb2) { float l1 = color_luminance(rgb1); float l2 = color_luminance(rgb2); float brightest = max(l1, l2); float darkest = min(l1, l2); return (brightest + 0.05) / (darkest + 0.05); } // Perceptual color difference (simple delta E) float color_deltaE(vec3 lab1, vec3 lab2) { return length(lab1 - lab2); } // Approximate blackbody radiation (temperature in Kelvin) vec3 color_temperature(float temperature) { temperature = clamp(temperature, 1000.0, 40000.0) / 100.0; vec3 color = vec3(1.0); bool under66 = temperature <= 66.0; // Red color.r = under66 ? 1.0 : 1.29293618606274509804 * pow(temperature - 60.0, -0.1332047592); // Green color.g = under66 ? 0.39008157876901960784 * log(temperature) - 0.63184144378862745098 : 1.12989086089529411765 * pow(temperature - 60.0, -0.0755148492); // Blue if(under66) { if(temperature <= 19.0) color.b = 0.0; else color.b = 0.54320678911019607843 * log(temperature - 10.0) - 1.19625408914; } return clamp(color, 0.0, 1.0); } float color_estimateTemperature(vec3 rgb) { // Approximate CCT using McCamy's formula float n = (rgb.x - rgb.z) / (rgb.y - rgb.z); return 449.0 * pow(n, 3.0) + 3525.0 * pow(n, 2.0) + 6823.3 * n + 5520.33; } vec3 color_adjustTemperature(vec3 rgb, float currentTemp, float targetTemp) { vec3 current = color_temperature(currentTemp); vec3 target = color_temperature(targetTemp); return rgb * (target / current); } vec3 color_posterize(vec3 rgb, float steps) { steps = clamp(float(steps), 1.0, 256.0); rgb = floor(rgb * steps + 0.0000001) / (steps - 1.0); if((rgb.r + rgb.g + rgb.b) / 3 > (255/2)) { rgb = pow(rgb, vec3(M_GAMMA_INV)); } return rgb; //float numSteps = clamp(float(steps), 1.0, 256.0); //return floor(rgb * numSteps + 0.0000001) / (numSteps - 1.0); } vec3 color_colorize(vec3 rgb, vec3 tint, float strength) { float luma = color_luminance(rgb); vec3 hsvTint = convert_rgb2hsv(tint); return convert_hsv2rgb(vec3(hsvTint.x, hsvTint.y * strength, luma)); } vec3 color_gammaAdjust(vec3 rgb, vec3 gamma) { return pow(rgb, 1.0 / gamma); } bool color_isColorBlindSafe(vec3 rgb1, vec3 rgb2) { // Uses WCAG 2.0 guidelines for color contrast float contrast = color_contrastRatio(rgb1, rgb2); return contrast >= 4.5; // Minimum contrast for normal text } vec3 color_emphasizeForColorBlind(vec3 rgb) { // Enhances differences in color_luminance and saturation vec3 hsv = convert_rgb2hsv(rgb); hsv.y = pow(hsv.y, 0.5); // Boost saturation hsv.z = pow(hsv.z, 0.8); // Adjust value return convert_hsv2rgb(hsv); } // Simulate color blindness types vec3 color_simulateProtanopia(vec3 rgb) { return rgb * mat3( 0.567, 0.433, 0.000, 0.558, 0.442, 0.000, 0.000, 0.242, 0.758 ); } vec3 color_simulateDeuteranopia(vec3 rgb) { return rgb * mat3( 0.625, 0.375, 0.000, 0.700, 0.300, 0.000, 0.000, 0.300, 0.700 ); } vec3 color_gradient3(vec3 color1, vec3 color2, vec3 color3, float t) { t = clamp(t, 0.0, 1.0); return t < 0.5 ? mix(color1, color2, t * 2.0) : mix(color2, color3, (t - 0.5) * 2.0); } vec3 color_smoothGradient(vec3 color1, vec3 color2, float t) { t = smoothstep(0.0, 1.0, t); return mix(color1, color2, t); } //------------------------------------------------------------------------------ // PATTERNS AND EFFECTS //------------------------------------------------------------------------------ // Creates a gradient in polar coordinates vec3 color_radialGradient(vec3 center, vec3 edge, vec2 uv, vec2 center_pos) { float dist = length(uv - center_pos); return mix(center, edge, smoothstep(0.0, 1.0, dist)); } // Creates a checker pattern float color_checker(vec2 uv, float scale) { vec2 pattern = floor(uv * scale); return mod(pattern.x + pattern.y, 2.0); } // Simulates halftone dot pattern float color_halftone(vec2 uv, float value, float frequency, float angle) { vec2 rotated = vec2( cos(angle) * uv.x - sin(angle) * uv.y, sin(angle) * uv.x + cos(angle) * uv.y ); vec2 nearest = 2.0 * fract(frequency * rotated) - 1.0; float dist = length(nearest); return step(dist, 2.0 * value - 1.0); } //------------------------------------------------------------------------------ // COLOR MODIFICATIONS //------------------------------------------------------------------------------ // Adjust color based on shadows, midtones, and highlights separately vec3 color_toneSplit(vec3 rgb, vec3 shadows, vec3 midtones, vec3 highlights) { float lum = color_luminance(rgb); float shadow = smoothstep(0.0, 0.5, lum); float highlight = smoothstep(0.5, 1.0, lum); vec3 mid = mix(shadows, midtones, shadow); return mix(mid, highlights, highlight); } // Create a monochromatic variation of a color vec3 color_monochromatic(vec3 rgb, float offset) { vec3 hsv = convert_rgb2hsv(rgb); return convert_hsv2rgb(vec3( hsv.x, mix(0.0, hsv.y, 0.5 + offset), mix(0.3, 1.0, offset) )); } // Create a palette with weighted mix of colors vec3 color_weightedPalette(vec3 colors[4], vec4 weights) { weights = weights / (weights.x + weights.y + weights.z + weights.w); return colors[0] * weights.x + colors[1] * weights.y + colors[2] * weights.z + colors[3] * weights.w; } // Advanced color grading vec3 color_grade(vec3 rgb, vec3 lift, vec3 gamma, vec3 gain) { vec3 liftedColor = rgb * (1.0 - lift) + lift; vec3 gammaCorrected = pow(liftedColor, 1.0 / gamma); return gammaCorrected * gain; } #endif uniform sampler2D image; // | RGB(A) image uniform int steps; // 16;2;255;1 | Pixel data range allowed void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; vec4 orig = texture(image, uv.xy); vec3 color = color_posterize(orig.rgb, steps); fragColor = vec4(color, orig.a); }
CategoryJOV_GL 🌈/COLOR

Inputs (3)

NameTypeDefaultDescription
imageoptIMAGERGB(A) image
stepsoptINT162–255Pixel data range allowed
FRAGMENToptSTRING// name: POSTERIZE // desc: Reduce the pixel color data range // category: COLOR #ifndef LIB_COLOR #define LIB_COLOR //------------------------------------------------------------------------------ // COLOR //------------------------------------------------------------------------------ #ifndef LIB_CONVERT #define LIB_CONVERT //------------------------------------------------------------------------------ // COLOR //------------------------------------------------------------------------------ #ifndef LIB_CONST #define LIB_CONST //------------------------------------------------------------------------------ // CONSTANT //------------------------------------------------------------------------------ #define M_EPSILON 1.0e-10 // zero value for float comparisons #define M_DEG2RAD 0.017453292519943 // Degree to radian conversion factor #define M_RAD2DEG 57.29577951308232 // Radian to degree conversion factor #define M_TAU 6.283185307179586 // TAU (2 * π) #define M_TAU_INV 0.159154943091895 // TAU Inverse (1 / TAU) #define M_PI 3.141592653589793 // π #define M_PI_INV 0.318309886183790 // π Inverse (1 / π) #define M_PI_2 1.570796326794896 // π divided by 2 (π / 2) #define M_PI_4 0.785398163397448 // π divided by 4 (π / 4) #define M_3PI_4 2.356194490192345 // 3 * π divided by 4 (3π / 4) #define M_PHI 1.618033988749895 // Golden ratio (φ) #define M_PHI_INV 0.618033988749895 // Inverse of golden ratio (1 / φ) #define M_PHI_SQ 2.618033988749895 // Square of the golden ratio (φ^2) #define M_PHI_SQRT5 0.723606797749979 // φ / √5 (useful for fibonacci spherical distribution) #define M_GOLD_ANG 2.399963229728653 // Golden angle in radians #define M_E 2.718281828459045 // Euler's number (base of natural logarithm) #define M_LOG2E 1.442695040888963 // Log base 2 of e #define M_LOG10E 0.434294481903252 // Log base 10 of e #define M_LN2 0.693147180559945 // Natural log of 2 #define M_LN10 2.302585092994046 // Natural log of 10 #define M_SQRT2 1.414213562373095 // Square root of 2 #define M_SQRT3 1.732050807568877 // Square root of 3 #define M_SQRT2_INV 0.707106781186547 // 1 divided by square root of 2 (1 / sqrt(2)) #define M_SQRT3_INV 0.577350269189626 // 1 divided by square root of 3 (1 / sqrt(3)) #define M_SQRT5 2.236067977499790 // Square root of 5 //------------------------------------------------------------------------------ // GENERAL //------------------------------------------------------------------------------ // useful for triangle interpolation vec3 barycentricCoords(vec2 p, vec2 a, vec2 b, vec2 c) { vec2 v0 = b - a; vec2 v1 = c - a; vec2 v2 = p - a; float d00 = dot(v0, v0); float d01 = dot(v0, v1); float d11 = dot(v1, v1); float d20 = dot(v2, v0); float d21 = dot(v2, v1); float denom = d00 * d11 - d01 * d01; vec3 result; result.y = (d11 * d20 - d01 * d21) / denom; result.z = (d00 * d21 - d01 * d20) / denom; result.x = 1.0 - result.y - result.z; return result; } #endif #define M_SRGB_ALPHA 0.055 #define M_SRGB_THRESH 0.04045 // LAB constants #define M_LAB_E 0.008856 // LAB epsilon #define M_LAB_K 903.3 // LAB kappa #define M_LAB_16_116 0.137931 // 16/116 // Additional illuminants #define M_D50 vec3(96.422, 100.0, 82.521) // D50 reference white #define M_D65 vec3(95.047, 100.0, 108.883) // D65 reference white #define M_D75 vec3(94.972, 100.0, 122.638) // D75 reference white // ============================================================================= // PROTOTYPES // ============================================================================= vec3 convert_rgb2hsv(vec3 rgb); vec3 convert_rgb2lab(vec3 rgb); vec3 convert_rgb2xyz(vec3 rgb); vec3 convert_hsv2rgb(vec3 hsv); vec3 convert_hsv2lab(vec3 hsv); vec3 convert_hsv2xyz(vec3 hsv); vec3 convert_lab2rgb(vec3 lab); vec3 convert_lab2hsv(vec3 lab); vec3 convert_lab2xyz(vec3 lab); vec3 convert_xyz2rgb(vec3 xyz); vec3 convert_xyz2hsv(vec3 xyz); vec3 convert_xyz2lab(vec3 xyz); vec3 convert_rgb2oklab(vec3 rgb); vec3 convert_oklab2rgb(vec3 lab); //------------------------------------------------------------------------------ // RGB //------------------------------------------------------------------------------ vec3 convert_rgb2hsv(vec3 rgb) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(rgb.bg, K.wz), vec4(rgb.gb, K.xy), step(rgb.b, rgb.g)); vec4 q = mix(vec4(p.xyw, rgb.r), vec4(rgb.r, p.yzx), step(p.x, rgb.r)); float d = q.x - min(q.w, q.y); return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + M_EPSILON)), d / (q.x + M_EPSILON), q.x); } vec3 convert_rgb2lab(vec3 rgb) { vec3 xyz = convert_rgb2xyz(rgb); return convert_xyz2lab(xyz); } vec3 convert_rgb2xyz(vec3 rgb) { vec3 tmp; tmp.x = (rgb.r > 0.04045) ? pow((rgb.r + 0.055) / 1.055, 2.4) : rgb.r / 12.92; tmp.y = (rgb.g > 0.04045) ? pow((rgb.g + 0.055) / 1.055, 2.4) : rgb.g / 12.92; tmp.z = (rgb.b > 0.04045) ? pow((rgb.b + 0.055) / 1.055, 2.4) : rgb.b / 12.92; return 100.0 * tmp * mat3( 0.4124, 0.3576, 0.1805, 0.2126, 0.7152, 0.0722, 0.0193, 0.1192, 0.9505 ); } //------------------------------------------------------------------------------ // HSV //------------------------------------------------------------------------------ vec3 convert_hsv2rgb(vec3 hsv) { hsv = vec3(hsv.x, clamp(hsv.yz, 0.0, 1.0)); vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(hsv.xxx + K.xyz) * 6.0 - K.www); return hsv.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsv.y); } vec3 convert_hsv2lab(vec3 hsv) { float H = hsv.x * 360.0; float S = hsv.y; float V = hsv.z; // Convert to LAB float L = V * 100.0; float C = S * L; float h = H * M_PI / 180.0; float a = C * cos(h); float b = C * sin(h); // Normalize LAB return vec3(L / 100.0, (a + 128.0) / 255.0, (b + 128.0) / 255.0); } vec3 convert_hsv2xyz(vec3 hsv) { vec3 rgb = convert_hsv2rgb(hsv); return convert_rgb2xyz(rgb); } //------------------------------------------------------------------------------ // LAB //------------------------------------------------------------------------------ vec3 convert_lab2rgb(vec3 lab) { vec3 xyz = convert_lab2xyz(lab); return convert_xyz2rgb(xyz); } vec3 convert_lab2hsv(vec3 lab) { vec3 rgb = convert_lab2rgb(lab); return convert_rgb2hsv(rgb); } vec3 convert_lab2xyz(vec3 lab) { float fy = (lab.x + 16.0) / 116.0; float fx = lab.y / 500.0 + fy; float fz = fy - lab.z / 200.0; vec3 f = vec3(fx, fy, fz); vec3 thresh = step(vec3(0.206897), f); vec3 xyz = mix( (f - vec3(16.0/116.0)) / 7.787, f * f * f, thresh ); return xyz * M_D65; } //------------------------------------------------------------------------------ // XYZ //------------------------------------------------------------------------------ vec3 convert_xyz2rgb(vec3 xyz) { vec3 v = xyz / M_D65; vec3 thresh = step(0.0031308, v); return mix( 12.92 * v, 1.055 * pow(v, vec3(1.0/2.4)) - 0.055, thresh ); } vec3 convert_xyz2hsv(vec3 xyz) { vec3 rgb = convert_xyz2rgb(xyz); return convert_rgb2hsv(rgb); } vec3 convert_xyz2lab(vec3 xyz) { vec3 n = xyz / M_D65; vec3 thresh = step(vec3(0.008856), n); vec3 v = mix( 7.787 * n + vec3(16.0/116.0), pow(n, vec3(1.0/3.0)), thresh ); return vec3( 116.0 * v.y - 16.0, 500.0 * (v.x - v.y), 200.0 * (v.y - v.z) ); } //------------------------------------------------------------------------------ // OKLAB //------------------------------------------------------------------------------ // RGB to Oklab (perceptually uniform color space) vec3 convert_rgb2oklab(vec3 rgb) { vec3 lms = rgb * mat3( 0.4122214708, 0.5363325363, 0.0514459929, 0.2119034982, 0.6806995451, 0.1073969566, 0.0883024619, 0.2817188376, 0.6299787005 ); lms = pow(lms, vec3(1.0/3.0)); return lms * mat3( 0.2104542553, 0.7936177850, -0.0040720468, 1.9779984951, -2.4285922050, 0.4505937099, 0.0259040371, 0.7827717662, -0.8086757660 ); } // Oklab to RGB vec3 convert_oklab2rgb(vec3 lab) { vec3 lms = lab * mat3( 1.0000000000, 0.3963377774, 0.2158037573, 1.0000000000, -0.1055613458, -0.0638541728, 1.0000000000, -0.0894841775, -1.2914855480 ); lms = lms * lms * lms; return lms * mat3( 4.0767416621, -3.3077115913, 0.2309699292, -1.2684380046, 2.6097574011, -0.3413193965, -0.0041960863, -0.7034186147, 1.7076147010 ); } #endif #define M_GAMMA 2.2 // Standard gamma correction value #define M_GAMMA_INV 0.4545 // 1.0 / 2.2 for inverse gamma #define M_LUMA_R 0.2126 // Rec. 709 luma coefficients for red #define M_LUMA_G 0.7152 // Rec. 709 luma coefficients for green #define M_LUMA_B 0.0722 // Rec. 709 luma coefficients for blue // ============================================================================= // PROTOTYPES // ============================================================================= vec3 color_complementary(vec3 rgb); vec3[3] color_triadic(vec3 rgb); vec3[3] color_splitComplementary(vec3 rgb, float angle); vec3[4] color_tetradic(vec3 rgb, float angle); vec3[5] color_analogous(vec3 rgb, float angle); vec3 color_duotone(vec3 rgb, vec3 dark, vec3 light); vec3 color_vibrance(vec3 rgb, float amount); vec3 color_levelAdjust(vec3 rgb, vec3 inBlack, vec3 inWhite, vec3 outBlack, vec3 outWhite); float color_perceivedBrightness(vec3 rgb); float color_colorfulness(vec3 rgb); bool color_isNeutral(vec3 rgb, float threshold); vec3 color_saturate(vec3 rgb, float adjustment); vec3 color_brighten(vec3 rgb, float adjustment); vec3 color_rotateHue(vec3 rgb, float angle); vec3 color_tint(vec3 base, vec3 tintColor, float amount); float color_luminance(vec3 rgb); float color_contrastRatio(vec3 rgb1, vec3 rgb2); float color_deltaE(vec3 lab1, vec3 lab2); vec3 color_temperature(float temperature); float color_estimateTemperature(vec3 rgb); vec3 color_adjustTemperature(vec3 rgb, float currentTemp, float targetTemp); vec3 color_posterize(vec3 rgb, float steps); vec3 color_colorize(vec3 rgb, vec3 tint, float strength); vec3 color_gammaAdjust(vec3 rgb, vec3 gamma); bool color_isColorBlindSafe(vec3 rgb1, vec3 rgb2); vec3 color_emphasizeForColorBlind(vec3 rgb); vec3 color_simulateProtanopia(vec3 rgb); vec3 color_simulateDeuteranopia(vec3 rgb); vec3 color_gradient3(vec3 color1, vec3 color2, vec3 color3, float t); vec3 color_smoothGradient(vec3 color1, vec3 color2, float t); vec3 color_radialGradient(vec3 center, vec3 edge, vec2 uv, vec2 center_pos); float color_checker(vec2 uv, float scale); float color_halftone(vec2 uv, float value, float frequency, float angle); vec3 color_toneSplit(vec3 rgb, vec3 shadows, vec3 midtones, vec3 highlights); vec3 color_monochromatic(vec3 rgb, float offset); vec3 color_weightedPalette(vec3 colors[4], vec4 weights); vec3 color_grade(vec3 rgb, vec3 lift, vec3 gamma, vec3 gain); //------------------------------------------------------------------------------ // COLOR HARMONY //------------------------------------------------------------------------------ vec3 color_complementary(vec3 rgb) { vec3 hsv = convert_rgb2hsv(rgb); hsv.x = fract(hsv.x + 0.5); // Rotate hue by 180 degrees return convert_hsv2rgb(hsv); } vec3[3] color_triadic(vec3 rgb) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[3]( rgb, convert_hsv2rgb(vec3(fract(hsv.x + 1.0/3.0), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x + 2.0/3.0), hsv.yz)) ); } vec3[3] color_splitComplementary(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[3]( rgb, convert_hsv2rgb(vec3(fract(hsv.x + 0.5 - angle), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x + 0.5 + angle), hsv.yz)) ); } vec3[4] color_tetradic(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[4]( rgb, convert_hsv2rgb(vec3(fract(hsv.x + 0.5), hsv.yz)), // Complement convert_hsv2rgb(vec3(fract(hsv.x + angle), hsv.yz)), // Third color convert_hsv2rgb(vec3(fract(hsv.x + angle + 0.5), hsv.yz)) // Fourth color ); } vec3[5] color_analogous(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); return vec3[5]( convert_hsv2rgb(vec3(fract(hsv.x - angle*2.0), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x - angle), hsv.yz)), rgb, convert_hsv2rgb(vec3(fract(hsv.x + angle), hsv.yz)), convert_hsv2rgb(vec3(fract(hsv.x + angle*2.0), hsv.yz)) ); } //------------------------------------------------------------------------------ // COLOR EFFECTS //------------------------------------------------------------------------------ vec3 color_duotone(vec3 rgb, vec3 dark, vec3 light) { float lum = color_luminance(rgb); return mix(dark, light, lum); } vec3 color_vibrance(vec3 rgb, float amount) { float mx = max(max(rgb.r, rgb.g), rgb.b); float avg = dot(rgb, vec3(1.0/3.0)); return mix(rgb, vec3(mx), (mx - avg) * (-amount * 3.0)); } vec3 color_levelAdjust(vec3 rgb, vec3 inBlack, vec3 inWhite, vec3 outBlack, vec3 outWhite) { return outBlack + (rgb - inBlack) * (outWhite - outBlack) / (inWhite - inBlack); } //------------------------------------------------------------------------------ // COLOR ANALYSIS //------------------------------------------------------------------------------ float color_perceivedBrightness(vec3 rgb) { // Uses perceived brightness formula return sqrt( rgb.r * rgb.r * 0.299 + rgb.g * rgb.g * 0.587 + rgb.b * rgb.b * 0.114 ); } float color_colorfulness(vec3 rgb) { vec3 hsv = convert_rgb2hsv(rgb); return hsv.y * hsv.z; } bool color_isNeutral(vec3 rgb, float threshold) { vec3 hsv = convert_rgb2hsv(rgb); return hsv.y < threshold; } //------------------------------------------------------------------------------ // GENERAL PURPOSE //------------------------------------------------------------------------------ vec3 color_saturate(vec3 rgb, float adjustment) { vec3 hsv = convert_rgb2hsv(rgb); hsv.y *= adjustment; return convert_hsv2rgb(hsv); } vec3 color_brighten(vec3 rgb, float adjustment) { vec3 hsv = convert_rgb2hsv(rgb); hsv.z = clamp(hsv.z * adjustment, 0.0, 1.0); return convert_hsv2rgb(hsv); } vec3 color_rotateHue(vec3 rgb, float angle) { vec3 hsv = convert_rgb2hsv(rgb); hsv.x = fract(hsv.x + angle); return convert_hsv2rgb(hsv); } // Tints color towards another color by amount (0-1) vec3 color_tint(vec3 base, vec3 tintColor, float amount) { return mix(base, tintColor * color_luminance(base), amount); } // Luminance calculation using Rec. 709 coefficients float color_luminance(vec3 rgb) { return dot(rgb, vec3(M_LUMA_R, M_LUMA_G, M_LUMA_B)); } // Contrast ratio calculation (WCAG) float color_contrastRatio(vec3 rgb1, vec3 rgb2) { float l1 = color_luminance(rgb1); float l2 = color_luminance(rgb2); float brightest = max(l1, l2); float darkest = min(l1, l2); return (brightest + 0.05) / (darkest + 0.05); } // Perceptual color difference (simple delta E) float color_deltaE(vec3 lab1, vec3 lab2) { return length(lab1 - lab2); } // Approximate blackbody radiation (temperature in Kelvin) vec3 color_temperature(float temperature) { temperature = clamp(temperature, 1000.0, 40000.0) / 100.0; vec3 color = vec3(1.0); bool under66 = temperature <= 66.0; // Red color.r = under66 ? 1.0 : 1.29293618606274509804 * pow(temperature - 60.0, -0.1332047592); // Green color.g = under66 ? 0.39008157876901960784 * log(temperature) - 0.63184144378862745098 : 1.12989086089529411765 * pow(temperature - 60.0, -0.0755148492); // Blue if(under66) { if(temperature <= 19.0) color.b = 0.0; else color.b = 0.54320678911019607843 * log(temperature - 10.0) - 1.19625408914; } return clamp(color, 0.0, 1.0); } float color_estimateTemperature(vec3 rgb) { // Approximate CCT using McCamy's formula float n = (rgb.x - rgb.z) / (rgb.y - rgb.z); return 449.0 * pow(n, 3.0) + 3525.0 * pow(n, 2.0) + 6823.3 * n + 5520.33; } vec3 color_adjustTemperature(vec3 rgb, float currentTemp, float targetTemp) { vec3 current = color_temperature(currentTemp); vec3 target = color_temperature(targetTemp); return rgb * (target / current); } vec3 color_posterize(vec3 rgb, float steps) { steps = clamp(float(steps), 1.0, 256.0); rgb = floor(rgb * steps + 0.0000001) / (steps - 1.0); if((rgb.r + rgb.g + rgb.b) / 3 > (255/2)) { rgb = pow(rgb, vec3(M_GAMMA_INV)); } return rgb; //float numSteps = clamp(float(steps), 1.0, 256.0); //return floor(rgb * numSteps + 0.0000001) / (numSteps - 1.0); } vec3 color_colorize(vec3 rgb, vec3 tint, float strength) { float luma = color_luminance(rgb); vec3 hsvTint = convert_rgb2hsv(tint); return convert_hsv2rgb(vec3(hsvTint.x, hsvTint.y * strength, luma)); } vec3 color_gammaAdjust(vec3 rgb, vec3 gamma) { return pow(rgb, 1.0 / gamma); } bool color_isColorBlindSafe(vec3 rgb1, vec3 rgb2) { // Uses WCAG 2.0 guidelines for color contrast float contrast = color_contrastRatio(rgb1, rgb2); return contrast >= 4.5; // Minimum contrast for normal text } vec3 color_emphasizeForColorBlind(vec3 rgb) { // Enhances differences in color_luminance and saturation vec3 hsv = convert_rgb2hsv(rgb); hsv.y = pow(hsv.y, 0.5); // Boost saturation hsv.z = pow(hsv.z, 0.8); // Adjust value return convert_hsv2rgb(hsv); } // Simulate color blindness types vec3 color_simulateProtanopia(vec3 rgb) { return rgb * mat3( 0.567, 0.433, 0.000, 0.558, 0.442, 0.000, 0.000, 0.242, 0.758 ); } vec3 color_simulateDeuteranopia(vec3 rgb) { return rgb * mat3( 0.625, 0.375, 0.000, 0.700, 0.300, 0.000, 0.000, 0.300, 0.700 ); } vec3 color_gradient3(vec3 color1, vec3 color2, vec3 color3, float t) { t = clamp(t, 0.0, 1.0); return t < 0.5 ? mix(color1, color2, t * 2.0) : mix(color2, color3, (t - 0.5) * 2.0); } vec3 color_smoothGradient(vec3 color1, vec3 color2, float t) { t = smoothstep(0.0, 1.0, t); return mix(color1, color2, t); } //------------------------------------------------------------------------------ // PATTERNS AND EFFECTS //------------------------------------------------------------------------------ // Creates a gradient in polar coordinates vec3 color_radialGradient(vec3 center, vec3 edge, vec2 uv, vec2 center_pos) { float dist = length(uv - center_pos); return mix(center, edge, smoothstep(0.0, 1.0, dist)); } // Creates a checker pattern float color_checker(vec2 uv, float scale) { vec2 pattern = floor(uv * scale); return mod(pattern.x + pattern.y, 2.0); } // Simulates halftone dot pattern float color_halftone(vec2 uv, float value, float frequency, float angle) { vec2 rotated = vec2( cos(angle) * uv.x - sin(angle) * uv.y, sin(angle) * uv.x + cos(angle) * uv.y ); vec2 nearest = 2.0 * fract(frequency * rotated) - 1.0; float dist = length(nearest); return step(dist, 2.0 * value - 1.0); } //------------------------------------------------------------------------------ // COLOR MODIFICATIONS //------------------------------------------------------------------------------ // Adjust color based on shadows, midtones, and highlights separately vec3 color_toneSplit(vec3 rgb, vec3 shadows, vec3 midtones, vec3 highlights) { float lum = color_luminance(rgb); float shadow = smoothstep(0.0, 0.5, lum); float highlight = smoothstep(0.5, 1.0, lum); vec3 mid = mix(shadows, midtones, shadow); return mix(mid, highlights, highlight); } // Create a monochromatic variation of a color vec3 color_monochromatic(vec3 rgb, float offset) { vec3 hsv = convert_rgb2hsv(rgb); return convert_hsv2rgb(vec3( hsv.x, mix(0.0, hsv.y, 0.5 + offset), mix(0.3, 1.0, offset) )); } // Create a palette with weighted mix of colors vec3 color_weightedPalette(vec3 colors[4], vec4 weights) { weights = weights / (weights.x + weights.y + weights.z + weights.w); return colors[0] * weights.x + colors[1] * weights.y + colors[2] * weights.z + colors[3] * weights.w; } // Advanced color grading vec3 color_grade(vec3 rgb, vec3 lift, vec3 gamma, vec3 gain) { vec3 liftedColor = rgb * (1.0 - lift) + lift; vec3 gammaCorrected = pow(liftedColor, 1.0 / gamma); return gammaCorrected * gain; } #endif uniform sampler2D image; // | RGB(A) image uniform int steps; // 16;2;255;1 | Pixel data range allowed void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord / iResolution.xy; vec4 orig = texture(image, uv.xy); vec3 color = color_posterize(orig.rgb, steps); fragColor = vec4(color, orig.a); }

Outputs (3)

NameTypeDescription
RGBAIMAGEFull channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output.
RGBIMAGEThree channel [RGB] image. There will be no alpha.
MASKMASKSingle channel mask output.