PDA

View Full Version : Blending RBG with a seperate Alpha map



surgptr
05-05-2003, 10:29 AM
I've looked through all the old topics on masking and blending but none of them help me much.

I have a dynamic alpha map that needs to be combined with an RGB texture.
I'm using multipass rendering and cannot use RGBA or multitexturing due to the dynamic alpha maps and many texture layers that I will add later.

So far I have placed my alpha map onto the primitive (pass 1) and then I blend my texture onto the primitive using the previous passes alpha map as the transparency (pass 2).
However it doesn't work and I can't figure out why.

Here is my code.

// Sand texture loaded in RGB format
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, Image);

// Alpha map loaded in ALPHA format
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, Image);

glColor3f(1,1,1);
glDisable(GL_DEPTH_TEST);

glBindTexture(GL_TEXTURE_2D, AlphaMap);
glEnable(GL_TEXTURE_2D);

glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0,0);
glVertex3f(0,400,0);
glTexCoord2f(0,1);
glVertex3f(0,0,0);
glTexCoord2f(1,0);
glVertex3f(400,400,0);
glTexCoord2f(1,1);
glVertex3f(400,0,0);
glEnd();
glDisable(GL_TEXTURE_2D);

// sand
glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, SandTexture);
glEnable(GL_TEXTURE_2D);

glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0,0);
glVertex3f(0,400,0);
glTexCoord2f(0,1);
glVertex3f(0,0,0);
glTexCoord2f(1,0);
glVertex3f(400,400,0);
glTexCoord2f(1,1);
glVertex3f(400,0,0);
glEnd();

glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);


My blending equation should be :
(Dr; Dg; Db; Da) = (Sr*Da; Sg*Da, Sb*Da; Sa*Da)

Why doesn't glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA) seem to work?

Paul

[This message has been edited by surgptr (edited 05-05-2003).]

Bob
05-05-2003, 11:08 AM
The blending equation is



Dc = Sc * Sf + Dc * Df

where Sc and Dc is the source and destination color, and Sf and Df are the source and destination factors (first and second parameter of glBlendFunc).

You want Sc * Dc.a, so just identify this with the equation above and we get this.



Sf = Dc.a = GL_DST_ALPHA
Df = 0 = GL_ZERO

Which is set like this.



glBlendFunc(GL_DST_ALPHA, GL_ZERO);

Bob
05-05-2003, 11:16 AM
Sorry, I was a little bit too quick to answer your question and didn't really read through it enough and I want to add some details.

This method will not produce any transparency. It will only scale the output color using the destination alpha as scale factor. For example, if alpha is zero, black will be written to the frame buffer, no matter what was there before. If you want transparency, you need to interpolate the source and destination color using, in this case, the destination alpha as interpolation factor. This should act more like transparency.



glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);

Also note that this method requires a destination alpha channel.

Second, you should think about going for multitexturing instead. You can easily combine the alpha map and the base texture to a single pass.

surgptr
05-05-2003, 11:45 AM
Originally posted by Bob:
If you want transparency, you need to interpolate the source and destination color using, in this case, the destination alpha as interpolation factor. This should act more like transparency.



glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);

Also note that this method requires a destination alpha channel.

Second, you should think about going for multitexturing instead. You can easily combine the alpha map and the base texture to a single pass.

Ok I tried glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA) but all that happens is that I get a sand texture.
I did check that my video card supports destination alpha (which it does) since not all accelerators or OpenGL implementations support DST_ALPHA.

About the multitexturing - I would love to use multitexturing but how would I combine 6 sets of alpha and color maps and then combine all of the results together? At the moment the only solution I can see is by building up an image through accumulation.

BTW - Can you recommend any good books that focus specifically on blending/masking?
All the books I have only devote 5-10 pages on blending and they approach it from a high level. Something that explains it at the pixel level with flow diagrams of the process would help immensely.

Paul

Bob
05-05-2003, 12:43 PM
You said your card supports destination alpha, but do you have a pixel format that have a destination alpha channel? If you don't explicitly ask for destination alpha when you set pixel format, you probably won't get it, so make sure you're really asking for a destination alpha channel.

For information on blending, the equation I gave above is really all you need to know about blending. The incomming color is multiplied by a factor, and the destination color is also multiplied by a factor, and then they are added together (see note 1 below) and finally stored in the frame buffer. Anything else is just techniques to use the blending equations to achieve different effects, but as long as you know what you want and can express it as an equation, you don't need anything else, it's just identification with the base equations. Maybe the OpenGL specification (look around this site in the documentation section) have something of interest.

note 1: Addition is only one blend function, you can subtract and use min/max functions too. See the spec for more info.

surgptr
05-06-2003, 12:22 AM
[QUOTE]Originally posted by Bob:
You said your card supports destination alpha, but do you have a pixel format that have a destination alpha channel? If you don't explicitly ask for destination alpha when you set pixel format, you probably won't get it, so make sure you're really asking for a destination alpha channel.

Thanks Bob!
You hit the problem right on the head.

I had to tell SDL to set up a destination alpha channel with 8 bit resolution.
i.e. SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

The blending works perfectly now. http://www.opengl.org/discussion_boards/ubb/biggrin.gif
Paul

surgptr
05-06-2003, 06:58 AM
The DST_ALPHA works great except when you want to apply another alpha map.

How can I get glBlendFunc to blend my textures like this :
SRC * (0,0,0,1) + DST (1,1,1,0)

i.e. I want to replace the alpha channel in DST with a new alpha channel from SRC image.

There doesn't seem to be any way of only manipulating alpha channels in OpenGL. http://www.opengl.org/discussion_boards/ubb/frown.gif

Texture mapping the alpha map over the existing color map without blending doesn't work either.
It just replaces the color map.

Or is there some way that I can switch off color mapping while I replace the alpha channel and then switch it back on when I'm done?

What I'm trying to achieve is this :
- Texture map primitive with base texture
- Apply alpha map 1
- Blend in grass texture with alpha map 1
- Apply alpha map 2
- Blend in sand texture with alpha map 2
- Apply alpha map 3
- Blend in rock texture with alpha map 3
- Apply alpha map 4
- Blend in snow texture with alpha map 4
...

Bob
05-06-2003, 07:25 AM
SRC * (0,0,0,1) + DST (1,1,1,0) can be achieved in two ways.

#1: By using separate blending equations for RGB and A. With glBlendFuncSeparate you can control the RGB and A blending separately. glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO) should be the blending function you want. First two is for RGB, and last two for A.

#2: By masking the RGB portion out by disable color writes. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) will disable writes for RGB and enable writes for A. That is what the function above does, RGB from destination and A from source.

That was an answer to your blending problem, now to an answer to what you're trying to achieve. As I understand it, you're going to do an 8-pass rendering, where alpha 1 controls how much of grass you want, and alpha 2 controls how much sand you want, and so on. So basically you want A1*grass + A2*sand + ... (you're not clear whether you actually want to add the different stages, just tell me if you want something else, but for now I assume you want addition).

This is a perfect situation for multitexturing, where you can combine alpha 1 and gras in one pass, alpha 2 and sand in a second pass, and so on, giving only 4 passes.

For each base texture (that is, grass, sand, rock and snow), bind the base texture on texture unit 0, and the corresponding alpha map on texture unit 1, and setup the texture environment like this.



glActiveTexture(GL_TEXTURE0);
glTexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnv(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnv(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glActiveTexture(GL_TEXTURE1);
glTexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnv(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnv(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
glTexEnv(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ALPHA);
glTexEnv(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

And use additive blending to blend different passes. That is, glBlendFunc(GL_ONE, GL_ONE).

surgptr
05-06-2003, 09:09 AM
The glBlendFuncSeparate solution works but the framerate dropped from 150 fps to less than 1 fps. Probably due to my old TNT2 not supporting the operation. http://www.opengl.org/discussion_boards/ubb/smile.gif

The disable color writes solution works very well and doesn't seem to affect the framerate at all.

The multitexturing solution just gives me a black screen. I think there's something wrong with the texture environment setup but I'll go and wade through the OpenGL 1.4 spec and see if I can figure it out.

Your assumption was right about adding each stage together (A1*T1 + A2*T2 + ... ) so multitexturing will work nicely for my application but I didn't know that you could blend alpha and color maps together using multitexturing.
It should also give me much better performance since I will only have to push half as many vertices down the pipeline.

Paul

Bob
05-06-2003, 09:35 AM
I now see an error in the environment setup. In the second texture unit GL_OPERAND1_RGB should be GL_SRC_ALPHA, not GL_ALPHA.

And less than 1 fps for glBlendFuncSeparate on a TNT2 is pretty much as expected. Separate blending functions is a part of the imaging subset, and the TNT2 doesn't support much of it in hardware.

[This message has been edited by Bob (edited 05-06-2003).]

surgptr
05-06-2003, 10:12 AM
Originally posted by Bob:
I now see an error in the environment setup. In the second texture unit GL_OPERAND1_RGB should be GL_SRC_ALPHA, not GL_ALPHA.

It works perfectly now and I get decent framerates of 75 fps with 3 set of textures and alpha maps.

Blending the first 2 textures with glBlendFunc(GL_ONE, GL_ONE) works fine but when I add the third multitexture I have to use glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) and add an alpha component of 1 to get the third texture to blend in otherwise I end up with an oversaturated looking image.

So I guess (A1*T1 + A2*T2 + A3*T3 ...) is not exactly what I thought it was. http://www.opengl.org/discussion_boards/ubb/smile.gif The new texture being blended in should replace what is already there except where the alpha value from the alpha map is less than 1. In that case it must blend in.

Ok, now where did you learn about all those extensions because they are not in the OpenGL 1.4 specs and they are hardly mentioned on the Web. I did a search for GL_SOURCEX_RGB and GL_OPERANDX_RGB and hardly any sites showed up. I guess most people are still doing blending the "old way".

Those opengl extensions rock! I hope they make them part of the standard base of OpenGL 2.0 and someone writes a book on how to use them properly.

[This message has been edited by surgptr (edited 05-06-2003).]

Bob
05-06-2003, 10:40 AM
The extension is called ARB_texture_env_combine (EXT instead of ARB for an earlier version), and you can find the official extension specifications here (http://oss.sgi.com/projects/ogl-sample/registry/) . In the OpenGL 1.4 specification, you find it in in section 3.8.13, page 152.