Palette Swap (no recolor / recolor)
Okay, so you want to a palette swap. First, let’s get a sprite. Let’s use a sprite from Pixel Adventure by Pixel Frog because it was the first thing I saw on itch.io. I’ll be using the slime since it has five colors.
First, let’s do the version that doesn’t require you to recolor the sprite. Let’s start by getting the colors of the sprite. Shader colors are vec4s of floats, so you have to convert them to a float between 0 and 1.
Next, get the colors that you want to convert the colors to. You can make them uniforms so that you can edit them easier and so you can just use the color picker instead of trying to convert colors to float vectors.
Now, compare the color of the current pixel to the colors that you’re looking to swap to. Note that float comparison is imprecise so you can’t use ==. You can use the distance() function instead.
shader_type canvas_item;
uniform vec4 original_0: hint_color;
uniform vec4 original_1: hint_color;
uniform vec4 original_2: hint_color;
uniform vec4 original_3: hint_color;
uniform vec4 original_4: hint_color;
uniform vec4 replace_0: hint_color;
uniform vec4 replace_1: hint_color;
uniform vec4 replace_2: hint_color;
uniform vec4 replace_3: hint_color;
uniform vec4 replace_4: hint_color;
const float precision = 0.1;
vec4 swap_color(vec4 color){
vec4 original_colors[5] = vec4[5] (original_0, original_1, original_2, original_3, original_4);
vec4 replace_colors[5] = vec4[5] (replace_0, replace_1, replace_2, replace_3, replace_4);
for (int i = 0; i < 5; i ++) {
if (distance(color, original_colors[i]) <= precision){
return replace_colors[i];
}
}
return color;
}
void fragment() {
COLOR = swap_color(texture(TEXTURE, UV));
}
Okay, now let’s do the method that requires you to require to recolor the sprite. Here, we’ll be using UV coordinates to determine what color to set the current pixel to.
First, make a palette. Okay – now that you have the palette made, note the UV coordinates of the top-left corner of each color. The UV coordinates are floats that correspond to how far the pixel is from the origin – so a U coordinate of 0.5 is halfway across the image’s width.
Now, for each color you want to recolor, change the RED value to the U-coordinate of the color you want to replace it with in the palette, and the GREEN value to the V-coordinate (BLUE value can be whatever you want, I guess). The result should look pretty weird but don’t worry, the shader will fix it!
Now you also need to make a uniform for your palette. Its type should be sampler2D.
shader_type canvas_item;
uniform sampler2D palette;
void fragment() {
COLOR = texture(palette, texture(TEXTURE, UV).rg);
}
If you want to preserve the alpha channel you can use this code instead:
void fragment() {
vec4 color = texture(TEXTURE, UV);
vec4 result_color = texture(palette, color.rg);
result_color.a = color.a;
COLOR = result_color;
}
And there you have it! Two different ways to do a palette swap with a Godot shader!
Shader code
// No recolor
shader_type canvas_item;
uniform vec4 original_0: hint_color;
uniform vec4 original_1: hint_color;
uniform vec4 original_2: hint_color;
uniform vec4 original_3: hint_color;
uniform vec4 original_4: hint_color;
uniform vec4 replace_0: hint_color;
uniform vec4 replace_1: hint_color;
uniform vec4 replace_2: hint_color;
uniform vec4 replace_3: hint_color;
uniform vec4 replace_4: hint_color;
const float precision = 0.1;
vec4 swap_color(vec4 color){
vec4 original_colors[5] = vec4[5] (original_0, original_1, original_2, original_3, original_4);
vec4 replace_colors[5] = vec4[5] (replace_0, replace_1, replace_2, replace_3, replace_4);
for (int i = 0; i < 5; i ++) {
if (distance(color, original_colors[i]) <= precision){
return replace_colors[i];
}
}
return color;
}
void fragment() {
COLOR = swap_color(texture(TEXTURE, UV));
}
// Recolor
shader_type canvas_item;
uniform sampler2D palette;
void fragment() {
vec4 color = texture(TEXTURE, UV);
vec4 result_color = texture(palette, color.rg);
result_color.a = color.a;
COLOR = result_color;
}
Thanks for the share, it works great
Thanks!
I will certainly be bookmarking this as I’ve been looking for a good, easy to use palette swap shader for a while now! Thank you very much, this is a well written shader with a very well written post to accompany it!
Thank you!
Out of curiosity, is there a way to kind of combine these two? Taking the first shader, but instead of using the color picker for picking colors, you instead provide an input and output palette texture. Input being the colors in the [base] sprite, output being the new palette.