Blending and Multitexturing

Hi !

I want to blend several textures together using
the alpha channel of the textures.

I have to render an water surface with an oil slick on it.

I want the water (1. texture) to be partial transparent. (like real water is…)

Second texture is that of an oil slick, which
has an alpha value of 0.0f where no oil is and 1.0f when there’s oil.
(This means the oil texture should replace the
water texture at this locations)

After that I want to apply one or more textures
which show where currents are. They have an alpha
value of 0.0f where no current is and an alpha
value of 0.5f where a current is.
(This means that the color of the water should
change for example to red where the current is.)

How do I have to set my multitexturing and/or
blending and/or alpha test up ?

I thought

glBlendFunc (GL_ONE_MINUS_DST_ALPHA, 
             GL_DST_ALPHA);
glAlphaFunc (GL_GREATER, 
             0.0f );

would be sufficient, but it doesn’t work.

Andre

you use blendfunc for all incoming fragments, in this case a multipass solution.

if you check this forum there is a thread about blending factors active at the moment, which describes how blendfunc works quite nicely.

the SRC is always what you are about to render, the DST is what is already on screen, guess you just mixed those two up.

glAlphaFunc (GL_GREATER, 0.0f );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glColor4f(1,1,1,0.5); // 0.5 for 50% transparency
drawWater();
glColor4f(1,1,1,1);
drawOil();
glDisable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
drawCurrent();
glDisable(GL_ALPHA_TEST);

not sure if that would achieve the proper result, since not sure if that is the effect you wanted

to blend textures within multitexture stages, with a single pass (depending if your system can handle as many texture units) you would use
GL_ARB_texture_env_combine, NV_combine4, or this shader language stuff which I have no clue about hehe.
however you would need to set blendfunc to (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) before rendering the mtex combined surface as well, and sometimes it might not be possible to get the proper effect done within tex_combine

so the decision is to either do multipass or (if enough texunits exist) do single pass and try to combine the textures properly.
Easiest would be multipass for sure.

you use blendfunc for all incoming fragments, in this case a multipass solution.

I thought I need the blendfunc, because I want to
make my water texture semitransparent and I need
the blend func to combine the floor with the
water.

if you check this forum there is a thread about blending factors active at the moment, which describes how blendfunc works quite nicely.

the SRC is always what you are about to render, the DST is what is already on screen, guess you just mixed those two up.

That’s correct. I mixed it up… :slight_smile:

glAlphaFunc (GL_GREATER, 0.0f );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glColor4f(1,1,1,0.5); // 0.5 for 50% transparency
drawWater();
glColor4f(1,1,1,1);
drawOil();
glDisable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
drawCurrent();
glDisable(GL_ALPHA_TEST);

not sure if that would achieve the proper result, since not sure if that is the effect you wanted

to blend textures within multitexture stages, with a single pass (depending if your system can handle as many texture units) you would use
GL_ARB_texture_env_combine, NV_combine4, or this shader language stuff which I have no clue about hehe.
however you would need to set blendfunc to (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) before rendering the mtex combined surface as well, and sometimes it might not be possible to get the proper effect done within tex_combine

so the decision is to either do multipass or (if enough texunits exist) do single pass and try to combine the textures properly.
Easiest would be multipass for sure.

I already implemented multitexturing. I just have to find the correct combination and/or change my texture to the correct format.

I used a tool called “OGLMulTex” from ATI to try
several combinations for GL_SOURCE0_RGB_EXT and all the other parameters, but didn’t find the correct combination.

Any ideas ? (Most of my textures are created at runtime, so I can change almost all colors, alpha values, … if needed)

Andre

well using blendfunc is perfectly fine, likely misformulated that first sentence.

setting up the combines hm well, here my first try:

first pass using the blendfunc:

tex 0 = water
texenvcolor (1 1 1 0.5)
combinergb: replace
src: texture
op: src_color

combinealpha : replace
src: constant
op: src_alpha

tex 1 = oil
combinergb: interpolate
srcs: previous, texture, texture
ops: src_color, src_color, src_alpha

combinealpha: (same as above, but use src_alpha instead of src_color)

this should result into blended oil/water, which is unlit
you could lit it with

tex 2 = (doesnt matter)
combinergb: modulate
srcs: previous, primarycolor
ops: src_color, src_color
combinealpha: replace
src: previous
op: src_alpha

now the thing with the alphatest would still require a second pass.

not sure if this would work but well worth a try.

the problem is with interpolate (GL_combine) or just GL_DECAL you always loose the light (primary color) information, which you would need to multiply with after the intpolation between the textures is done.
the other workaround would require arb_texenv_crossbar / nv_combine4 extension to mix textures of different texture units.

I am still new to this stuff myself, so lacking the “will work like this” experience hehe

I tried your idea und wrote the following code:

GLfloat af_texEnvColor[] = {1.0f, 1.0f, 1.0f, 0.5f};

glTexEnvi (GL_TEXTURE_ENV, 
	   GL_TEXTURE_ENV_MODE, 
	   GL_COMBINE_EXT);

/*
    init water texture 
*/
glActiveTextureARB (GL_TEXTURE0_ARB);

glTexEnvfv (GL_TEXTURE_ENV,
  	    GL_TEXTURE_ENV_COLOR,
	    af_texEnvColor);

glTexEnvi (GL_TEXTURE_ENV, 
           GL_COMBINE_RGB_EXT, 
           GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE0_RGB_EXT, 
           GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND0_RGB_EXT, 
           GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_COMBINE_ALPHA_EXT, 
           GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE0_ALPHA_EXT, 
           GL_CONSTANT_EXT);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND0_ALPHA_EXT, 
           GL_SRC_ALPHA);
/*
  wrapper class for texture...
*/
mpk_texture[0].bind ();
glEnable (GL_TEXTURE_2D);

/*
   init oil texture
*/
glActiveTextureARB (GL_TEXTURE1_ARB);

glTexEnvi (GL_TEXTURE_ENV, 
           GL_COMBINE_RGB_EXT, 
           GL_INTERPOLATE_EXT);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE0_RGB_EXT, 
           GL_PREVIOUS_EXT);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND0_RGB_EXT, 
           GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE1_RGB_EXT, 
           GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND1_RGB_EXT, 
           GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE2_RGB_EXT, 
           GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV,  
           GL_OPERAND2_RGB_EXT, 
           GL_SRC_ALPHA);

glTexEnvi (GL_TEXTURE_ENV, 
           GL_COMBINE_ALPHA_EXT,
           GL_INTERPOLATE_EXT);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE0_ALPHA_EXT, 
           GL_PREVIOUS_EXT);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND0_ALPHA_EXT, 
           GL_SRC_ALPHA);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE1_ALPHA_EXT, 
           GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND1_ALPHA_EXT, 
           GL_SRC_ALPHA);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_SOURCE2_ALPHA_EXT, 
           GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, 
           GL_OPERAND2_ALPHA_EXT, 
           GL_SRC_ALPHA);
mpk_texture[1].bind ();
glEnable (GL_TEXTURE_2D);

I made some screenshots to show the results.

  1. That's how it looks like using only one water texture and “normal” texturing.

  2. That's how it looks like with 2 textures and the above init code.

The colors for the second texture are
{1.0f, 1.0f, 1.0f, 0.0f}
{0.0f, 0.0f, 0.0f, 1.0f}

Andre

Andre,

You’re going to want to use ‘Decal’ tex mode to texture the oil. Unfortunately this is the only way (that I know of) of applying a texture with transparency onto a surface while still maintaining the alpha values of the underlying fragments. (check the formulas for INTERPOLATE as well as MODULTE).

My guess is that you’ll soon want that oil to appear a bit more realistic (a GL_DECAL will simply replace the fragment color with the texture sample without consideration to the underlying fragment color which will cause your oil slick to appear artificial - you’re losing the light reflection on the water in these areas). In order to achieve this you’re going to need a fragment shader to combine GL_MODULATE and GL_DECAL into your own custom texturing method which both preserves underlying alpha fragment values as well as modulates the texture sample with the underlying fragment. I can post a shader that will do just that if you’re interested.

I don’t know how clear that all is. I hope I made some sense.

but for now :
use normal texturing for your water and
GL_DECAL for any other textures that have transparency in them.

Hi Aeluned,

You’re going to want to use ‘Decal’ tex mode to texture the oil. Unfortunately this is the only way (that I know of) of applying a texture with transparency onto a surface while still maintaining the alpha values of the underlying fragments. (check the formulas for INTERPOLATE as well as MODULTE).

Are you talking about multitexturing or multipass texturing ?

How do I have to set up

GL_SOURCE0_RGB_EXT
GL_OPERAND0_RGB_EXT

and stuff ?

what happens if both textures have an alpha value of 128 ?
I tried DECAL and MODULATE and if I remember right
it doesn’t work in any case… but I’m not quit sure…

My guess is that you’ll soon want that oil to appear a bit more realistic (a GL_DECAL will simply replace the fragment color with the texture sample without consideration to the underlying fragment color which will cause your oil slick to appear artificial - you’re losing the light reflection on the water in these areas). In order to achieve this you’re going to need a fragment shader to combine GL_MODULATE and GL_DECAL into your own custom texturing method which both preserves underlying alpha fragment values as well as modulates the texture sample with the underlying fragment. I can post a shader that will do just that if you’re interested.

I write on a simulator and I’ll need most of the power for the underlying simulation. I want the
graphic to look as good as possible while using
as less CPU time as possible (btw. I only have an
Intel 82845G controler :wink: )

Later on I’ll perhaps try to simulate some kind
of waves…

Are there any good tutorials about fragment shaders ? Can I use them with any GPU which supports multitexturing (GeForce x, ATI, Intel, …) ?

I don’t know how clear that all is. I hope I made some sense.

but for now :
use normal texturing for your water and
GL_DECAL for any other textures that have transparency in them.

Sorry, but same question again. Do you mean multipass texturing ?

Andre

the texmodes like GL_MODULATE, GL_COMBINE, GL_DECAL… work with both mtex and mpass, just that the incoming fragment will be different on mpass.

default is modulate, which will just serve as
“primary * tex” in a pass
or “previous * tex” in mtex environment

those are set with glTexEnvf(GLenum mode);
make sure you set this to GL_COMBINE_ARB when you want to make use of all this combine stuff

try to take parts of the combines out
or do just GL_REPLACE combines with
src: texture op: src_alpha/src_rgb

and then work your way up, ie check out what stuff will result into which effects, then maybe you get closer to a solution

Good Morning,

just thought a little bit about my problem…
I want the following formula for my second texture:

GL_PREVIOUS_COLOR * (1 - GL_SRC_ALPHA) + GL_SRC_COLOR * GL_SRC_ALPHA

Interpolate has the following formula:

Arg0 * (Arg2) + Arg1 * (1 - Arg2)

which is the same, only whis Arg0 and Arg1 switched.

I tried the following settings with oil alpha = 1.0f and everything else 0.0f :

glActiveTextureARB (GL_TEXTURE0_ARB);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
.
.

glActiveTextureARB (GL_TEXTURE1_ARB);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INTERPOLATE_EXT);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA);

I tried the following settings with oil alpha = 0.0f and everything else 1.0f :

.
.

glActiveTextureARB (GL_TEXTURE1_ARB);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INTERPOLATE_EXT);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_TEXTURE);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA);

In the first case, no texture was displayed…
in the second case the water was rendered correct,
but no oil visible…

Any ideas where my the problem is ? Did I apply
the formula wrong ?

Andre

hm it seems to look correct to me

but you could also try just normal texmodes
ie
tex0: TexEnvf(GL_REPLACE)
tex1: TexEnvf(GL_DECAL)

should produce what you want

If I use GL_REPLACE and GL_DECAL
or GL_REPLACE and GL_MODULATE, it
looks like the picture I posted above…
the water’s invisible and the oil has
the same color as the floor only a little
bit darker.

Once again, you can’t interpolate,modulate,replace,etc… with a texture having transparent sample fragments. These samples will factor into the previous fragment’s (in this case your water) alpha.
Think of what will happen when you modulate,interpolate or replace your oil slick texture sample (R,G,B,0) with an underlying fragment - the new fragment ends up with an alpha of 0!

If you render the water texture using GL_MODULATE or GL_REPLACE and then your oil slick and arrows using GL_DECAL you should see everything you want - water, oil, arrows (though not exactly the way you want to).

Unless someone out there knows of another way you’re going to need a fragment shader to get this looking exactly the way you want.

posted below is a fragment shader for some custom texturing applications.

what you want is Blend or Fade.
I understand that you’re not so familiar with shaders, but when you get that stuff rolling you can use the code below as a starting point.

  
varying vec3 normal; 	//calculated vertex normal.
varying vec3 eye;		//eye vector.

struct TexInfo
{
	uniform sampler2D tex;	//texture unit
	int application;		//application(modulate,decal,interpolate,etc...)
	float transp;			//overall texture tranparency level
	int method;			//method(reflective,projected,etc...)
	int enabled;  			//is this texture unit enabled?
};
//max texture units exposed to application is currently 4.  There are some issues with iterating over an array inside a loop which is controlled
//by a uniform variable.  driver issues? not currently implemented? I have no idea at the moment.
uniform TexInfo textureInfo[4];



//#############################################################################################         TEXTURE APPLICATION
//......------=========================================================------......
//	Augmented GL_MODULATE: (fragColor *= textureColor)
//	Preserves underlying fragments alpha value and modulates only texture
//	samples where alpha > 0.0.
//====================================================================
vec3 Blend(in vec3 fragColor, in vec3 textureColor, in float alpha)
{
	vec3 blend;
	blend = vec3(fragColor*(1.0-alpha) + fragColor*textureColor*alpha);
	return blend;
}

//......------=========================================================------......
//	Custom GL_INTERPOLATE:
//	'Blend' texture method with configurable overall texture tranparency.
//====================================================================
vec3 Fade(in vec3 color, in vec3 texture, in float TexAlpha, in float alpha)
{
	vec3 fade;
	//as texture transparency increases, underlying fragment color becomes more dominant.
	//this is the 'exchange' 
	//float exchange = max(0,TexAlpha-TexAlpha*alpha);
	float exchange = TexAlpha*(1.0-alpha);
	fade = vec3(color*(1.0-TexAlpha) + color*texture*TexAlpha*alpha + color*exchange);
	return fade;
}


void ApplyTextures()
{
	const int BLEND = 0;
	const int DECAL = 1;
	const int FADE  = 2;
	for(int i=0; i<4; i++)
	{
		if(textureInfo[i].enabled != 0)
		{
			//look up color in texture memory
			vec4 texture = texture2D(textureInfo[i].tex,vec2(gl_TexCoord[i]));

			if(textureInfo[i].application == BLEND)
			{
				gl_FragColor = vec4(Blend(gl_FragColor.rgb,texture.rgb,texture.a),gl_FragColor.a);
			}
			if(textureInfo[i].application == DECAL)
			{
				vec3 col = mix(gl_FragColor.rgb,texture.rgb,texture.a);
				gl_FragColor.rgb = col;
			}
			if(textureInfo[i].application == FADE)
			{
				float a = textureInfo[i].transp;
				float oneMinusa = 1.0-a;

				gl_FragColor = vec4(Fade(gl_FragColor.rgb,texture.rgb,texture.a,a),gl_FragColor.a);
			}
		}	
	}
}
	
//##############################################################################################        MAIN
void main()
{
	gl_FragColor = gl_Color;
	
	ApplyTextures();
}

good luck.

I looked at the supported extensions and
just saw that my graphic card only supports
vertex shading and no fragment/pixel shading.

Is it possible to do what I want with a vertex shader ?

I guess it’s not, but perhaps I’m wrong.

Andre