How to blend two textures together in hardware

Ok, so we are doing a terrain rendering engine, and we want to be able to combine texturemaps to have variety. The idea is to have a “base” texture (let’s say texture A) and a second texture (B).

Then, we want to have a “blending mask”, which will be a texture we will scale up in size (texture M).

We want to do:

A + M*B

in such a way that texture M determines how A and B are blended together. I have seen this effect in many terrain engines, but have been unable to implement it so far.

We have been looking at painting them with multitexturing, and we know how to do A+B, but are wondering how to apply (multiply) the blending mask to texture B.

Please help!.

If M is independent of A and B then on hardware which has 2 texture units, you will need to do this in 2 passes. Setup T1 to use GL_MODULATE mode by way of the GL_COMBINE_EXT, and set T0 to just pass on the texture color to T1, in the first pass render the terrain using (1-M) * A while glBlendFunc(GL_ONE, GL_ZERO), then in the second pass render M * B while glBlendFunc(GL_ONE, GL_ONE). The final result will be an interpolation (1-M) * A + M * B, which may look better than just A + M * B. But of course if you do want A + M * B, then in the first pass just render with A, then do the second pass like I described. On hardware that has one texture unit, you’d probably have to use dynamic textures, or do it in 3 complex passes. In newer hardware that has at least 3 texture units, this could likely be done in one simple pass.

Ok, sounds just perfect. Three questions:

how will the z-buffer resolve the two triangles blended together in the two-pass method? i mean, maybe it will put one before the other, and changing viewpoint slightly will reverse the situation, yielding visual artifacts?

does the COMBINE_EXT require an nVidia board? I mean, is it implemented just on GeFORCEs as part of the per-pixel stuff, or is it just plain multitexturing available on mainstream boards?

second, could you write down a chunk of code (or pseudocode) to illustrate the technique in detail? I’m afraid there’s too many details to get it right from our perspective, as we have never done it before… if you could just sketch the code it would help a lot…

well, thanks anyway.

how will the z-buffer resolve the two triangles blended together in the two-pass method? i mean, maybe it will put one before the other, and changing viewpoint slightly will reverse the situation, yielding visual artifacts?

There shouldn’t be any visual artifacts of this type since the terrain is rendered from the same exact point of view during the second pass. Just use GL_LEQUAL for the depth test of the first pass, and GL_EQUAL as the depth test of the second pass.

does the COMBINE_EXT require an nVidia board? I mean, is it implemented just on GeFORCEs as part of the per-pixel stuff, or is it just plain multitexturing available on mainstream boards?

No, the GL_EXT_texture_env_combine is not a NVIDIA specific extension. Most implementations should support it (if multitexturing is available).

second, could you write down a chunk of code (or pseudocode) to illustrate the technique in detail? I’m afraid there’s too many details to get it right from our perspective, as we have never done it before… if you could just sketch the code it would help a lot…

There should be code examples around that demonstrate the use of GL_EXT_texture_env_combine. I’ll see if I can find a few for you.

Eh, I’ll go ahead an type out an example

// setup first pass
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D, M);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_2D, A);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
glBlendFunc(GL_ONE,GL_ZERO);
glDepthFunc(GL_LEQUAL);

draw_terrain_polys();

// setup second pass
glBindTexture(GL_TEXTURE_2D, B);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
glBlendFunc(GL_ONE,GL_ONE);
glDepthFunc(GL_EQUAL);

draw_terrain_polys();


I would of course optimize this a bit in actual use by sorting the terrain polys by textures, and rendering them in groups of equivalent textures (which M would tend to mess up unfortunately).

In case you haven’t noticed, this example assumes that M is a greyscale or luminance type texture.

Also you’d probably need to rework the code a bit to get lighting to work. Or, do the lighting in a third pass.

[This message has been edited by DFrey (edited 10-06-2000).]

If you can encode the mask into the alpha channel of one of the textures, you can do this in one pass, even with the EXT combine extension.

Let’s say that you want A(1-M)+BM, and that M is encoded in the alpha of B.

Then bind A in unit 0 and B in unit 1, and set the environment as follows:

Unit 0: output Texture
Unit 1: interpolate Previous and Texture using TexAlpha

If you want any calculations beyond a pure blend with the mask stored in one of the textures’ alphas, you will have to move on to NV_texture_env_combine4 or NV_register_combiners, or accept a second rendering pass. For example, you might want to perform a vertex lighting calculation and output something like VertexColor*(A(1-M)+BM). This requires combine4.

  • Matt

I should add, if you’re doing two-pass rendering, in addition to setting the DepthFunc to EQUAL, as suggested by the other poster, make certain to set DepthMask to FALSE.

  • Matt

Good point about disabling depth writes during second pass, that slipped my mind.

cheers DFrey for taking the time out and answering the question thouroughly? ive been trying to do that for a while but never got it 100% chucked up a little demo of the code in action on me site anyways http://members.xoom.com/myBollux under the tex_env_combine extension

you guys kick major ass… thanks a lot. We’ll try to code that down in our engine next week, and maybe have a screenshot sometime by then.

thx all.