This is an email from Gilbert B. who tried to post a really nice and exhaustive description about the magic behind the scenes when dealing with blending in GL but the comment ran too long so it got cut. Here is the text in full.
Thanks Gilbert!
—————————————–
Whenever you draw something to the screen in OpenGL, it goes through a multi-stage process called the pipeline. We don’t need to know about the whole thing. We’re just interested in what is called the screen buffers, which are large arrays of data that correspond to the screen. For instance, the most obvious one is called the color buffer and stores the pixel color for every pixel on the screen. This is what you’re drawing to in OpenGL. There’s also one called the depth buffer, which stores the distance of a pixel from the viewpoint. This is used to perform visible surface tests, so that when you draw 3d, things closer to you don’t get drawn over by things father away.
Let’s take a quick look at the depth buffer test, (also called the z-buffer, because it stores “z-values” as in xyz coordinates relative to the screen) and in so doing, we’ll make it easier to explain blending. All of these operations are going to be performed on a per-pixel basis, so from now on, we’re going to just talk about a single pixel. Now, if you have a drawn pixel, which has a depth value, stored in this buffer, and you’re drawing a new pixel in the same place, you just compare their z-values to see which pixel gets to be the value there.
if(newpixel.zvalue < pixel.zvalue) // if the new/incoming pixel is closer
pixel = newpixel; // make it the new pixel here
(note: I’m assuming that z-values just measure how far away from the viewpoint the pixel is, which isn’t really how those values are being stored. At least this gives you an idea of what’s going on, however.)
This is all done on the graphics card for you as long as GL_DEPTH_TEST is enabled. As mentioned above, you can set gl.glDepthMask(false) to disable writing to the depth buffer. This would mean that the statement “pixel = newpixel;” above does not actually include “pixel.zvalue = newpixel.zvalue” However, it would include the statement “pixel.color = newpixel.color” so you wouldn’t be able to see the new pixel unless it was considered visible. This makes possible another cool trick you can do which is to set the color buffer mask to false, so that nothing gets drawn to the color buffer, BUT stuff does get drawn to the depth buffer. In this way you can draw an “invisible” object that blocks line of sight.
So on to blending. Blending is a nifty way to copy in color data to a pixel. Before, we just overwrote the old color data with the new color data, so that either
color = old_color OR color = new_color
Now, we want,
color = f(new_color,old_color)
In the documentation you pointed to these are given the names source (= new_color) and destination (= old_color) They have these names, because whoever made the documentation spends too much time with hardware. The names come from assembly code where a source is a register that a number is coming from (say, to be added to another number from another source) and the destination is where the result of the operation is going to be stored. OKAY, digression. Sorry about that.
Anyways, our question is what the heck is f()? The answer is that we get to specify f() through the use of two functions which are glBlendFunc() and glBlendEquation(). They are going to modify some variables in the following equation:
color = g(new_color * new_scale, old_color * old_scale)
glBlendEquation() sets g() and glBlendFunc() sets new_scale and old_scale, which are just numbers we’re going to use to scale our colors. For now, let’s just assume we set glBlendEquation(GL_FUNC_ADD) which makes g(x,y) = x+y. So, our equation becomes:
color = new_color * new_scale + old_color * old_scale
You’re adding, hence additive blending. There’re still a bunch of options for additive blending however. Let’s take your code as an example. You set new_scale to GL_SRC_ALPHA. Remeber SRC=source=new_color, so all that means is that new_scale is going to be the alpha value you have for the new pixel coming in. (If you don’t know or remember, alpha is a fourth invisible color that is often used for transparency/opacity, but as we will see can be used for a number of blending effects) Since you’re scaling by alpha, you’re basically just factoring in transparency for your incoming color. This is why you get rid of those nasty rectilinear lines when you turn this on–that black region is invisible. Now, where you depart from the traditional transparency effect is by using GL_ONE instead of GL_ONE_MINUS_SRC_ALPHA. If we used GL_ONE_MINUS_SRC_ALPHA we get the equation:
color = new_color * new_alpha + old_color * (1 – new_alpha)
In math, this form of equation is called a linear combination, and we can think of alpha as a slider that says how much of the new color you want (alpha = 1, fully opaque) vs. how much of the old color you want. (alpha = 0, fully transparent) With this kind of blending, we can draw an opaque black pixel over a white one, as long as our alpha is nice and high. (ie. opaque)
Now, what you’re doing is saying that you’re gonna keep all the old color anyways by setting our old_scale to GL_ONE, which is going to give us the equation:
color = new_color * new_alpha + old_color * 1
The end result here is that drawing new stuff in can only make pixels brighter/more white, not dimmer. This follows directly from that equation. This is also why your orbs appear more glowy. If you try changing it to GL_ONE_MINUS_SRC_ALPHA, it’ll probably look worse, but you should hopefully get that sort of “aha!” reaction.
Now, just to be complete about things, I should mention the glBlendEquation. You could have it do all sorts of crazy things like glBlendEquation(GL_MAX) which would change our equation to:
color = max(new_color * new_alpha, old_color)
which still strictly increases color, though probably not as much so. You can hopefully parse the documentation enough to figure out what the different values do now. Similarly, you can hopefully make some sense of the table in the glBlendFunc documentation now.
Blending is really a ripe opportunity for exploration right now. It got stuck into graphics cards largely as a way to support transparency. It just seemed like a good idea at the time to generalize it, since it didn’t really cost any extra computing power to do so. I’ve hacked motion blending into an application before using blending. It really can be a powerful tool. However, most people nowadays have moved on to pixel shaders and the like, which are an even more generalized notion of this per-pixel computation.

Anothr feed track -all manner of distractions…
One new subscriber from Anothr Alerts…
[...] Since yesterday I’m testing the texture implementation of Andreas’ surface library (thanks for sending the early version) in combination with various blending modes (already documented and described by Robert Hodgin a few weeks ago – Mini-tutorial: Additive Blending pt. 1 / pt. 2). After increasing the Eclipse startup memory settings, I was able to load even highres textures and to write highres stills using the TileSaver class by Marius once again. [...]