combining textures and lighting

im trying to do this V * ( BA + C(1-A))
V = vertex lighting B+C are RGB textures, A is a alpha texture.
ive managed to do it by substiting a lightmap for V but this takes 3 passes which is not exactly cheap.
my problem stems from basically not understanding how textures combine together
are there any pages on the net that explain taking an equation eg the one above and turing it into opengl code
cheers zed

There are a lot of ways to do this. I’ll try to suggest some ways that will work on a fairly broad range of HW.

First of all, realize that you can rewrite the mathematical expression V(AB + (1-A)C) in a few different ways:

VAB + V(1-A)C
A(VB) + (1-A)(VC)

The latter suggests the following. First render VB; enable texture 0, bind texture B, texenv mode MODULATE, and send down your vertex colors or use OGL lighting. You now have VB in the framebuffer.

On the second pass, enable both textures and bind texture C as the first and A as the second. Set the texenv modes to be MODULATE and then REPLACE. Send down the vertex colors again, or leave lighting on. The output of texturing will then be VC in RGB and A in alpha. Set your blending mode to be ONE_MINUS_SRC_ALPHA, SRC_ALPHA.

So this gives you the result you want on any dual-texture HW with no texenv extensions, in two passes.

Here is the setup code:

// first pass of multipass
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, B);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);

Render();

// second pass of multipass
glDepthFunc(GL_EQUAL);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, C);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, A);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

Render();

If you want to make it faster, the best thing you can do, if it’s possible, is to roll texture A into either texture B or C as an alpha channel. I don’t know what texture coordinates you’re using, but if A uses the same coordinates as either B or C, you can roll that pair together. (If all three use the same coordinates, you should probably just precompute AB+(1-A)C and put it in a texture on its own.)

Let’s assume A and B had the same coordinates, so now B’s alpha is A. You can now render in one pass – but only with NV_texture_env_combine4. (The math is there with EXT_t_e_combine, but you need to be able to access either texture in either stage.) This extension is supported on TNT/TNT2 and up.

Bind texture B as texture 0 and bind texture C as texture 1. The first texenv stage will compute B_a*B_rgb + (1-B_a)*C_rgb = AB+(1-A)C, and the second will multiply by the vertex color.

Here is the setup code:

// no multipass (you may be able to omit this code)
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, B);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV);

// Combine4 stage 0: B_a*B_rgb+(1-B_a)*C_rgb
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_RGB, GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_RGB, GL_SRC_COLOR);

glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, C);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV);

// Combine4 stage 1: PrimaryColor*Previous
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_RGB, GL_ZERO);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_RGB, GL_SRC_COLOR);

Render();

// second pass of multipass
glDepthFunc(GL_EQUAL);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, C);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, A);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

Render();

  • Matt

Argh, it refuses to let me edit my message. A correction: I forgot to delete the second pass from the second setup code sample. It doesn’t need it.

  • Matt

WOW bloody hell mate i wasnt expecting such an essay from my short question. thanks heaps ill have to stick up a demo of it on me site, i was hoping to do it today but have been suffering the effects of having a sister who is taking a winemaking degree, namely a throbing headache
cheers zed