I made a mock-up of three circles overlapping one another in GIMP, each with its opacity set to 50%. See the first attachment for the results.
Then I drew the same three circles in Allegro 5 using al_draw_filled_circle(), each with its alpha set to 128. However, the Allegro 5 results were different than what I got from GIMP. See the second attachment.
Here's my code:
So what's the deal? I expected Allegro to generate the same result as GIMP, but it's different. Why is this?
Allegro uses pre-multiplied alpha by default. You'd need to multiply the color channels by the fractional alpha (e.g., 0.5). Otherwise, you get an additive effect.
http://liballeg.org/a5docs/trunk/graphics.html#al_premul_rgba_f
http://liballeg.org/a5docs/trunk/graphics.html#al_set_blender
I would love if Allegro had a tutorial / explaination of the reasoning for the different blending modes, and why pre-multiplied is the default. That's something I've never understood.
Thanks for linking to the docs, Aaron. I'm with Chris on this one; I'm curious to know the reason for using pre-multiplied alpha by default as well.
Just looking at ex_premulalpha should be enough reasoning - without pre-multiplied alpha it leads to incorrect results, with pre-multiplied alpha it leads to the correct results.
The explanation is simple. When OpenGL interpolates it does linear interpolation on all channels. So lets say you draw a texture where one pixel is opaque red (red = 1, green = 0, blue = 0, alpha = 1). And the pixel next to it is completely transparent black (red = 0, green = 0, blue = 0, alpha = 0). Now you scale this up enough that OpenGL has to interpolate a pixel between those two - it will therefore be:
red = (1 + 0) / 2 = 0.5
green = (0 + 0) / 2 = 0
blue = (0 + 0) / 2 = 0
alpha = (1 + 0) / 2 = 0.5
So far, there is no difference between normal and pre-multipled alpha. Now OpenGL draws that pixel, lets say on a completely black background (red_back = 0, green_back = 0, blue_back = 0).
The result is:
result_red = red_back * (1 - alpha) + red * alpha = 0 + 0.5 * 0.5 = 0.25
result_green = 0
result_blue = 0
We drew a pixel that is 25% red instead of a pixel that clearly should be 50% red, as it is exactly between the completely red and the completely black one! So, just like you can see in ex_premul_alpha, it appears wrong.
What if we use pre-multiplied alpha?
result_red = red_back * (1 - alpha) + red = 0 + 0.5 = 0.5
result_green = 0
result_blue = 0
Now we get the correct 50% red between the full red and the full black pixel.
In a way the problem is just that with not pre-multiplied alpha there is very many ways to specify transparent colors, e.g. 0/0/0/0 and 1/1/1/0 are both the exact same 100% transparent color. But one will darken everything when interpolating to it and the other will interpolate everything to white - and there is no way to simply interpolate towards transparency.
I have to agree with Chris - this is the bit where I had most difficulties porting allegro 4 games to allegro 5. They way blending works is different and hard (at least for me) to wrap my head around.