outputting mux to final combiner

Hello,

I’ve got a strange problem. I have 2 textures that I would like to compare and output the minimum. So, I’m using the register combiners’ mux operation, with a bias to 0.5:
tex2 - tex1 + 0.5

In the code below, I’m actually using 2 register combiners - one to add a constant color to one of the textures. I store the output of the mux in spare1, and just input that to the final combiner which basically doesn’t do anything. However, I’ve found that if I set the final combiner inputs A to spare1 and B to GL_ONE with a mapping of GL_UNSIGNED_IDENTITY, then the mux works but the texture values get changed - non-zero values are changed. But, if use GL_ZERO with GL_UNSIGNED_INVERT for B (or just map spare1 to D and not use A and B), then the textures don’t really get muxed - one texture is mapped to the polygon. The code snippet is below. Thanks for any help!

  • Quynh.

    // Add color to texture1
    glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    // Alpha portion: texture2 - texture1 + 0.5. To be placed in spare0_alpha for use with mux in combiner 1.
    glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_HALF_BIAS_NEGATE_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_ZERO, GL_SIGNED_NEGATE_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV, GL_TEXTURE2_ARB, GL_SIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV, GL_ZERO, GL_SIGNED_NEGATE_NV, GL_RGB);
    glCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    // Combiner 1: MUX using spare0_alpha between texture1 and texture2
    glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
    glCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_TRUE);

    // Final Combiner
    glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    //glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ONE, GL_UNSIGNED_IDENTITY_NV, GL_RGB); // Works, but non-zero texture values change
    glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB); // Doesn’t output mux.
    glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glFinalCombinerInputNV(GL_VARIABLE_E_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glFinalCombinerInputNV(GL_VARIABLE_F_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    glFinalCombinerInputNV(GL_VARIABLE_G_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
    glBegin(GL_QUADS);
    glNormal3f( 0.0f, 0.0f, 1.0f);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
    glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0f, 0.0f);
    glVertex3f(-1.0f, -1.0f, 1.0f);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
    glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0f, 0.0f);
    glVertex3f( 1.0f, -1.0f, 1.0f);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
    glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0f, 1.0f);
    glVertex3f( 1.0f, 1.0f, 1.0f);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
    glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0f, 1.0f);
    glVertex3f(-1.0f, 1.0f, 1.0f);
    glEnd();
    glDisable(GL_TEXTURE_SHADER_NV);
    glDisable(GL_REGISTER_COMBINERS_NV);
    glFlush();

I see a lot of issues here. First, GL_ONE can’t be used as a combiner input. You have to use GL_ZERO with GL_UNSIGNED_INVERT_NV.

Second, all of your calls to glCombinerInputNV with the portion parameter set to GL_ALPHA are actually failing because the componentUsage parameter cannot be GL_RGB. For alpha combiners, the componentUsage parameter must be GL_ALPHA or GL_BLUE.

For your B and D variables in the combiner 0 alpha portion, why are you using GL_SIGNED_NEGATE_NV? This doesn’t change the value of GL_ZERO. For the C variable, you probably want to use GL_UNSIGNED_IDENTITY_NV, unless texture 2 has signed components stored in it.

Also, I’m assuming that you remove the second pair of calls in the final combiner setup that set both A and B to zero.

It appears that you realize that the alpha portion of GL_SPARE0_NV is equal to the alpha portion of GL_TEXTURE0_ARB in combiner 0, but I thought I’d mention it just in case. The code would be more readable if you made the input to variable A, alpha portion, GL_TEXTURE0_ARB.

[This message has been edited by Eric Lengyel (edited 09-29-2003).]

Thanks for the reply. I didn’t know that the componentUsage had to be GL_ALPHA or GL_BLUE when portion is GL_ALPHA. Unfortunately, with this change the mux still doesn’t seem to be operating the way I think it should. Some answers to your questions:

I was using signed values in the combiner 0 alpha portion because I was concerned that tex1 - tex2 would result in a negative value. But, I don’t think that’s the problem - I had used unsigned values before and it gave the same answer.

The reason that I didn’t use GL_TEXTURE0_ARB is because GL_TEXTURE1_ARB is actually a dependent pixel texture that depends on TEXTURE0. I wasn’t really giving the full picture in my first post because I didn’t want too long of a post. But, here goes…

What I’m trying to do is a multi-pass algorithm that basically does a texture copy from one pass to the next so that it can use the previous results. The steps are:

  1. load a texture into TEXTURE2, and a pixel texture into TEXTURE0 and TEXTURE1
  2. Add a constant color to TEXTURE1, output to RGB component of Spare0.
  3. Store modified TEXTURE1 - TEXTURE2 + 0.5 into the alpha component of Spare0 so that mux can be used in the next combiner stage.
  4. Do mux in combiner 1 with modified TEXTURE1 and TEXTURE2 as input.
  5. Output to final combiner.

The result I’m getting is basically no mux - I get either TEXTURE1 or TEXTURE2, not a pixel by pixel mux. Strangely enough, if I use final inputs A and B with Spare1 in A and GL_ONE in B which isn’t allowed, the result is closer to what I expect - mux actually does output something.

Sorry for the very long post! Thanks for all your help.

  • Quynh.

// First Texture stored in GL_TEXTURE2_ARB
glActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_SHADER_NV);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // Clears out previous textures - no combining.
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);

glBegin(GL_QUADS);
	glNormal3f( 0.0f, 0.0f, 1.0f);
	glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0f, 0.0f); 
	glVertex3f(-1.0f, -1.0f,  1.0f);
	glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0f, 0.0f); 
	glVertex3f( 1.0f, -1.0f,  1.0f);
	glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0f, 1.0f); 
	glVertex3f( 1.0f,  1.0f,  1.0f);
	glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0f, 1.0f); 
	glVertex3f(-1.0f,  1.0f,  1.0f);
glEnd(); 
glFlush(); 

// Create and bind temp texture for accumulation.
glGenTextures(1, &temp_texture);					
glBindTexture(GL_TEXTURE_2D,temp_texture);			
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);	
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 512, 512, 0);
glFlush(); 

// Multi-pass part - for now, just do 2 outer passes and 1 inside.
for (int pass=0; pass<2; pass++) {
for (int i=2; i<3; i++) {
if (pass > 0) {
// Save texture from last pass into GL_TEXTURE2_ARB
glActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_SHADER_NV);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,temp_texture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 512, 512);
glFlush();
}

	// Texture using pixel texture
	glActiveTextureARB(GL_TEXTURE0_ARB);
	glEnable(GL_TEXTURE_SHADER_NV);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture[i]);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);   // Clears out previous textures - no combining.
	glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D); 

	glActiveTextureARB(GL_TEXTURE1_ARB);
	glEnable(GL_TEXTURE_SHADER_NV);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, temp_texture); 
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);   // Clears out previous textures - no combining.
	glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_DEPENDENT_GB_TEXTURE_2D_NV); 
	glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB);

	// Enable NV register combiners.
	glEnable (GL_REGISTER_COMBINERS_NV);
	glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 2);

	// Set constant color to "1"
	glCombinerParameterfvNV (GL_CONSTANT_COLOR0_NV, add_one_constcol);	

	// Combiner 0
	// RGB portion - adding "1" to shifted texture.
	glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);  
	glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
	glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

	// Alpha portion: texture0 - texture1 + 0.5.  To be placed in spare0_alpha for use with mux in combiner 1.
	glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_HALF_BIAS_NEGATE_NV, GL_BLUE);
	glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_BLUE);
	glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV, GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);
	glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_BLUE);
	glCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

	// Combiner 1: MUX using spare0_alpha
	glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
	glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB);
	glCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_TRUE);

	// Final Combiner
	glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glFinalCombinerInputNV(GL_VARIABLE_E_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glFinalCombinerInputNV(GL_VARIABLE_F_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
	glFinalCombinerInputNV(GL_VARIABLE_G_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

	glClearColor(0.0f, 0.0f, 0.0f, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glBegin(GL_QUADS);
		glNormal3f( 0.0f, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f); 
		glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0f, 0.0f); 
		glVertex3f(-1.0f, -1.0f,  1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f); 
		glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0f, 0.0f); 
		glVertex3f( 1.0f, -1.0f,  1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f); 
		glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0f, 1.0f); 
		glVertex3f( 1.0f,  1.0f,  1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f); 
		glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0f, 1.0f); 
		glVertex3f(-1.0f,  1.0f,  1.0f);
	glEnd(); 
    	glDisable(GL_TEXTURE_SHADER_NV);
	glDisable(GL_REGISTER_COMBINERS_NV);
	glFlush(); 

}
}

Addendum to my previous reply -

Why are you saying that the alpha portion of GL_SPARE0_NV is equal to the alpha portion of GL_TEXTURE0_ARB? I thought that I was using combiner 0 to construct the alpha portion of GL_SPARE0_NV in order to use it in combiner 1 for mux’ing.

  • Quynh.

Ah – so you’re not using TEXTURE0.a in combiner 0. Register combiners automatically copy the TEXTURE0.a to SPARE0.a before combiner 0 executes, but SPARE0.rgb is undefined. Your problem is in this line:

glCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_HALF_BIAS_NEGATE_NV, GL_BLUE);

You’re using an undefined component of SPARE0. It appears that you want to use the result of the RGB portion of combiner 0, but you can’t use that in the alpha portion of combiner 0. The two portions operate in parallel, so neither result is available until combiner stage 1 begins. You’ll probably have to add a third combiner stage.

Thanks so much for your help! You wouldn’t believe how long I’ve been mucking around with this to get it working. It’s not completely there, but VERY close! At least the problem I was having is fixed. Thanks again!

  • Quynh.