In a previous article that we published on blend modes, we covered the simple blend modes that you can use in your games, and gave a basic overview of how they work. However, to really get the most from blend modes you have step away from the general ones that GameMaker Studio 2 supports and start to use the extended blend modes.
GPU_SET_BLENDMODE_EXT()
In the previous article we concentrated on gpu_set_blendmode()
, but this week we are going to look at gpu_set_blendmode_ext()
. This is (as the name implies) an extended function to set custom blend modes, but how does this work? Let's just have a look at the dry technical answer first before we go any further:
gpu_set_blendmode_ext(src, dest)
Indicates what blend mode to use for both the source and destination colour. The new colour is some factor times the source and another factor times the destination. These factors are set with this function. To understand this, the source and destination both have a red, green, blue, and alpha component. So the source is (Rs, Gs, Bs, As) and the destination is (Rd, Gd, Bd, Ad). All values are considered to lie between 0 and 1.
Now, while that text is correct and explains what blend modes do, it's not really intuitive... so let's go into a bit more detail and explain what this is all about.
SOURCE, DESTINATION AND FACTORS
When we talk about the source we are talking about the colour and alpha values of the pixel that is being drawn, and when we talk about the destination we are talking about the colour and alpha values of the pixel that it is being drawn over. To keep things easier we can write out the source colour as it's components, like this:
- (Rs, Gs, Bs, As) = the RGB and Alpha of the colour you are going to draw.
And the destination would be like this:
- (Rd, Gd, Bd, Ad) = the RGB and Alpha of the colour you are going to draw on.
So when we draw something on the screen, our graphics processor is actually doing the following for every single pixel:
final_pixel_colour = (Rs,Gs,Bs,As) * source_blend_factor + (Rd,Gd,Bd,Ad) * destination_blend_factor
The blend factors mentioned above are defined in GameMaker Studio 2 by a number of GML constants, and each one represents a factor by which the source or destination R, G, B and A values should be multiplied by. The factors available are:
bm_zero
: Blend factor is (0, 0, 0, 0)bm_one
: Blend factor is (1, 1, 1, 1)bm_src_colour
: Blend factor is (Rs, Gs, Bs, As)bm_inv_src_colour
: Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As)bm_src_alpha
: Blend factor is (As, As, As, As)bm_inv_src_alpha
: Blend factor is (1-As, 1-As, 1-As, 1-As)bm_dest_alpha
: Blend factor is (Ad, Ad, Ad, Ad)bm_inv_dest_alpha
: Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad)bm_dest_colour
: Blend factor is (Rd, Gd, Bd, Ad)bm_inv_dest_colour
: Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad)bm_src_alpha_sat
: Blend factor is (f, f, f, 1); f = min(As, 1-Ad)
The four "basic" blend modes that GameMaker Studio 2 has are actually composites of two of these blend factors.
NORMAL BLENDING
Time to look at a practical example of how this comes together for drawing in GameMaker: Studio. For this, we are going to look at bm_normal
, which is really:
gpu_set_blendmode_ext(bm_src_alpha, bm_inv_src_alpha);
One of the problems people have with blend modes is visualising the results, so to show the maths behind them, we'll use this blend mode as our test subject since it's the blend mode that everyone uses the most and we know exactly what to expect from it. Imagine we are drawing a rectangle with the colour (128, 255, 64, 255) over a background with the colour (64, 128, 255, 255):
So, our blend mode looks like this:
Source: (128, 255, 64, 255) = (0.5, 1, 0.25, 1)
Destination: (64, 128, 255, 255) = (0.25, 0.5, 1, 1)
bm_src_alpha (As, As, As, As) = (255, 255, 255, 255) = (1, 1, 1, 1)
bm_inv_src_alpha (1-As, 1-As, 1-As, 1-As) = (0, 0, 0, 0)
(Rs,Gs,Bs,As) * bm_src_alpha + (Rd,Gd,Bd,Ad) * bm_inv_src_alpha
(0.5, 1, 0.25, 1) * (1, 1, 1, 1) + (0.25, 0.5, 1, 1) * (0, 0, 0, 0)
(0.5, 1, 0.25, 1) + (0, 0, 0, 0) = (0.5, 1, 0.25, 1) = (128, 255, 64, 255)
As you can see, we multiply the destination values by 0, which gives them a value of 0 too, meaning that the source colours are unchanged, so our final colour value is ** (128, 255, 64, 255)**. You can see how the inclusion of alpha affects this value too:
Source (128, 255, 64, 128) = (0.5, 1, 0.25, 0.5)
Destination: (64, 128, 255, 255) = (0.25, 0.5, 1, 1)
bm_src_alpha (As, As, As, As) = (128, 128, 128, 128) = (0.5, 0.5, 0.5, 0.5)
bm_inv_src_alpha (1-As, 1-As, 1-As, 1-As) = (0.5, 0.5, 0.5, 0.5)
(Rs,Gs,Bs,As) * bm_src_alpha + (Rd,Gd,Bd,Ad) * bm_inv_src_alpha
(0.5, 1, 0.25, 0.5) * (0.5, 0.5, 0.5, 0.5) + (0.25, 0.5, 1, 1) * (0.5, 0.5, 0.5, 0.5)
(0.25, 0.5, 0.125, 0.25) + (0.125, 0.25, 0.5, 0.5) = (0.375, 0.75, 0.625, 0.75) = (96, 192, 159, 192)
This gives a final pixel colour (96, 192, 159, 192), and will give an image like that shown below:
Hopefully you can see clearly now what the blend mode factors do, and how combining them can change what is drawn to create some interesting effects. We will now look at a practical example of how to combine these factors to create your own custom blend modes.
CREATING YOUR OWN BLEND MODES
Using the different factors available, you can create your own blend modes to achieve blending options that are not available to you normally. In this example, we are going to create a multiply effect like that used in Photoshop. We can simulate this blend mode using two of our above mentioned blend mode factor constants like this:
gpu_set_blendmode_ext(bm_dest_colour, bm_zero);
Before we actually draw something using this blend mode, let's have a look at how it should look in Photoshop:
Using our extended blend mode we get the following:
(Rs, Gs, Bs, As) * (Rd, Gd, Bd, Ad) + (Rd, Gd, Bd, Ad) * (0,0,0,0) = (Rs, Gs, Bs, As) * (Rd, Gd, Bd, Ad)
Source colour * dest colour + dest colour * zero = source colour * dest colour
The blend factor bm_zero
effectively removes the destination colour from the equation (as zero times anything is zero), so we are left with the source colour multiplied by the blend factor of the destination colour, hence the name "multiply" for the mode. To use this in GameMaker Studio 2, you would simply have something like this in your Draw Event:
gpu_set_blendmode_ext(bm_dest_colour, bm_zero);
draw_self();
gpu_set_blendmode(bm_normal);
The next image was taken from a test project in GameMaker using this exact code:
Can you spot the difference? Probably not! There will, however, be some deviation between the two due to differences between the render tools used, but it's almost exactly as you would expect. Note though that not all the blend modes used by art programs are available to you within GameMaker Studio 2 due to the fact some of them can actually force the colour values to go over 255 and so create special effects, but that doesn't mean you can't create passable imitations of these effects, or even brand new effects, using them.
It's worth mentioning that with GameMaker Studio 2 you can take this one step further using the function gpu_set_blendmode_ext_sepalpha()
. This permits you to separate out the alpha component of the different blend mode factors and use them individually to create even more possible combinations. We won't cover this function here as it's explained in depth already in the manual, but it's worth mentioning just so you know it's available for use.
SURFACES AND ALPHA
One final thing is worth noting about blend modes, and that is how they affect the alpha component of a colour. When simply drawing to the application surface or the back buffer, you don't really need to take into consideration the destination alpha value as it will always be one. However surfaces are different, since you can clear a surface to have any alpha value from zero to one that you wish. This leads to some interesting effects that are contrary to what most people would expect.
To illustrate this, we are going to look at drawing using the basic default bm_normal
. Most people think that having a surface cleared to alpha 0, and then drawing something with an alpha of 0.5 will give a resulting alpha value 0.5 too... but this is not the case and is something that many people think is a "bug" in how GameMaker Studio 2 renders things. However, as you will see, it's not a bug at all!
Say you have surface with every pixel at alpha 0, then you draw a circle on it with alpha 0.5 using the bm_normal
blend mode. The entire surface is still going to be alpha 0, but in the circle the alpha will actually be 0.25, since:
0.5 * 0.5 + 0 * 0.5 = 0.5 * 0.5 = 0.25
If you then proceed to draw another circle in the existing circle, also with an alpha of 0.5, then the alpha outside the circles would be 0, in between the circles 0.25, and in the smaller circle it would be 0.375:
0.5 * 0.5 + 0.25 * 0.5 = 0.25 + 0.125 = 0.375
If you were to continue to draw a number of 0.5 alpha circles to a surface with 0 alpha, and then draw this surface to the room, you would be able to clearly see this:
If you are still a bit confused about the alpha, try to picture it as just another colour. It behaves like a colour and all calculations on it are done in a similar manner. It's just not really visible, although the effect it has on other colours is. Which brings us to the final thing to note when using surfaces and blend modes (even bm_normal
)...
Even if the alpha of the surface is 0, *the colour components are still there and exist and will influence in all blending operations*. So drawing to a zero alpha cleared surface that has been cleared using the colour red (for example) will blend the source colours with the destination colours and give different effects to that which you may think. The above image was created on a surface cleared to black over a black background, but if we clear the surface to red, we get this instead:
Hopefully you can now work out why this happens for yourself using the formulas I've outlined above and applying the appropriate figures for bm_normal
.