Skip to content

Instantly share code, notes, and snippets.

@robinduckett
Last active March 5, 2023 03:22
Show Gist options
  • Save robinduckett/7e9089596a3bd4b497e9e90568f8238e to your computer and use it in GitHub Desktop.
Save robinduckett/7e9089596a3bd4b497e9e90568f8238e to your computer and use it in GitHub Desktop.
A Godot shader to replace one colour in a palette with another (with a mask support for additional colours)

You must create a palette of colours and set them to the shader's parameters before use in the manner used by the Sprite script source code palette.gd

shader_type canvas_item;
uniform highp vec2 mask_position;
uniform highp vec2 mask_size;
uniform highp vec2 sprite_size;
uniform mat4 global_transform;
uniform sampler2D palette_swap : hint_albedo;
uniform sampler2D palette_with : hint_albedo;
uniform sampler2D palette_swap_mask : hint_white;
uniform sampler2D palette_swap_with : hint_white;
int size(sampler2D tex) {
return int(textureSize(tex, 0).x);
}
vec4 lookupPaletteSwap(sampler2D swap, sampler2D with, vec4 currcol, vec2 TEXTURE_PIXEL_SIZE) {
for (int i = 0; i < size(swap); i++) {
vec2 lookup = vec2(float(i) / float(size(swap)), 0.0);
vec4 color = texture(swap, lookup);
vec4 rcolor = texture(with, lookup);
if (currcol == color) {
return rcolor;
}
}
return currcol;
}
void fragment() {
vec4 currcol = lookupPaletteSwap(palette_swap, palette_with, texture(TEXTURE, UV), TEXTURE_PIXEL_SIZE);
vec2 origin = vec2(global_transform[3][0], global_transform[3][1]) - sprite_size;
vec2 rect1Pos = (origin + mask_position) * SCREEN_PIXEL_SIZE;
vec2 rect1Size = mask_size * SCREEN_PIXEL_SIZE;
vec2 normUV = vec2(SCREEN_UV.x, 1.0-SCREEN_UV.y);
if (normUV.x > rect1Pos.x && normUV.x < (rect1Pos + rect1Size).x) {
if (normUV.y > rect1Pos.y && normUV.y < (rect1Pos + rect1Size).y) {
currcol = lookupPaletteSwap(palette_swap_mask, palette_swap_with, texture(TEXTURE, UV), TEXTURE_PIXEL_SIZE);
}
}
COLOR = currcol;
}
extends Sprite
func generate_image(colors):
var swap = Image.new();
swap.create(colors.size(), 1, false, Image.FORMAT_RGBA8);
for i in range(0, colors.size()):
var color = colors[i];
swap.lock()
swap.set_pixel(i, 0, color)
swap.unlock()
var texture = ImageTexture.new()
texture.create_from_image(swap, 0);
return texture;
# Use your own custom colours here
func generate_palettes():
var swaps = [
Color(240.0 / 255.0, 128.0 / 255.0, 0.0, 1.0),
Color(200.0 / 255.0, 80.0 / 255.0, 0.0, 1.0),
Color(136.0 / 255.0, 48.0 / 255.0, 0.0, 1.0),
Color(112.0 / 255.0, 56.0 / 255.0, 0.0, 1.0)
]
var withs = [
Color(54.0 / 255.0, 54.0 / 255.0, 54.0 / 255.0, 1.0),
Color(42.0 / 255.0, 42.0 / 255.0, 42.0 / 255.0, 1.0),
Color(33.0 / 255.0, 33.0 / 255.0, 33.0 / 255.0, 1.0),
Color(12.0 / 255.0, 12.0 / 255.0, 12.0 / 255.0, 1.0),
]
var mask_swaps = [
Color(0.0, 24.0 / 255.0, 144.0 / 255.0, 1.0)
]
var mask_withs = [
Color(42.0 / 255.0, 42.0 / 255.0, 42.0 / 255.0, 1.0)
]
set_shader("palette_swap", generate_image(swaps))
set_shader("palette_with", generate_image(withs))
set_shader("palette_swap_mask", generate_image(mask_swaps))
set_shader("palette_swap_with", generate_image(mask_withs))
func set_shader(param, value, silent = true):
if (!silent):
print_debug(param + " " + String(value))
get_material().set_shader_param(param, value)
func init_shader():
set_shader("sprite_size", get_rect().size, false)
set_shader("global_transform", get_global_transform())
func _ready():
generate_palettes()
init_shader()
func _process(delta):
set_shader("global_transform", get_global_transform())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment