multipass-multitexturing problem

I have a hundred images I want to draw to an object. I have it working fine (albeit a bit slow) with only multipass. To speed things up I want to render them eight (for my card) at a time. However, when I do that only the first eight show. There seems to be a blending problem. One odd thing, I can render eight on the first pass and then one on each following pass.

To recap these rendering patterns work:
1,1,1,1,…1
8,1,1,1,…1
and this one doesn’t
8,8,8,…8,4

I have very little experience with OpenGL so I can’t tell if this likely to be a bug in my card or bug in my code. I feel my code is right and that there is a problem in my video card. What does it sound like to you guys? Is there a problem with combining mulitpass and multitexturing?

it is probably your code.
What is your video card and driver / os ?

The card is an ATI 9800 pro. The driver version is 8.282.0.0. The driver date is 8/2/2006. The OS is Winsows XP.

Here is my code. It’s in Java using JOGL (Java’s bindings for OpenGL). It should be easy to follow the OpenGL part though. Basically on the first pass I decal the textures and then on every pass after that I blend them. It only works if you set the numberOfTextures to 1 after the first pass.

 
	public void displayLayer(GL gl){

		for(LandSurfaceShard shard:shards){
			positionCamera(gl,shard.shardOffsetX,shard.shardOffsetY,shard.shardOffsetZ);
			boolean shardDrawn=false;
			boolean textureDrawn=false;
			boolean firstPass=true;
			int multiTextureCounter=0;

			for(Survey survey:layer.getSurveyList()){
				
				// .........Math removed..........

				for(SurveyTile surveyTile:survey.getSurveyTileList()){
					Boundary tileBoundary=surveyTile.getBoundary(survey.surveyX,survey.surveyY,survey.yAxisDegreesFromNorth,survey.surveyReferenceEarthRadius);
					if(!shard.boundary.overlaps(tileBoundary)){
						continue;
					}
					
					textureDrawn=false;
					gl.glActiveTexture(GL_TEXTURE[multiTextureCounter]);
					gl.glBindTexture(GL.GL_TEXTURE_2D,surveyTile.openGLImageName);
					gl.glEnable(GL.GL_TEXTURE_2D);

					if(firstPass){
						gl.glDisable(GL.GL_BLEND);
						gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_TEXTURE_ENV_MODE,GL.GL_DECAL);
					}else{
						gl.glEnable(GL.GL_BLEND);
						gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
						gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_TEXTURE_ENV_MODE,GL.GL_MODULATE);
					}

					gl.glEnable(GL.GL_TEXTURE_GEN_S);
					gl.glEnable(GL.GL_TEXTURE_GEN_T);

					gl.glTexGeni(GL.GL_S,GL.GL_TEXTURE_GEN_MODE,GL.GL_OBJECT_LINEAR);
					gl.glTexGeni(GL.GL_T,GL.GL_TEXTURE_GEN_MODE,GL.GL_OBJECT_LINEAR);

					// .........math removed...........

					gl.glTexGendv(GL.GL_S,GL.GL_OBJECT_PLANE,sParametersBuffer);
					gl.glTexGendv(GL.GL_T,GL.GL_OBJECT_PLANE,tParametersBuffer);

					multiTextureCounter++;
					
					if(multiTextureCounter==this.numberOfTextures){
						shard.display(gl);   //this draws the polygons

						for(int textureIndex=0;textureIndex<multiTextureCounter;textureIndex++){
							gl.glActiveTexture(GL_TEXTURE[textureIndex]);
							gl.glDisable(GL.GL_TEXTURE_2D);
						}
						
						firstPass=false;
						textureDrawn=true;
						shardDrawn=true;
						multiTextureCounter=0;
					}
				}

				if(!textureDrawn){
					shard.display(gl);

					for(int textureIndex=0;textureIndex<multiTextureCounter;textureIndex++){
						gl.glActiveTexture(GL_TEXTURE[textureIndex]);
						gl.glDisable(GL.GL_TEXTURE_2D);
					}
			
					firstPass=false;
					multiTextureCounter=0;
				}
			}

			if(!shardDrawn){
				shard.display(gl);

				for(int textureIndex=0;textureIndex<multiTextureCounter;textureIndex++){
					gl.glActiveTexture(GL_TEXTURE[textureIndex]);
					gl.glDisable(GL.GL_TEXTURE_2D);
				}
			}
			
		}

		gl.glFlush();
	}
 

I managed to test this with a different video card and I got the same result. So I guess the problem is in the code and not the card.

I have been looking around the web for examples of multipass-multitexture and I can’t find any. I can only find examples that use only one technique or the other and not both at once. If anyone knows of an example could he please post the link.

it looks like you are using the fixed pipeline with 8 textures
i believe most cards will only let u use 4 with that
if u use shaders eg glsl or cg u can access more textures (normally at least 16)

Originally posted by wgj000:
I have a hundred images I want to draw to an object. I have it working fine (albeit a bit slow) with only multipass. To speed things up I want to render them eight (for my card) at a time. However, when I do that only the first eight show. There seems to be a blending problem. One odd thing, I can render eight on the first pass and then one on each following pass.

Check to see how many texture units you have.
It would be something with glGetInteger(GL_MAX_TEXTURE_UNITS) or something like that.
ATI 9800 has 8 units for the fixed pipe.
16 for shaders. Actually, I think all cards from 9500 and above are like that.

I have 8 texture units. The variable this.numberOfTextures is determined from glGetInteger(GL_MAX_TEXTURE_UNITS). However, I can set it to whatever I want. It works if I set it to one. If fails if I set it to 2 or any other number. It also works if I set it to a number <=8 on the first pass and 1 for every other pass.

There seems to be a lack of information on doing multipass and multitexturing at the same time on the web. My worry is that when I try use multiple textures the the behavior of the card changes and multipass blending doesn’t work. But then maybe it’s just more complicated then I thought. If anyone knows for sure that multipass and multitexture at the same time is possible it would be helpful to tell me so. At least that way I can keep plugging away at the problem with confidence that I will eventually figure it out.

When you’re switching from multipass to multitexture+multipass, you have to be careful to choose the correct texture environment functions to achieve the same end result. I’ll demonstrate what i mean for three textures.

With multipass only, using glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), your result looks like this:

  Variables:
    d  - destination (color value in framebuffer)
    ai - alpha value of texture i
    ti - color value of texture i

  d'   = (1-a1)d + a1*t1
  d''  = (1-a2)d' + a2*t2
       = (1-a2)((1-a1)d + a1*t1) + a2*t2
  d''' = (1-a3)d'' + a3*t3
       = (1-a3)((1-a2)((1-a1)d + a1*t1) + a2*t2) + a3*t3

When you try to move to multitexture, you have to split out the input d, as you don’t have acces to frame buffer values in the texturing stage:

  d''' = (1-a3)(1-a2)(1-a1)d
       + (1-a3)(1-a2)a1*t1
       + (1-a3)a2*t2
       + a3*t3

In this form, it can be implemented using a combination of blending and multitexturing. For example using the blend function glBlendFunc(GL_SRC_ALPHA, GL_ONE).

In this case, you need your texturing stage to output

  alpha = (1-a3)(1-a2)(1-a1)
  color = (1-a3)(1-a2)a1*t1
        + (1-a3)a2*t2
        + a3*t3
        = (1-a3)((1-a2)a1*t1 + a2*t2) + a3*t3

To achieve this, you need the following texture unit setup:

Unit0:
  alpha = 1-alpha
  color = alpha*color

Unit1:
  alpha = previous * (1-alpha)
  color = (1-alpha)*previous + alpha * color

Unit2 and following use the same setup as unit1.

All that makes sense. Correct me if I’m wrong, but doesn’t that require that all the multitextuing take place in a single pass. I have too many textures to do in a single pass. I need to merge the new multitexture with the contents of the frame buffer. You say that during multitextuing I don’t have access to the framebuffer values. This sound like what I’m trying to do is not possible. It that your understanding?

No, you misunderstood. It is possible to combine multiple passes, it’s just that when you have more than one texture per pass, you need to use different texture environments and blend functions.

The texture unit setup i outlined above works for one or more textures per pass in one or more passes. The combination of GL_MODULATE and glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) only works for one texture per pass.

Btw, i just realized that my solution discards the result of the lighting process. If you want to include that, you’ll have to switch the last texture unit to

  alpha = previous
  color = previous * primary

This, of course, reduces the number of units available to merge textures by one.

Well I’ll be hornswaggled. Looks like I got it to work. Thanks to your advice. All I needed was a kick in the right direction.

Here is what it took:
gl.glBlendFunc(GL.GL_DST_COLOR,GL.GL_ZERO);
gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_TEXTURE_ENV_MODE,GL.GL_DECAL);

I would not have thought that that would work.

Thank you, thank you, thank you…

I want to keep this up to date since these pages get used as references.

The above fixed worked ok until I made the textures overlap.

What does work is this:

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

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

gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_COMBINE_RGB,GL.GL_INTERPOLATE); gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_SRC0_RGB,GL.GL_TEXTURE);
gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_OPERAND0_RGB,GL.GL_SRC_COLOR);
gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_SRC1_RGB,GL.GL_PREVIOUS);
gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_OPERAND1_RGB,GL.GL_SRC_COLOR); gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_SRC2_RGB,GL.GL_TEXTURE);
gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_OPERAND2_RGB,GL.GL_SRC_ALPHA);

gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_COMBINE_ALPHA,GL.GL_ADD);
gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_SRC0_ALPHA,GL.GL_TEXTURE); gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_OPERAND0_ALPHA,GL.GL_SRC_ALPHA); gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_SRC1_ALPHA,GL.GL_PREVIOUS); gl.glTexEnvf(GL.GL_TEXTURE_ENV,GL.GL_OPERAND1_ALPHA,GL.GL_SRC_ALPHA);

On the first pass, set the image receiving polygon color to something completely opaque and then make it transparent for every pass after that.

if(firstPass){
gl.glColor4f(1f,1f,1f,1f);
}else{
gl.glColor4f(1f,1f,1f,0f);
}