Using a separate alpha channel for a repeating texture

Sage
Posts: 1,199
Joined: 2004.10
Post: #1
I'm curious if there's a way to selectivly multitexture based on values in a 3rd 8-bit luminance type texture ( acting as a separate alpha-channel )

Now, to try to explain this better, my situation is that I've got a terrain, with repeating surface and detail textures ( applied via auto texture coordinate generation and specially set up texture matrix ) and it looks nice and all. That said, I'd like to be able to apply different textures based on some factor -- perhaps height, perhaps something else. My terrain is uploaded via VBO, so I can't just draw in immediate mode and use glColor4f to set the alpha on a per texture vertex basis.

My textures are small and repeating, so I can't just embed a large alpha channel to them. So what I was thinking was what if I had a large 8-bit texture acting as a mask where 0 means don't apply the X texture and 255 means fully apply the X texture.

Is there some way to tell opengl to apply texture X for fragment A based on the value of texture Y for fragment A?

Thanks,
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
you could use ARB_texture_env_combine to use one texture as the interpolation parameter between two others, but you'd need a 3-texture card to do it in one pass (Radeon or GeForce3).

You could do it in two passes with two textures per pass (the mask and one detail texture) quite straightforwardly...
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #3
What is your target hardware?
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #4
arekkusu Wrote:What is your target hardware?

To be honest, the game right now isn't meant for mass consumption -- I'm just having fun adapting my robotics code for a game engine. I'm using it as an opportunity to study flocking AI and emergent behaviors. And to drive around Mars and shoot aliens Wink

So, that said, my PB has a 5200 Fx GO, which as far as I can tell from Opengl profiler supports 4 texture units. The trouble is, I'm already using 3 -- a surface texture, a detail texture, and a lightmap/colormap texture. The first two are applied with an inverse scaling to repeat across the terrain and the latter is a 1 to 1 mapping across the terrain.

So this means I'd have just one texture unit left. I'm willing to drop the detail texture, so I'd be able to apply the surface texture across the whole terrain, then use 2 texture units for my blending of an alternate texture.

Since I'm an ABSOLUTE OPENGL NOOB ( self taught, sporadically as needed, over that last 13 months using NeHE and The red Book ) can somebody give me a 10'000 foot view of how to approach this? As in what ARB_texture_env_combine *is*?

My work has focused on AI, electronics, and machine-shop work for the last couple years -- all for my robotics work -- so my OpenGL has mainly been a minimum; as in how do I do exactly what I need. Accordingly, my robot simulator is pretty simple graphically. Example: [Image: Quadruped.png]

Writing a game has taught me a lot, but I've got a way to go, yet.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #5
If you only care about things working on a GeForce FX, then ARB_vertex_program and ARB_fragment_program are definitely the way to go -- much easier than ARB_texture_env_combine Smile

Take a look at the OpenGL Shader Builder in /Developer/Applications...
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #6
The 10,000 foot view is that hardware compatibility is a pain in the ass. Already, using three texture units, your application won't work on a GeForce 2MX or 4MX, which eliminates Macs sold as recently as this year (Rev A 17" PowerBook G4.)

Your GFFXGo actually has 8 texture units, but only 4 are exposed through the fixed-function pipeline. Compare MAX_TEXTURE_UNITS and MAX_TEXTURE_IMAGE_UNITS_ARB.

ARB_texture_env_combine is the extension to allow flexible texture combining in the fixed-function pipeline. It is supported on all renderers under OS X, so within your available texture units you can play with the combining to achieve different effects. In your case using interpolation mode like OSC said.

The modern approach this this sort of problem is to use ARB_fragment_program where you have total control on how texels are chosen. But this is supported on a much smaller set of hardware, so although it might work out fine for your application, I can't recommend it in general until Apple ships capable hardware in everything they sell. That means sticking a Radeon 9600 into the iBook, sometime next year.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #7
Thanks for the answers; I would love to pester you two further but I'm leaving for vacation to san diego tomorrow. When I get back I'll take a look at the two approaches.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #8
It looks to me like ARB_texture_env_combine is the way to go for me since, well, I don't know spittle about fragment programming and at the moment it's not worth my time to remedy that.

Now, that said, I'm going to attempt to write a simple program to learn how to use ARB_texture_env_combine. Probably just slapping a textured quad on the screen and seeing what I can do.

So, reading the SGI page it looks like I can pass COMBINE_ARB to glTexEnf and friends, but I'm not exactly certain of the syntax. Could you give me a pointer? Also, do I need to explicitly turn *on* the functionality, or does it just work, provided the card supports it?

Sorry to pester.

Anyway, on a side note, during free time on my vacation I wrote a cool flocking/hive-mind system for the enemies in my game. It's really cool -- the enemies by themselves are unintelligent but as they team up their "intelligence" grows and they're capable of independent as well as hive-mind group directed actions. In my demo they look and act creepily like cellular phenomena, tracking down and engulfing targets. Very lifelike.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #9
I've done a little more reading and I have some more specific questions.

Let's say I've got an 8-bit luminance style texture, called "A" which I want to use as my "alpha channel". And I've got another texture "B" which is my texture I want to apply.

Wherever A is white I want full application of B. Where A is black, no application of B, and linearly interpolated in between.

So, given the definition of glTexEnf,
Code:
void glTexEnvf(GLenum target, GLenum pname, GLfloat param);


And the SGI docs, it looks like I'd want to use the MODULATE parameter.

Code:
glTexEnv( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE );

But, the question is, how do I set up the texture units, so it knows which is which? I'm sorry if I'm being dense, here.

would it be something like this?
Code:
glTexEnvi( GL_TEXTURE_ENV, SOURCE0_RGB_ARB, GL_TEXTURE_0_ARB );
glTexEnvi( GL_TEXTURE_ENV, SOURCE1_RGB_ARB, GL_TEXTURE_1_ARB );

//or

glTexEnvi( GL_TEXTURE_ENV, SOURCE0_RGB_ARB, surfaceTextureID );
glTexEnvi( GL_TEXTURE_ENV, SOURCE1_RGB_ARB, surfaceAlphaTextureID );

Thanks,
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #10
No... but that first approach is like texture_env_crossbar, which is available on ATI hardware.
With texture_env_combine, you only get one texture input per texture unit, you can't specify TEXTURE_N. Only TEXTURE, PREVIOUS, PRIMARY, and CONSTANT.

There are some posts on the board with example code for texture_env_combine effects, check them out:
one
two

Apple also has some sample code called TexCombineLab which is good for experimenting with the possible combinations. It is no longer listed on their download page but you can still get it from the ftp archive.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #11
So, this morning, after reading the pages arrekusu linked on idevgames as well as ( http://www.cs.auckland.ac.nz/~jvan006/mu...titex.html ) and some more, I wrote a quick GLUT app to try to figure this thing out.

I made it simple as possible, I've got three textures. An "X" over blue, repeating. An "O" over purple, repeating as well. And a mixmap which is a black square fading to white in the center. The idea being that I'd try to use the mixmap to cause the "O" texture to show in the middle, fading out to the "X" texture on the rest. By the way, I'm aware that I can use vertex colors to do my mixing -- it even seems like a good idea but my terrain engine has an LOD system which results in tesselation which probably wouldn't be too useful for this task. Ultimately, I figure I might use the alpha channel of my colormap to do the mixing.

I managed to get it to work, sort of, but I'm doing something dumb. Here's a screenshot.

[Image: BooHoo.png]

So here's what's happening, as I see it. The "O" texture is correctly being blended in in the center fading out to the "X" texture everywhere else, just as I want. But the mixmap is being drawn *over* the whole thing, anyway.

Here's my display function:

Code:
void display(void)
{
    glClearColor (0.5,0.5,0.5,1);
    glClearDepth( 0 );
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glDisable( GL_LIGHTING );
    glShadeModel( GL_SMOOTH );
    
    int cx = _width / 2;
    int cy = _height / 2;
    int size = (int)((_width * 3.0) / 4.0);
    int hSize = size / 2;
    float tex0Scale = 12.0f;
    float tex1Scale = 12.0f;
    
    /*
        Bind textures
    */

    glActiveTextureARB( GL_TEXTURE0_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _texture0 );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

    glActiveTextureARB( GL_TEXTURE1_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _texture1 );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

    glActiveTextureARB( GL_TEXTURE2_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _mixmap );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );



    glActiveTextureARB( GL_TEXTURE1_ARB );

    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );

    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE1_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE0_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE2_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR );

    glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB );
    
    glPushMatrix();
    glTranslatef( cx, cy, 0 );
    
    glColor4f( 1, 1, 1, 1 );
    glBegin( GL_QUADS );
    
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, -tex0Scale, -tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, -tex1Scale, -tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 0, 0 );
        glVertex2i( -hSize, -hSize );
        
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, tex0Scale, -tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, tex1Scale, -tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 1, 0 );
        glVertex2i( hSize, -hSize );
        
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, tex0Scale, tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, tex1Scale, tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 1, 1 );
        glVertex2i( hSize, hSize );
        
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, -tex0Scale, tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, -tex1Scale, tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 0, 1 );
        glVertex2i( -hSize, hSize );    
    
    glEnd();
    
    glPopMatrix();

    glutSwapBuffers();
}

I suppose what I need is to figure out how to get GL_TEXTURE2_ARB to be mixed against, but not drawn.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #12
Oh no! Just seconds after posting I solved it.

Code:
glActiveTextureARB( GL_TEXTURE1_ARB );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );

It should have been GL_TEXTURE2_ARB.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #13
New developments, new troubles.

I managed to extend the testbed such that instead of using the whole RGB mixmap texture as a channel for blending the two other textures, I got it to just use the alpha channel of the mixmap -- such that in principle I could use the RGB channels for a colormap. I would need 4 texture units but only three textures. Without applying the colormap, it works great!

So, I figured, I have three textures, A, B, & ColorMixmap, where the RGB channels of ColorMixmap are the colormap and the alpha is the mixmap for A & B. Then, all I have to do is bind the colormap to a fourth texture unit and set it up to blend just the RGB parts.

Here's my code:
Code:
void display(void)
{
    glClearColor (0.5,0.5,0.5,1);
    glClearDepth( 0 );
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glDisable( GL_LIGHTING );
    glShadeModel( GL_SMOOTH );
    
    int cx = _width / 2;
    int cy = _height / 2;
    int size = (int)((_width * 3.0) / 4.0);
    int hSize = size / 2;
    float tex0Scale = 12.0f;
    float tex1Scale = 12.0f;
    
    /*
        Bind textures
    */

    glActiveTextureARB( GL_TEXTURE0_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _texture0 );

    glActiveTextureARB( GL_TEXTURE1_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _texture1 );

    glActiveTextureARB( GL_TEXTURE2_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _mixmap );

    glActiveTextureARB( GL_TEXTURE3_ARB );
    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _mixmap );

    /*
        Set up mixmap blending, use alpha channel of TEXTURE2
        as interpolative factor between TEXTURE0 and TEXTURE1
    */
    glActiveTextureARB( GL_TEXTURE2_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );

    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE0_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE1_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE2_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, GL_SRC_ALPHA );

    glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB );

    /*
        Set up colormap blending
    */
    glActiveTextureARB( GL_TEXTURE3_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );

    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE3_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );

    //why doesn't this give me a constant alpha -- it seems to still blend against
    //the images alpha channel
    glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_CONSTANT_ARB );
    glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA );
    
    glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE );

    
    glPushMatrix();
    glTranslatef( cx, cy, 0 );
    
    glColor4f( 1, 1, 1, 1 );
    glBegin( GL_QUADS );
    
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, -tex0Scale, -tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, -tex1Scale, -tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 0, 0 );
        glMultiTexCoord2fARB( GL_TEXTURE3_ARB, 0, 0 );
        glVertex2i( -hSize, -hSize );
        
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, tex0Scale, -tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, tex1Scale, -tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 1, 0 );
        glMultiTexCoord2fARB( GL_TEXTURE3_ARB, 1, 0 );
        glVertex2i( hSize, -hSize );
        
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, tex0Scale, tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, tex1Scale, tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 1, 1 );
        glMultiTexCoord2fARB( GL_TEXTURE3_ARB, 1, 1 );
        glVertex2i( hSize, hSize );
        
        glMultiTexCoord2fARB( GL_TEXTURE0_ARB, -tex0Scale, tex0Scale );
        glMultiTexCoord2fARB( GL_TEXTURE1_ARB, -tex1Scale, tex1Scale );
        glMultiTexCoord2fARB( GL_TEXTURE2_ARB, 0, 1 );
        glMultiTexCoord2fARB( GL_TEXTURE3_ARB, 0, 1 );
        glVertex2i( -hSize, hSize );    
    
    glEnd();
    
    glPopMatrix();

    glutSwapBuffers();
}

What I get is the blended TEXTURE0 and TEXTURE1 as expected, but the colormap is applied only where the colormap's alpha channel is non-zero. And everywhere else blends to black.

[Image: BooHoo2.png]

Any ideas? Perhaps there's a way without needing to use the fourth texture unit?
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #14
Quick question, how are you loading your texture data for the colormap. Are you sure that the texels are not being premultiplied by the image loader?
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #15
I think this setup does what you want; interpolate between two textures using the alpha channel of a third texture, and modulate the RGB color by the RGB of the same third texture. I do it with three units using combine and crossbar:
Code:
    // setup first texture unit as REPLACE (no vertex colors for us)
    glActiveTextureARB(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture[GRASS]);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    // setup second texture unit as INTERPOLATE (using the mixmap alpha via crossbar)
    glActiveTextureARB(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture[WATER]);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE2);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_INTERPOLATE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA, GL_TEXTURE2);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, GL_SRC_ALPHA);

    // setup third unit as MODULATE (colormap tinting, passthrough alpha)
    glActiveTextureARB(GL_TEXTURE2);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture[MIXMAP]);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
Note that I am using crossbar to specify the third texture as the interpolation parameter. Also note that this requires an image loader which will give you the raw RGB,A values of your mixmap without destroying them with premultiplication. QuickTime works, NSImage will not.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  OpenGL Alpha Channel Problem Moganza 1 2,995 Jan 19, 2013 08:25 AM
Last Post: sealfin
  Pasting an image with alpha mask on a texture Najdorf 10 5,808 Jun 24, 2008 03:23 PM
Last Post: Najdorf
  Trouble writing PNGs with 16 bits per channel using libpng flash 2 3,748 Aug 28, 2006 11:01 AM
Last Post: flash
  Using 8-bit greyscale texture as an alpha channel? TomorrowPlusX 12 4,904 Apr 10, 2006 08:43 AM
Last Post: TomorrowPlusX
  Alpha Channel Woes GraySupreme 5 4,218 Jul 26, 2005 01:24 PM
Last Post: NYGhost