+3 votes

Im trying to make a fantasy game which the user can choose from a multiple types of abilities and i wanted to make it so for each type some colors on the player's sprite will change, for example from white to red(for fire).

I went through the docs but couldn't make it work, and barely understood the way the shaders in godot 3 works...

can anyone help me with this one or hook me up with a good tutorial about it?

in Engine by (1,200 points)

1 Answer

+6 votes
Best answer

I played around with color replacement and started with this simple shader which is able to replace only a specific color:

shader_type canvas_item;

uniform vec4 u_color_key : hint_color;
uniform vec4 u_replacement_color : hint_color;

void fragment() {
    vec4 col = texture(TEXTURE, UV);
    vec4 d4 = abs(col - u_color_key);
    float d = max(max(d4.r, d4.g), d4.b);
    if(d < 0.01) {
        col = u_replacement_color;
    }
    COLOR = col;
}

Then I went further and made a shader that can replace a color by another while keeping the shading and transparency intact:

shader_type canvas_item;

// Which color you want to change
uniform vec4 u_color_key : hint_color;
// Which color to replace it with
uniform vec4 u_replacement_color : hint_color;
// How much tolerance for the replacement color (between 0 and 1)
uniform float u_tolerance;

void fragment() {
    // Get color from the sprite texture at the current pixel we are rendering
    vec4 original_color = texture(TEXTURE, UV);
    vec3 col = original_color.rgb;
    // Get a rough degree of difference between the texture color and the color key
    vec3 diff3 = col - u_color_key.rgb;
    float m = max(max(abs(diff3.r), abs(diff3.g)), abs(diff3.b));
    // Change color of pixels below tolerance threshold, while keeping shades if any (a bit naive, there may better ways)
    float brightness = length(col);
    col = mix(col, u_replacement_color.rgb * brightness, step(m, u_tolerance));
    // Assign final color for the pixel, and preserve transparency
    COLOR = vec4(col.rgb, original_color.a);
}

But I found a bug when trying it, I hope it will gets fixed: https://github.com/godotengine/godot/issues/14012

by (28,835 points)
selected by

thanks, it really helped me understand how to shade what i want, anyway hope they will fix that bug(even tho it doesn't matter to me right now)

Thanks! Why can't I just compare the color values directly like:

if (original_color.rgb == u_color_key.rgb)

I tried and it did not work, and I see that you do some math, I think comparing the difference between the colors to a very small quantity, but... why? The color I get from the gimp color picker tool is not "really" the color shown on screen by godot?

This is to rule out floating point error. Shaders work with floating point math, and colors you set in shader parameters are 32-bit floats, not 8-bit RGB values like GIMP shows them. So unless you make sure that every color at any point is clamped to the 8-bit value range, it's just safer to handle a threshold because comparing two floats for equality doesn't alway works out.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.