GMS 2 Subtracting/Adding color value(s)? (SOLVED)

Discussion in 'Programming' started by Fixer90, Sep 22, 2018.

  1. Fixer90

    Fixer90 Member

    Joined:
    Nov 17, 2016
    Posts:
    142
    Let's say I have a sprite that is a simple, gray-colored square, with RGB values (128, 128, 128). Is there a code function or method I can use to decrease/increase the color values? I tried playing around and experimenting with image_blend, but the calculations for that were too complicated. Alternately, if anyone knows exactly how GMS2 handles color blending, I might be able to setup my own function based on that math.

    What I'd want the most is a function in which I can input a number to add (positive) or subtract (negative) to each RGB value of my choice to the image of a sprite.

    EDIT: For example, if I went back to the gray square - since it has 128 RGB - I'd just input 127, and it would turn from 128 to 255 on each, making it pure white.
     
  2. Phil Strahl

    Phil Strahl Member

    Joined:
    Jul 3, 2016
    Posts:
    387
    There's the gpu_set_blend_mode() function which allows you to blend sprites or surfaces together via the GPU in the Draw event, but that's not what you're looking for, correct?

    Another possibility is to import your gray sprite as a white and only draw it in gray, e.g.
    Code:
    draw_sprite_ext(sprite_index, image_index, x, y, 1 , 1, 0 , my_color, 1)
    
    whereas my_color would be
    Code:
    my_color = make_color_rgb(128, 128, 128);
    
    Of course, if your sprite has a color of its own, setting a color with draw_sprite_ext() just multiplies whatever is there with it. If that's what you want, then you just can write a little script that constructs a new color via make_color_rgb() or make_color_hsv().

    And lastly, you could look into writing a shader for your sprites, but I don't have much experience with that.

    Hope this helps!
     
  3. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    2,163
    Can you elaborate on exactly what you plan to use this for? Because I can think of different solutions, but that would depend on what you are doing exactly.

    If all you are doing is drawing a square with some gray value, then the easiest thing would be to draw an all white sprite but with a blending colour that is somewhere between black and white. Or just draw a colored rectangle.
     
  4. Fixer90

    Fixer90 Member

    Joined:
    Nov 17, 2016
    Posts:
    142
    Basically, if I have a sprite that has 3 colors in it: (128, 128, 128), (200, 200, 200), and (64, 64, 64), I want a function that will decrease all values by a given number I input. Let's say I input -64, that would result in all the colors becoming (64, 64, 64), (136, 136, 136), and (0, 0, 0).

    I may have to make a function that makes a shader to do this... but shaders are so complicated and I have no idea where to start.
     
  5. CardinalCoder64

    CardinalCoder64 Member

    Joined:
    May 19, 2017
    Posts:
    101
    Sprites load with a default image_blend of -1, which is white (255, 255, 255). If you're wanting to change the brightness of the sprite, I suggest using HSV instead of RGB. With HSV you can do (0, 0, value) that way you don't have to copy numbers and you only have one value to worry about. I'm going to use your example, so say I wanted to subtract 64 from the sprite color. I'd do just that. Let argument0 be -64.
    Code:
    //scr_change_brightness
    ///@param change_val
    var brightness = 255 + argument0;
    brightness = clamp(brightness, 0, 255);
    image_blend = make_color_hsv(0, 0, brightness);
    Personally I would just set the brightness value to argument0 but I don't know your scenario.
     
  6. Fixer90

    Fixer90 Member

    Joined:
    Nov 17, 2016
    Posts:
    142
    So I tested this on a box that had the color value (224, 224, 224) - I input -24 into the script, hoping it'd make the values (200, 200, 200). Instead it made them (203, 203, 203). Hmmm...
     
  7. dannyjenn

    dannyjenn Member

    Joined:
    Jul 29, 2017
    Posts:
    568
    What you're trying to do is literally impossible.

    image_blend uses a multiply blend mode. The reason that @CardinalCoder64's script isnt working is because his script returns the color ((255-24),(255-24),(255-24)), i.e. (231,231,231). But that color (231,231,231), when multiplied by the (224,224,224) in your sprite, gets (203,203,203)... not (200,200,200).

    Here's why I say it's impossible:

    Begin by working backwards:
    (200,200,200) = (224,224,244) * (??,??,??)
    (200,200,200) / (224,224,244) = (??,??,??)
    (228,228,228) = (??,??,??)
    So the script needs to return (228,228,228), not (231,231,231).

    But you say the sprite has three colors in it. So for the sake of the example, suppose the second color is (64,64,64). You want that color to be (40,40,40). So, again, work backwards:
    (40,40,40) = (64,64,64) * (??,??,??)
    (40,40,40) / (64,64,64) = (??,??,??)
    (159,159,159) = (??,??,??)

    But as you can see, (159,159,159) is not equal to (228,228,228). So there is no image_blend that can simultaneously decrease all colors by (24,24,24). (For that you'd need a subtract blend mode... but image_blend is multiplication)

    Increasing the brightness is even more impossible, since (in addition to the problem I just mentioned) you'd need a blend color to be something greater than (255,255,255) pure white. But such colors simply do not exist.


    What you're going to need is a shader. (Sorry, I haven't worked with them before, so I don't know where to start either.)
    There are a few non-shader options you could try, but they're impractical due to slowness. I'd say the best idea is to figure out shaders...
     
    Last edited: Sep 22, 2018
  8. RefresherTowel

    RefresherTowel Member

    Joined:
    Jul 13, 2016
    Posts:
    1,212
    dannyjenn likes this.
  9. Murzy

    Murzy Member

    Joined:
    Jul 28, 2016
    Posts:
    24
    I believe you could do this by using the additive and subtractive blend modes, and then drawing a completely white sprite, on top of the original one, with an image_blend set to the amount of color you want to add / subtract.

    The kind of functionality you are asking for is probably easier to implement with a shader, though.
     
  10. The Reverend

    The Reverend Member

    Joined:
    Sep 8, 2016
    Posts:
    553
    If you want to do this by shader, here's an example:

    Object creation: Init shader and demo
    Code:
    shader = shd_add_subtract_rgb;
    u_add_subtract_rgb = shader_get_uniform(shader, "add_subtract_rgb"); // handle to pass the value to the shader
    add_subtract_rgb = 0; // how much to add or subtract, value to pass to the shader
    add_subtract_rgb_interval = 5; // for demo: how much the value changes per mouse wheel up and down
    
    Object Step: mouse wheel demo
    Code:
    if (mouse_wheel_down()) add_subtract_rgb = max(add_subtract_rgb - add_subtract_rgb_interval, -255);
    if (mouse_wheel_up()) add_subtract_rgb = min(add_subtract_rgb + add_subtract_rgb_interval, 255);
    
    Object Draw:
    Code:
    shader_set(shader);
    shader_set_uniform_f(u_add_subtract_rgb, add_subtract_rgb);
    draw_self();
    shader_reset();
    draw_text(10, 10, "rgb-change: " + string(add_subtract_rgb));
    
    Vertex Shader "shd_add_subtract_rgb": just a standard passthrough

    Fragment Shader "shd_add_subtract_rgb":
    Code:
    varying vec2 v_vTexcoord;
    varying vec4 v_vColour;
    
    uniform float add_subtract_rgb;
    
    void main() {
    vec4 col    = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
    gl_FragColor = vec4(col.rgb + vec3(add_subtract_rgb/255.0), col.a);
    }
    
    If you want to learn how this works, watch the first few videos in my shader tutorial (link in the signature)
     
    Last edited: Sep 22, 2018
    dannyjenn and Fixer90 like this.
  11. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    2,163
    just use a shader, and use the image_blend colour as input instead of a uniform, that way you wont need to set uniforms.
     
    Fixer90 and The Reverend like this.
  12. Fixer90

    Fixer90 Member

    Joined:
    Nov 17, 2016
    Posts:
    142
    It works, thanks a ton!

    I'm guessing you're talking about implementing this into The Reverend's shader. Do you know exactly how I'd do this?
     
  13. flyingsaucerinvasion

    flyingsaucerinvasion Member

    Joined:
    Jun 20, 2016
    Posts:
    2,163
    It would depend on whether you want the shader to be able to add, subtract, or both. If just subtract it could be simply:

    gl_FragColor = texture2d( gm_BaseTexture, v_vTexcoord ) - v_vColour;

    In this situtuation, image_blend cannot be used for anything else, and image_alpha would be zero, unless you want it to subtract from the sprite's alpha.

    You could make the shader more complicated if you wanted to preserve the normal use of image_alpha, or if you wanted the shader to be able to both add or subtract colours. For example, if you are adding or subtracting only from gray colours, the red channel could hold the amount to add/subtract, and the green channel could hold information indicating whether to add or to subtract.
     
  14. Fixer90

    Fixer90 Member

    Joined:
    Nov 17, 2016
    Posts:
    142
    Is there a way I could modify this so as to have 3 separate values that represent the change in R, G, and B, where I could add/subtract different numbers from each?
     
  15. The Reverend

    The Reverend Member

    Joined:
    Sep 8, 2016
    Posts:
    553
    There sure is :)

    Made another example this time using the vertex colour instead of a uniform as flyingsaucerinvasion suggested.
    Draw backs:
    You'll loose some precision because I'm remapping the colour range {0, 255} to {-255, 255}.
    You can't use the vertex colour for anything else anymore.

    But it's easier to implement and it's faster I guess.

    Here's the fragment shader:
    Code:
    /* -------------------------------------------------------------------
    Simple add/subtract rgb fragment shader
    using the vertex colour
    ----------------------------------------------------------------------
    vertex colour < 128 => subtract
    vertex colour = 128 => no change
    vertex colour > 128 => add
    --------------------------------------------------------------------*/
    
    varying vec2 v_vTexcoord;
    varying vec4 v_vColour;
    
    void main() {
        vec4 base_col    = texture2D(gm_BaseTexture, v_vTexcoord);
        base_col.rgb    += 2.0 * (v_vColour.rgb - 0.5);
        gl_FragColor    = base_col;
    }
    
    And here's a demo file for GMS2:
    https://www.dropbox.com/s/v06m61fgkkp3hnc/add_subtract_rgb.yyz?dl=0
     
    Fixer90 likes this.
  16. Fixer90

    Fixer90 Member

    Joined:
    Nov 17, 2016
    Posts:
    142
    Now the question is, can we turn all of this into a script that allows me to add/subtract from each color value (R, G, and B) by inputting them in order? Because honestly, a script that could do that would be revolutionary to my developing. Another option, if it'd be simpler, would be to make a script that adds the argument values, and one that subtracts.

    EDIT: Based off of the Dropbox demo you posted (which I totally didn't just now notice), I should be able to make a script to do this.
     
    Last edited: Sep 24, 2018

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice