OKLCH Posterization/Color Palette Shader
A godot 4.4 posterization/color palette shader that utitlizes the OKLCH color space.
Uniforms
- mix_amount → determines how much the color palette affects the screen colors.
- steps_l → controls the quantitization of the screen colors.
- level_c → controls the chroma (0.0 = gray).
- level_h → controls the hue level.
Instructions
- In the gdshaderinc file, include all the colors you wish to use for your color palette. If you do not wish to use a color palette, simply set the mix_amount uniform to 0.0.
- To quickly convert from hex to vec3, see Convert Hex Color to GLSL vec3
- In the main shader, make sure the #include filepath is correct.
- Apply the shader to the material section of a color rect node and set its anchors preset to full rect.
Shader code
/*
Shader from Godot Shaders - the free shader library.
godotshaders.com/shader/oklch-posterization-color-palette-shader
MIT License. Made by Yūgen. Feel free to use, improve and change this shader according to your needs
and consider sharing the modified result on godotshaders.com.
Inspired by https://gist.github.com/ronniebasak/e5331e54cf9414ab0fec23b4f6a27e2a
And https://www.youtube.com/watch?v=Gjr2CUczDpk
For color palette inspiration, see https://lospec.com/palette-list
*/
// Posterization effect using the oklch (lightness, chroma, hue) color space.
shader_type canvas_item;
#include "res://shaders/file_path_here"
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_nearest;
group_uniforms Color_Palette;
uniform float mix_amount = 1.0;
group_uniforms;
group_uniforms OKLCH_Values;
uniform bool use_l = true;
uniform bool use_c = true;
uniform bool use_h = true;
uniform float steps_l : hint_range(1.0, 50.0, 1.0) = 25;
uniform float level_c : hint_range(0.01, 15.0) = 5.0;
uniform float level_h : hint_range(0.01, 0.1) = 0.05;
group_uniforms;
vec3 get_nearest_color(vec3 color) {
float min_diff = 1.0;
vec3 nearest_color = color;
for (int i = 0; i < COLOR_PALETTE.length(); i++) {
float curr_dist = distance(COLOR_PALETTE[i], color);
if (curr_dist < min_diff) {
min_diff = curr_dist;
nearest_color = COLOR_PALETTE[i];
}
}
return nearest_color;
}
vec3 rgb_to_oklch(vec3 rgb) {
// convert srgb to linear rgb
vec3 linear_rgb;
linear_rgb.r = (rgb.r <= 0.04045) ? rgb.r / 12.92 : pow((rgb.r + 0.055) / 1.055, 2.4);
linear_rgb.g = (rgb.g <= 0.04045) ? rgb.g / 12.92 : pow((rgb.g + 0.055) / 1.055, 2.4);
linear_rgb.b = (rgb.b <= 0.04045) ? rgb.b / 12.92 : pow((rgb.b + 0.055) / 1.055, 2.4);
// convert linear rgb to xyz (CIE 1931 XYZ color space) primary color space
float x = linear_rgb.r * 0.4124 + linear_rgb.g * 0.3576 + linear_rgb.b * 0.1805;
float y = linear_rgb.r * 0.2126 + linear_rgb.g * 0.7152 + linear_rgb.b * 0.0722;
float z = linear_rgb.r * 0.0193 + linear_rgb.g * 0.1192 + linear_rgb.b * 0.9505;
// CIE standard illuminant D65 (color temperature of ~6504K) in the xyz color space
// For reference: https://en.wikipedia.org/wiki/Standard_illuminant
float xd65 = 0.95047;
float yd65 = 1.0;
float zd65 = 1.08883;
// normalize xyz color values by the natural daylight factor
float x_norm = x / xd65;
float y_norm = y / yd65;
float z_norm = z / zd65;
// precompute xyz values for oklab conversion later
float fx = (x_norm > 0.008856) ? pow(x_norm, 1.0 / 3.0) : (7.787 * x_norm + 16.0 / 116.0);
float fy = (y_norm > 0.008856) ? pow(y_norm, 1.0 / 3.0) : (7.787 * y_norm + 16.0 / 116.0);
float fz = (z_norm > 0.008856) ? pow(z_norm, 1.0 / 3.0) : (7.787 * z_norm + 16.0 / 116.0);
// convert from xyz to oklab
float l_lab = 116.0 * fy - 16.0;
float a_lab = 500.0 * (fx - fy);
float b_lab = 200.0 * (fy - fz);
// convert from oklab to oklch
float c = sqrt(a_lab * a_lab + b_lab * b_lab);
float h = degrees(atan(b_lab, a_lab));
if (h < 0.0) {
h += 360.0;
}
return vec3(l_lab / 100.0, c / 100.0, h);
}
vec3 oklch_to_rgb(vec3 lch) {
// scale l and h back to values between 0 and 100 and h to radians from degrees
float l = lch.x * 100.0;
float c = lch.y * 100.0;
float h_rad = radians(lch.z);
// convert c and h to oklab a and b
float a_lab = cos(h_rad) * c;
float b_lab = sin(h_rad) * c;
// precompute oklab values for xyz conversion later
float fy = (l + 16.0) / 116.0;
float fx = a_lab / 500.0 + fy;
float fz = fy - b_lab / 200.0;
vec3 f = vec3(fx, fy, fz);
vec3 f_cubed = pow(f, vec3(3.0));
// inverse oklab (to xyz) conversion
vec3 mask_xyz = step(vec3(0.008856), f_cubed);
vec3 xyz = mask_xyz * f_cubed + (1.0 - mask_xyz) * ((f - vec3(16.0/116.0)) / 7.787);
vec3 d65 = vec3(0.95047, 1.0, 1.08883);
xyz *= d65;
// convert from xyz to linear rgb
float r = xyz.x * 3.2406 + xyz.y * -1.5372 + xyz.z * -0.4986;
float g = xyz.x * -0.9689 + xyz.y * 1.8758 + xyz.z * 0.0415;
float b = xyz.x * 0.0557 + xyz.y * -0.2040 + xyz.z * 1.0570;
vec3 linear_rgb = vec3(r, g, b);
// gamma correction mask
vec3 linear_gamma = linear_rgb * 12.92;
vec3 non_linear_gamma = 1.055 * pow(linear_rgb, vec3(1.0 / 2.4)) - 0.055;
// linear rgb to srgb
vec3 mask_rgb = step(linear_rgb, vec3(0.0031308));
vec3 srgb = mix(non_linear_gamma, linear_gamma, mask_rgb);
return clamp(srgb, 0.0, 1.0);
}
float posterize(float value, float levels) {
return round(value * levels) / levels;
}
void fragment() {
vec4 original_sc = texture(SCREEN_TEXTURE, SCREEN_UV);
vec3 oklch = rgb_to_oklch(original_sc.rgb);
if (use_l)
oklch.r = posterize(oklch.r, steps_l);
if (use_c)
oklch.g = posterize(oklch.g, level_c);
if (use_h)
oklch.b = posterize(oklch.b, level_h);
vec3 posterized_sc = oklch_to_rgb(oklch);
vec3 nearest_color = get_nearest_color(posterized_sc);
vec3 final_sc = mix(posterized_sc, nearest_color, mix_amount);
COLOR = vec4(final_sc, 1.0);
}
// Example palette.gdshaderinc ↓
const vec3 COLOR_PALETTE[11] = vec3[](
vec3(0.137, 0.137, 0.137), // #232323
vec3(0.227, 0.227, 0.227), // #393939
vec3(0.318, 0.318, 0.318), // #515151
vec3(0.409, 0.409, 0.409), // #696969
vec3(0.500, 0.500, 0.500), // #808080
vec3(0.591, 0.591, 0.591), // #999999
vec3(0.682, 0.682, 0.682), // #B0B0B0
vec3(0.773, 0.773, 0.773), // #C4C4C4
vec3(0.864, 0.864, 0.864), // #D8D8D8
vec3(0.955, 0.955, 0.955), // #F2F2F2
vec3(1.000, 1.000, 1.000) // #FFFFFF
);




what type of file I will need to include here?
“ShaderInclude” type resource (.gdshaderinc)
#include “res://shaders/palette.gdshaderinc”
it must define COLOR_PALETTE
Egune already said everything but just make sure to specify the amount of colors inside the array correctly and change the array size accordingly