Textures and Blending

Back to index

One reason this is confusing is that we are dealing with two different, but interacting, things which refer to 'blend'. One is blending, switched on by

gl.glEnable(GL.GL_BLEND);

and controlled by, for example,

gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

The other is the function that controls how textures are applied, like

gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);

or

gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_BLEND);

First example

We draw a blue quad behind a red quad..

        final GL gl = gLDrawable.getGL();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        glu.gluLookAt(camera.getX(), camera.getY(), camera.getZ(), lookAt.getX(), lookAt.getY(), lookAt.getZ(), up.getX(), up.getY(), up.getZ());

        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);
        gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
        blueQuad(gl);
        redQuad(gl);

We've loaded this TGA texture..1 and we draw the red quad with the texture on it, like this:

    private void redQuad(GL gl) {
        gl.glBegin(GL.GL_QUADS);
        gl.glColor4f(1.0f, 0.0f, 0.0f, 0.0f);
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex3d(0, 0, 0);
        gl.glTexCoord2f(0.0f, 1.0f);
        gl.glVertex3d(0, 1, 0);
        gl.glTexCoord2f(1.0f, 1.0f);
        gl.glVertex3d(1, 1, 0);
        gl.glTexCoord2f(1.0f, 0.0f);
        gl.glVertex3d(1, 0, 0);
        gl.glEnd();
    }

We have not enabled blending. The colour of the quad is pure red, with zero alpha. We see: 2

Why? Chapter 9 of the Red Book says that if you use DECAL with glTexEnv, with an RGBA texture, you get:

C = Cf(1-At) + CtAt, A = Af

At is the texture alpha. Most places on ours that is zero, so C = Cf, where Cf is the original (fragment) colour, which is red. Otherwise our texture alpha is 1, so C = At, and we just see the texture colour. The 'Pure red' is invisible simply because it is pure red on a pure red background.

What about the alpha? We have A = Af, and the quad was drawn with zero alpha. So how come we can see the red quad at all? Because blending is off. If we switch it on:

   ..
        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);
        gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
        gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        blueQuad(gl);
        redQuad(gl)

It looks like this 3The red quad with zero alpha is invisible, and the blue quad with alpha of 0.6 is a bit dimmer.

If we draw the 'red quad' with a pure green colour, and an alpha of 0.3,

private void redQuad(GL gl) {
        gl.glBegin(GL.GL_QUADS);
        gl.glColor4f(0.0f, 1.0f, 0.0f, 0.3f);
        gl.glTexCoord2f(0.0f, 0.0f);
        gl.glVertex3d(0, 0, 0);
        ..

then 0.3 will be the alpha of the result, and we see 4

So for DECAL, we get a colour mixture of the quad and the texture, in proportion to their alphas, and the alpha of the result is the original quad alpha.

Using GL_REPLACE

gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);

simply replaces colour and alpha with that of the texture. This means we will just see the texture (and nothing else) on the surface:

5

This is drawn by

final GL gl = gLDrawable.getGL();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        glu.gluLookAt(..

       
        gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        gl.glDisable(GL.GL_TEXTURE_2D);
        blueQuad(gl);
        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
        gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
        redQuad(gl);

We've disabled textures before drawing the blue quad - or it would have been replaced by nothing, and been invisible.

Using GL_BLEND

If we use

gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_BLEND);

then the Red Book tells us we get

C = Cf(1-Ct) + CcCt, A = AfAt

Where Cc is the environment colour. To start with, we draw the red quad in white..

gl.glBegin(GL.GL_QUADS);
        gl.glColor4f(1.0f, 1.0f, 1.0f, 0.9f);
        gl.glTexCoord2f(0.0f, 0.0f);
        ..

and draw the scene with

        gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        gl.glDisable(GL.GL_TEXTURE_2D);
        blueQuad(gl);
        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_BLEND);
        gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
        redQuad(gl);

then we get 6

The Cf(1-Ct) 'inverts' the texture colour - for example blue = 001 becomes yellow = 110, and red=100 becomes cyan 011. Multiplying it by white=111 does not change it - if the quad was coloured it would simply filter the inverted colour. I can't see much point in this - so if we draw the quad in black Cf=0 and we can ignore this term. Then we can set the 'environment color' and use that to filter the texture colour:

gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        gl.glDisable(GL.GL_TEXTURE_2D);
        blueQuad(gl);
        gl.glEnable(GL.GL_TEXTURE_2D);
        gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_BLEND);

        gl.glTexEnvfv(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_COLOR, new float[]{1.0f, 1.0f, 0.0f, 1.0f}, 0);
       
        gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
        redQuad(gl); 

produces7