I wrote a simple canvas_item
shader that gets an input base palette (the original palette of the sprite) and a new palette (the replacement palette) and correctly replaces the original colors with the new colors.
With this shader I don't need to create any sprite masks, I can use the original sprites.
Sprite + base palette:


Tile created by https://gumroad.com/mnrart.
New palette + sprite with colors replaced (shader running) - don't mind the saturated colors, I chose them to be easier to see the shader at work:


To be able to find the replacement color, for every pixel, I search the original color with a custom function called find_color_in_base_palette
, which loops the base_palette
looking for the color (the function returns the x
position of the color). Then I read the color from the new_palette
that is in the same pixel position.
- I am happy with the end result (It works!), but I am not totally comfortable with using a loop to look for the base color for EVERY pixel of the sprite. Is there any better way to do it?
- I am not sure about the number that comes out of
textureSize(base_palette, 0)
. In the loop I iterate from 0 up to the x textureSize: for(float x = 0.0; x <= pal_size; x += pixel_size). Since there is no way to debug values, I wonder what exact value comes from textureSize
?
The shader:
shader_type canvas_item;
render_mode blend_mix;
uniform sampler2D base_palette;
uniform sampler2D new_palette;
float find_color_in_base_palette(in vec4 color, float pal_size, float pixel_size) {
for(float x = 0.0; x <= pal_size; x += pixel_size) {
vec4 pal_col = texture(base_palette, vec2(x, 0.0));
if(pal_col.rgba == color.rgba) {
return x;
}
}
return -1.0;
}
void fragment() {
vec4 color = texture(TEXTURE, UV);
ivec2 size = textureSize(base_palette, 0);
float pos = find_color_in_base_palette(color, float(size.x), TEXTURE_PIXEL_SIZE.x);
// We found the position of the color in the base palette, so fetch a new color from the new palette
if(pos != -1.0) {
COLOR = texture(new_palette, vec2(pos, 0.0));
}
// The color is not in the base palette, so we don't know its position. Keep the base color.
else {
COLOR = color;
}
}