POSTERIZE (JOV_GL)
Posterize isn't an Instagram filter — it's a shader that runs on your GPU
- image
- RGBA
- RGB
- MASK
Posterize is the "how few colors can we get away with" knob. Drop it on a smooth render and suddenly you have that flat, banded, print-shop look - the one people chase with a dozen different color-grading tricks. This node does it in one pass, on your GPU, and it's about the simplest way in ComfyUI to push an image from "photographic" toward "graphic."
What it actually does
Every node in the Jovi_GLSL pack is a compiled GLSL fragment shader, and this one is the posterize pass: it takes each pixel's RGB value and snaps it down to a fixed number of discrete steps. The shader does floor(rgb * steps) / (steps - 1) per channel - nothing clever, just quantization, plus a small gamma tweak for bright areas so highlights don't fall apart.
Because it runs per-pixel on the GPU through the pack's OpenGL pipeline, it's effectively instant, even at high resolution or on a batch of frames. That's the whole pitch of this pack: shader-style image ops at shader speed instead of waiting on CPU nodes.
The inputs that matter
- image - any RGB, RGBA, or even a pure MASK input. If you feed it a mask, you get a posterized mask back.
- steps - the number of color levels per channel, from 2 to 255, default 16. This is the entire creative control.
That's it. Two inputs, one idea. The FRAGMENT field you'll see on the node is the shader source itself - leave it alone unless you want to start hacking GLSL, in which case it's a genuinely nice way to learn.
All Jovi_GLSL nodes give you the same three outputs, and this one is no different:
- RGBA - full four-channel output; if the input had an alpha, it's applied here.
- RGB - no alpha, useful when you're wiring into nodes that choke on RGBA.
- MASK - the single-channel version, handy for feeding the result back into compositing or ControlNet-adjacent tricks.
How to install it
This is the same install for every node in the pack, so do it once:
Via ComfyUI Manager, search Jovi_GLSL and install. Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/Amorano/Jovi_GLSL.git
cd Jovi_GLSL
pip install -r requirements.txt
Then restart ComfyUI. Note the requirements aren't trivial - this pack pulls in PyOpenGL, glfw, and opencv-contrib-python, plus cozy_comfyui (installed straight from git @main). It needs a working OpenGL context at runtime, so on a truly headless server that's the first thing to suspect if nodes won't execute. No model downloads, no API keys - the author (Alexander Morano, the Jovimetrix guy) kept this one self-contained.
Where people get burned
The trap is treating steps like a brightness slider. It's not. Cranking it toward 255 does almost nothing - the image already has plenty of levels - while anything under ~8 turns your render into deliberate-looking bands, which is either exactly what you want or aggressively not. Also remember the alpha survives: posterize the RGB and keep the original mask, which makes this a great prep step for stylized compositing rather than just a filter to slap on the final image.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| imageopt | IMAGE | RGB(A) image | |
| stepsopt | INT | 162–255 | Pixel data range allowed |
| FRAGMENTopt | STRING | // 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)
| Name | Type | Description |
|---|---|---|
| RGBA | IMAGE | Full channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output. |
| RGB | IMAGE | Three channel [RGB] image. There will be no alpha. |
| MASK | MASK | Single channel mask output. |