GameMaker Subtracting/Adding color value(s)? (SOLVED)

Fixer90

Member
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.
 

Phil Strahl

Member
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!
 
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.
 

Fixer90

Member
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.
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.
 
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.
 

Fixer90

Member
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.
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...
 
D

dannyjenn

Guest
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 by a moderator:

Murzy

Member
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.
 
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:

Fixer90

Member
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)
It works, thanks a ton!

just use a shader, and use the image_blend colour as input instead of a uniform, that way you wont need to set uniforms.
I'm guessing you're talking about implementing this into The Reverend's shader. Do you know exactly how I'd do this?
 
I'm guessing you're talking about implementing this into The Reverend's shader. Do you know exactly how I'd do this?
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.
 

Fixer90

Member
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?
 
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

Member
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
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:
Top