Help with SURFACES and Opacity

CloseRange

Member
I'm fairly new to SURFACES (like i didn't know how to create them until this morning) and I thought I understood how they work but I started creating a simple SURFACE with a black background (using draw_clear(c_black)) but then I tried to draw a white circle with opacity on it and the black went away completly.

Aka anywhere where there was opacity the black got erased but I want the opacity to overlay the black

This is how my code works:
Code:
if surface_exists(surface) {
        draw_set_alpha(.5);
             draw_circle(mouse_x, mouse_y, 16, false);
        draw_set_alpha(1);
} else {
    surface = surface_create(room_width, room_height);
    surface_set_target(surface);
        draw_clear(c_black);
    surface_reset_target();
}
 
Last edited:
This code of yours:
This is how my code works:
Code:
if surface_exists(surface) {
draw_set_alpha(.5);
draw_circle(mouse_x, mouse_y, 16, false);
draw_set_alpha(1);
} else {
surface = surface_create(room_width, room_height);
surface_set_target(surface);
draw_clear(c_black);
surface_reset_target();
}
Doesn't make any sense to me at all. Where are you setting the target? Where the shader? Are you changeing the blend mode of the gpu? And what are you trying to do?
 

CloseRange

Member
This code of yours:


Doesn't make any sense to me at all. Where are you setting the target? Where the shader? Are you changeing the blend mode of the gpu? And what are you trying to do?
that was my bad I have more code than what I put but didn't want to waste your time with the usless junk but forgot to include where I set the target... so here is the full code:
Code:
if surface_exists(surface) {
        draw_surface(surface, 0, 0);
        if mouse_check_button(mb_left) {
            var dis = point_distance(mouse_x, mouse_y, mouse_xprevious, mouse_yprevious);
            var dir = point_direction(mouse_xprevious, mouse_yprevious, mouse_x, mouse_y);
            for(var i=0;i<=dis;i++) {
                var dx =lengthdir_x(i, dir);
                var dy =lengthdir_y(i, dir);
            
                surface_set_target(surface);
                    draw_set_alpha(.5);
                    draw_circle(mouse_x+dx, mouse_y+dy, brush_size, false);
                    draw_set_alpha(1);
                surface_reset_target();
            }
        }
} else {
    surface = surface_create(Width, Height);
    surface_set_target(surface);
        draw_clear(c_black);
    surface_reset_target();
}

mouse_xprevious = mouse_x;
mouse_yprevious = mouse_y;
I'm just trying to draw a white circle with an opacity of .5 onto a surface that is fully black. When I do this the black goes away and leaves just white but I want them to blend (turning it gray).

I never once change blend modes in my code anywhere.
 
I think you're confused on what a shader actually is. You're just using surfaces.

You can't just draw two colours and expect your PC to somehow know you want them to blend into their real-life mix. I recommend reading the manual on surfaces and then googling what a shader is.
 

CloseRange

Member
I think you're confused on what a shader actually is. You're just using surfaces.

You can't just draw two colours and expect your PC to somehow know you want them to blend into their real-life mix. I recommend reading the manual on surfaces and then googling what a shader is.
No apperintly I'm just retarded.... I know what a shader is but somehow got the names messed up.... didn't even notice i meant to say surfaces i know shaders fairly well sorry for the confusion :(

Edit. I fixed my wording in the original post
 
Then the answer is: When you draw on a surface the GPU blends the source pixel colour RGB & A with the destination colour RGB & A.

The destination surfaces colour was set to RGBA(0,0,0,1) by draw_clear(c_black) and the source circles colour is RGBA(1,1,1,0.5).

You draw in normal blend mode which is (bm_src_alpha, bm_inv_src_alpha),
where src_alpha is (As,As,As,As) and inv_src_alpha is (1-As,1-As,1-As,1-As).

So the final colour on the surface after drawing will be (component-wise calculated):
source(Rs,Gs,Bs,As) * bm_src_alpha +destination (Rd,Gd,Bd,Ad) * bm_inv_src_alpha
= (1, 1, 1, 0.5) * (0.5, 0.5, 0.5, 0.5) + (0, 0, 0, 1) * (1-0.5 , 1-.0.5, 1-0.5, 1-0.5)
= (0.5, 0.5, 0.5, 0.25) + (0, 0, 0, 0.5)
= (0.5,0.5,0.5,0.75)
So the result is a medium grey with some transparency:

Canvas:
transparent_white_on_solid_black1.png

surface after draw_clear(c_black):
transparent_white_on_solid_black2.png

circle to draw (white, 0.5 alpha):
transparent_white_on_solid_black3.png

surface after drawing (gray, alpha 0.75):
transparent_white_on_solid_black4.png

There's a great Tech blog about this by Mark Alexander:
https://www.yoyogames.com/blog/56/explaining-blend-modes-part-1
https://www.yoyogames.com/blog/57/explaining-blend-modes-part-2

And a really old forums post of mine:
http://gmc.yoyogames.com/index.php?showtopic=636009&hl=

A solution to this is changing the blend mode and pre-multiply the alpha into the colour.

Setting the blend mode:
before "if surface_exists(surface)" set the blend mode like this:
Code:
gpu_set_blendmode_ext(bm_one, bm_inv_src_alpha);
// or in GM:S 1: draw_set_blendmode_ext();
and before "mouse_xprevious = mouse_x;" set the blend mode back like this:
Code:
gpu_set_blendmode(bm_normal);
// or in GM:S 1: draw_set_blendmode();
pre-multiply the alpha:

somewhere in create event or where the colour changes:

Code:
var alpha = 0.5;

var colour = c_white; // or any colour in BGR format
var red = color_get_red(col);
var green = color_get_green(col);
var blue = color_get_blue(col);

colour_premultiplied = make_colour_rgb(red * alpha, green * alpha, blue * alpha);
inside your draw event where you draw the circle:
Code:
draw_set_alpha(alpha);
draw_circle_colour(mouse_x+dx, mouse_y+dy, brush_size, colour_premulitplied, colour_premulitplied, false);
draw_set_alpha(1);
Btw: you can move surface_set_target(surface) and surface_reset_target() outside the for-loop. This function costs quite some performance.

Edit 1: Hope I didn't mess up the code. But I don't know if there's another way in GMS2. Haven't looked into the new sepalpha blend mode functions yet.

Edit 2: fixed broken make_colour_rgb line.

Edit 3: In case you wonder why this doesn't happen when drawing to the application surface: The application surface cant change alpha.
 
Last edited:
Top