3-Line Color Array Palettes, with Shifting
Compact, expandable, no conditionals bc GPUs hate those. Uses R channel of sprite color. Uses alpha from color array, multiplied against alpha from the base sprite.
For the standard <=11 colors, increment the R channel by 25 at a time for each color, up to 250. (Red at 25 == COLOR.r at .1 == colors[1])
For more colors, you could bump up the array allocation to [21], then increment the R channel by 12/13.
Be sure to set array_max
if changing array size. It’s used to keep indices in-range after applying offset; out-of-range values will return vec4(0.0).
Use index_offset
to shift up/down the color array. Different versions of return_index()
provide different behavior, or disable this feature entirely. (see code)
For a highly detailed final product, you could use another color channel as a multiplier for the final RGB value, or as a mix()
factor to shift towards a “shadow” color (or an array of them!)
Note: return_index()
‘s default “Looping” behavior acts strange for index_offset
values in the low negatives, effectively making them a deadzone. If incrementing offset downwards, you can start at -15
since the loop seems to work as expected from there, or just use descending positive values on repeat. (If you can tell me why this happens, please do, I’m at a loss here lmao)
Artist QoL — if the blue-green mess of the “true” color hurts your eyes, you can (for example) use indexed palettes in Aseprite to switch between more varied placeholders and our “true” colors.
Shader code
shader_type canvas_item;
uniform int index_offset;
uniform vec4 colors[11]: source_color;
uniform int array_max = 11;
// Swap out the following return_index() funcs to change index shift behavior.
// LOOPING -- Out-of-range indices are looped around to the other side of the array.
// Useful for flashing effects. If using negative offset values, start at -15.
int return_index(float raw) { return (int(round(raw * 10.0)) + index_offset) % array_max; }
// CLAMPING -- Out-of-range indices are clamped to the edges of the array.
// Useful for fading effects.
// int return_index(float raw) { return min(array_max-1, max(0, (int(round(raw * 10.0)) + index_offset))); }
// NONE -- Disable index shifting.
// You can also comment out the index_offset and array_max declarations.
// int return_index(float raw) { return int(round(raw * 10.0)) }
void fragment() {
float alpha = COLOR.a;
COLOR = colors[return_index(COLOR.r)]; // Red 25 == COLOR.r .1 == colors[1]
COLOR.a *= alpha;
}