Legacy GM [SOLVED] 'Overlay' blend mode in Gamemaker

Hello everyone,

What blend mode in Game maker would be considered the technical equivalent of 'Overlay' in say, Photoshop?

Cheers,
Nathan
 
M

mariospants

Guest
There really isn't one, according to Rogelio Bernal Andreo (a respected astrophotographer), the formula for Overlay is: (Source> 0.5) * (1 - (1-2*(Source-0.5)) * (1-Destination)) + (Source<= 0.5) * ((2*Source) * Destination), which is pretty specific.

You can possibly try to emulate with bm_add when using a Source image that's lighter than the Destination and playing with alpha, and using bm_subtract for the opposite.

The closest I've gotten is using draw_set_blend_mode_ext(bm_dest_color,bm_src_color), give that a try and see if it gives you the results you're looking for.
 
Thanks for the information mariospants, that's interesting to know.
I've been experimenting with the brilliant, free blend mode test utility on the Gamemaker market place -- the blend results differ wildly depending on the images used, so it's tough to pin down a definitive mode.
 

Perseus

Not Medusa
Forum Staff
Moderator
A shader might be able to do what you want.

Code:
// Fragment (Overlay)

varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform sampler2D texOverlay;
void main() {
    vec4 inColor = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
    vec4 outColor = vec4(0.0,0.0,0.0, inColor.a);
    vec4 overlay = texture2D(texOverlay, v_vTexcoord);
    if (inColor.r > 0.5) {
        outColor.r = (1.0 - (1.0 - 2.0 * (inColor.r - 0.5)) * (1.0 - overlay.r));
    }
    else {
        outColor.r = ((2.0 * inColor.r) * overlay.r);
    }
    if (inColor.g > 0.5) {
        outColor.g = (1.0 - (1.0 - 2.0 * (inColor.g - 0.5)) * (1.0 - overlay.g));
    }
    else {
        outColor.g = ((2.0 * inColor.g) * overlay.g);
    }
    if (inColor.b > 0.5) {
        outColor.b = (1.0 - (1.0 - 2.0 * (inColor.b - 0.5)) * (1.0 - overlay.b));
    }
    else {
        outColor.b = ((2.0 * inColor.b) * overlay.b);
    }
    gl_FragColor = mix(outColor, inColor, 1.0 - overlay.a);
}
 
According to my tests, the following fragment shader will produce exactly the same results as the photoshop overlay blend mode.

A few notes first though.

It appears that the formula by @mariospants is incorrect because the source and destination colors have been swapped.

Also, I'm avoiding using any if statements because as far as I understand, if different fragments execute different segments of code, it will slow down the execution of the shader, perhaps by a lot.

Code:
//---------------------------------------------------------------
    uniform sampler2D overlayTexture;
    //---------------------------------------------------------------
    varying vec2 v_vTexcoord;
    //---------------------------------------------------------------
    float overlay( float S, float D ) {
        return float( D > 0.5 ) * ( 2.0 * (S + D - D * S ) - 1.0 )
        + float( D <= 0.5 ) * ( ( 2.0 * D ) * S );
    }
    //---------------------------------------------------------------
    void main() {
        vec4 D = texture2D( gm_BaseTexture, v_vTexcoord );  //destination color
        vec4 S = texture2D( overlayTexture, v_vTexcoord );  //source color
        //---------------------------------------------------------------
        gl_FragColor = vec4(
            mix(
                vec3( overlay( S.r, D.r ), overlay( S.g, D.g ), overlay( S.b, D.b ) ),
                D.rgb,
                1.0 - S.a
            ),
            D.a
        );
    }
//---------------------------------------------------------------
EDIT: slight simplification.
 
Last edited:
M

mariospants

Guest
According to my tests, the following fragment shader will produce exactly the same results as the photoshop overlay blend mode.

A few notes first though.

It appears that the formula by @mariospants is incorrect because the source and destination colors have been swapped.

Also, I'm avoiding using any if statements because as far as I understand, if different fragments execute different segments of code, it will slow down the execution of the shader, perhaps by a lot.
I was having some difficulty trying to shove one paradigm's terminology into another, and it ended up being a 50/50 crap shoot. Thanks for fixing my error, and thanks for the shader code... we need a KB for these kinds of things - something we can easily look up after hitting F1 in Game Maker. e.g., do search for "Photoshop overlay blend mode" or whatever, and get this code...
 

COWCAT

Member
Hello,

I'm trying to implement that "overlay" shader to implement some lighting effects for my game. (currently doing tests to see what art style works best)

I'd like to blend a "background" picture with a "lighting" sprite in order to get this (simulated with Photoshop) :

overlaytest.jpg

I'm not really a specialist with shaders so I'm not sure how to use it.
If I'm being correct, I'm supposed to pass a texture to the shader? But how?

I've tried finding some examples and did *something* :

Code:
uni_overlayTexture = shader_get_sampler_index(sh_overlay, "overlayTexture");
draw_sprite(bg, -1, 0, 0);

textbg = sprite_get_texture(bg, 0);
shader_set(sh_overlay);
texture_set_stage(uni_overlayTexture, textbg);
draw_sprite(light, -1, 0, 0);
shader_reset();
But the result doesn't work :

result.jpg

(Also I'm not sure if this shader will really help me at all because the background will actually be made of separate objects moving independently so passing a single texture might be complex and there will be other elements like HUD over the whole thing...)

Any help? Or other ways to simulate this "lighting" effect?
The only other alternative working for me is using the "bm_add" blend mode but it "burns" the picture...

Even the suggested "bm_dest_color,bm_src_color" blend mode doesn't work for me, the transparent parts of the light sprite get used for some reason.
 

GMWolf

aka fel666
I would play around with the different blend modes first.
If you know the blend equation this helps too. It's srcColor × scrFactor ? DestColor × destFactor.
Where ? Is the blend function (+ by default).


As for the shader, you would want to first render to a surface, then pass that surface in to be post processed. That is how you can apply it to multiple objects at a time.

Unless you want more complex equations, you shouldn't need to use a shader. Blend equations should do most of what you need.
 

COWCAT

Member
I admit I can't really guess how blending modes will work until I try them.

- Some blending modes don't work with all platforms (I'm exporting to PC/Consoles and potentially mobile too)
- I remember trying varied combinations for my previous game already and never finding a satisfying result (other than bm_add again)

Still I guess I'll try that again because yes, the shader solution seems a tiny bit too complex... and would be very slow, especially if I have to generate + render a surface of the whole screen size at each step.

I would have thought other devs would have encountered similar issues? Lighting should be pretty common but I actually never find any good examples... I just want something that works similarly to what Photoshop does. (it doesn't have to be perfect, just not burning the picture...)
 

GMWolf

aka fel666
and would be very slow, especially if I have to generate + render a surface of the whole screen size at each step.
Don't need to create the surface every frame, keep it around!
Instead of rendering directly to the back buffer, you first render to your surface, then render that surface to the back buffer.
It's a very common technique for post processing like this. Having one full screen surface is cheap. Many games make use of 10+ (but that is starting to be expensive..)!
 

COWCAT

Member
Well I have bad memories of using a lot of surfaces with my previous game, which became a messy nightmare. Drawing some sprites on a surface and not others, transparency issues etc...
Also with old Intel HD Graphics chipsets, even drawing the full 1080p picture one or two times is enough to make them slow down.

Anyway, I've settled to a different solution.
Instead of generating light, I draw over the character sprite with a default ambient light (with just image_blend) to make him darker - and I vary the alpha of this according to position in the room (compared to the light sources) So the closer to a light source, the less dark it becomes. Which gives the impression the character gets lit!

Not a full replacement for the overlay but it's simple and it works correctly :)
 
Top