1. Hey! Guest! The 36th GMC Jam will take place between February 27th, 12:00 UTC - March 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

Shaders Recoloring sprites

Discussion in 'Programming' started by Andymakeer, Feb 2, 2020.

  1. Andymakeer

    Andymakeer Member

    Joined:
    Feb 2, 2020
    Posts:
    2
    Hi guys!

    I'm using a shader to recolor my sprites but its not turning out the way i want.

    This is what happening:
    I have "bullet" sprites that i want them to change their color if they are "frozen".

    These are them at normal state:
    [​IMG]

    These is what I want them to become:
    [​IMG]
    I got this using the Colorize tool from the sprite editor

    Now the coding:

    This is my shader:
    Code:
    varying vec2 v_vTexcoord;
    varying vec4 v_vColour;
    uniform vec3 u_color;
    
    void main()
    {
        vec4 texColor = texture2D(gm_BaseTexture, v_vTexcoord);
        gl_FragColor = v_vColour * vec4(u_color.rgb, texColor.a);
    }
    and this is my code on draw event:
    Code:
    if freeze_amp < 1
    {
        shader_set(shader_Recolor);
        shader_set_uniform_f(_uniColor, 66 / 255, 236 / 255, 225 / 255);
        
    }
    draw_self();
    shader_reset();
    Those numbers on uniform are the RGB color divided by 255 (parsing to shader color).

    But ingame the result is this:
    [​IMG]

    The black borders around the bullets are being recolored too, which is NOT what I want.
    Basically, I want THE SAME EFFECT that happens when you use the Colorize tool from the sprite editor.

    Help me please ;-;
     
  2. Cat

    Cat Member

    Joined:
    Jun 22, 2016
    Posts:
    87
    You need to multiply gl_FragColor by texColor, e.g.
    Code:
    gl_FragColor = texColor * v_vColour * vec4(u_color.rgb, texColor.a);
     
  3. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    Colorizing is a different thing than simply multiplying one color with another. It essentially gets the hue, sat and lum of each pixel, then sets the hue to the chosen value.
    I've got code for that if you wanna see, and you can also look it up online, that's what I did
     
  4. Andymakeer

    Andymakeer Member

    Joined:
    Feb 2, 2020
    Posts:
    2
    i want to see the code, plssss
     
  5. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    I use these scripts in my project for the color picker:

    Code:
    ///color_get_hsl(color)
    
    //Takes standard gamemaker color and returns an array of [hue, sat, lum]
    
    var _color = argument0;
    
    var
    r = colour_get_red(_color) / 255,
    g = colour_get_green(_color) / 255,
    b = colour_get_blue(_color) / 255,
    _min, _max, _range, hue, sat, lum, hsl;
    
    _min = min(r, g, b)
    _max = max(r, g, b)
    _range = _max - _min
    
    lum = (_max + _min) / 2
    
    if _range = 0
    {
    hue = 0
    sat = 0
    }
    else
    {
    
    if lum < 0.5
    {sat = _range / (_max + _min)}
    else
    {sat = _range / (2 - _max - _min)}
    
    if r = _max
    {
    if g > b
    {hue = (g - b) / _range}
    else
    {hue = 6 - ((b - g) / _range)}
    }
    else
    {
    if g = _max
    {hue = 2 + (b - r) / _range}
    else
    {hue = 4 + (r - g) / _range}
    }
    if hue < 0
    {hue++}
    if hue > 6
    {hue--}
    }
    
    hsl[0] = round(hue * 255) //the multiplication here isn't that important if you're just using this in a shader
    hsl[1] = round(sat * 255) //it's just to make more sense in a color picker
    hsl[2] = round(lum * 510)
    
    return hsl
    
    Code:
    ///make_color_hsl(hue, sat, lum)
    
    //again, here the input values can range from 0-1 instead
    var hue = argument0 / 1530, sat = argument1 / 255, lum = argument2 / 510, r, g, b, tr, tg, tb, t1, t2;
    
    if lum < 0.5
    {t1 = lum * (1.0 + sat)}
    else
    {t1 = lum + sat - lum * sat}
    
    t2 = 2 * lum - t1
    
    tr = hue + 0.333
    tg = hue
    tb = hue - 0.333
    
    if tr > 1
    {--tr}
    if tg > 1
    {--tg}
    if tb > 1
    {--tb}
    
    if tr < 0
    {++tr}
    if tg < 0
    {++tg}
    if tb < 0
    {++tb}
    
    
    //Red
    if 6 * tr < 1
    {r = t2 + (t1 - t2) * 6 * tr}
    else
    {
    if 2 * tr < 1
    {r = t1}
    else
    {
    if 3 * tr < 2
    {r = t2 + (t1 - t2) * (0.666 - tr) * 6}
    else
    {r = t2}
    }
    }
    
    //Green
    if 6 * tg < 1
    {g = t2 + (t1 - t2) * 6 * tg}
    else
    {
    if 2 * tg < 1
    {g = t1}
    else
    {
    if 3 * tg < 2
    {g = t2 + (t1 - t2) * (0.666 - tg) * 6}
    else
    {g = t2}
    }
    }
    
    //Blue
    if 6 * tb < 1
    {b = t2 + (t1 - t2) * 6 * tb}
    else
    {
    if 2 * tb < 1
    {b = t1}
    else
    {
    if 3 * tb < 2
    {b = t2 + (t1 - t2) * (0.666 - tb) * 6}
    else
    {b = t2}
    }
    }
    
    r *= 255
    g *= 255
    b *= 255
    
    return make_color_rgb(r, g, b)
    I basically copied this code from easyrgb.com. So I can't really explain very well what they're doing and why.
    But for colorizing, you might not need the math for calculating the hue, which takes up most of that code. If you want the whole image to be the one hue, you can just use the code for getting the sat & lum, then apply those to a new hue:

    Code:
    ///color_get_sat(color)
    
    var _color = argument0;
    
    var
    r = colour_get_red(_color) / 255,
    g = colour_get_green(_color) / 255,
    b = colour_get_blue(_color) / 255,
    _min, _max, _range, lum, sat;
    
    _min = min(r, g, b)
    _max = max(r, g, b)
    _range = _max - _min
    
    lum = (_max + _min) / 2
    
    if _range = 0
    {sat = 0}
    else
    {
    if lum < 0.5
    {sat = _range / (_max + _min)}
    else
    {sat = _range / (2 - _max - _min)}
    }
    
    return sat
    Code:
    ///color_get_lum(color)
    
    var _color = argument0;
    
    var
    r = colour_get_red(_color) / 255,
    g = colour_get_green(_color) / 255,
    b = colour_get_blue(_color) / 255,
    _min, _max, _range, lum;
    
    _min = min(r, g, b)
    _max = max(r, g, b)
    _range = _max - _min
    
    lum = (_max + _min) / 2
    
    return lum
    So here's those things put into a shader, and code for applying sat & lum to an rgb color(or hue):

    Code:
    varying vec2 v_vTexcoord;
    
    uniform vec3 hue;
    
    void main()
    {
    
    vec3 color = texture2D(gm_BaseTexture, v_vTexcoord).rgb;
    
    //First get the sat & lum of the color
    float _min = min(color.r, color.g, color.b);
    float _max = max(color.r, color.g, color.b);
    float _range = _max - _min;
    float lum = (_max + _min) * 0.5;
    if (_range == 0.0)
    {
    sat = 0.0;
    }
    else
    {
    if (lum < 0.5)
    {sat = _range / (_max + _min)}
    else
    {sat = _range / (2 - _max - _min)}
    }
    
    //Then create a new color using the uniform hue with the found sat & lum
    
    //First apply the lum (blend towards black or white)
    if (lum > 0.5)
    {
    color = mix(hue, vec3(1.0), (lum * 2.0) - 1.0);
    }
    else
    {
    color = hue * lum * 2.0;
    }
    
    //Then apply the sat (blend towards grey)
    color = mix(vec3(lum), color, sat)
    
    gl_FragColor = vec4(color, 1.0);
    }
    This shader will be fine for basic colorizing where you just want to set a sprite to one certain hue.
    If you want to adjust the sat & lum, you can add 2 extra uniforms which affect the sat & lum(adding or multiplying).
    But if you want to shift the hue, you'd need to put the hue detecting code from "color_get_hsl" into the shader, then shift the hue, recalculate the rgb values for it, then apply the sat & lum. I didn't want to send code for that straightaway in case you don't need to do that, but if you do just let me know.

    I hope that code I sent helps
     
    Andymakeer likes this.
  6. 2DKnights

    2DKnights Member

    Joined:
    Nov 6, 2016
    Posts:
    49
    You may be able to do this using the blendmode functions to subtract out the right amount of red and green to make your objects appear the correct color just remember to reset after drawing so nothing else is affected. Might not be as efficient as shaders though, not sure.
     
  7. robproctor83

    robproctor83 Member

    Joined:
    Sep 30, 2019
    Posts:
    292
    The easiest thing would be to just change image_blend = c_aqua and it will shift the hue of the sprite, however it most likely won't get you the results you want. If you only need to shift the hue and retain the lightness I would use a shader like Joe posted. However, if you think you might need to swap color palettes at some point it might be worth looking into the link below. If you had a red and blue sprite and you wanted to shift the red to blue and the blue to yellow you would need to use something like this, otherwise you would only be able to get a blue and green sprite, or all blue or all yellow, but not blue and yellow.

    https://marketplace.yoyogames.com/assets/1661/retro-palette-swapper
     

Share This Page