Sign in to follow this  
CyberSlag5k

GLSL Multitexturing problems

Recommended Posts

I'm having a trouble getting multitexturing to work under GLSL. I've never done it using fixed functionality, so perhaps I lack the knowledge to know what it is I'm doing wrong, but from what I've read I think I'm doing it properly. Here is the code which sets up my shader program and (for the time being) passes in the multitexturing variables:
void Shader::CreateProgram()
{
	m_Program = glCreateProgram();

	CreateVertexShader();
	CreateFragmentShader();

	glLinkProgram(m_Program);

	glUseProgram(m_Program);

	m_Texture0 = new Texture("Data\\Textures\\grass.bmp");

	m_Texture0->Bind(0);
	
	int texture0Location = glGetUniformLocation(m_Program, "texture0");

	glUniform1i(texture0Location, 0);

	m_Texture1 = new Texture("Data\\Textures\\rock.bmp");

	m_Texture1->Bind(1);
	
	int texture1Location = glGetUniformLocation(m_Program, "texture1");

	glUniform1i(texture1Location, 1);
}


And Texture::Bind looks like this:
void Texture::Bind(const int& textureIndex) const
{
	glActiveTexture(GL_TEXTURE0 + textureIndex);

	if(glIsTexture(m_ID))
		glBindTexture(GL_TEXTURE_2D, m_ID);
}


So I'm setting the active texture unit, binding my texture object (by texture ID) to that active unit, and passing in the texture unit number as a uniform to my shaders, which look like this: Vertex (the 3rd and 4th lines from the bottom should be the only relevant ones):
void main()
{
	vec3 eyePos = vec3(gl_ModelViewMatrix * gl_Vertex);
	vec3 normalEyeSpace = normalize(gl_NormalMatrix * gl_Normal);
	vec3 lightDirection = normalize(gl_LightSource[0].position.xyz - eyePos);
	float reflectionAngle = max(dot(normalEyeSpace, lightDirection), 0.0);
	
	vec4 diffuseIntensity = reflectionAngle * 
								gl_FrontMaterial.diffuse *
									gl_LightSource[0].diffuse;
	
	vec4 ambient = gl_FrontMaterial.ambient *
					(gl_LightSource[0].ambient + gl_LightModel.ambient);
	
	
	gl_TexCoord[0] = gl_MultiTexCoord0;
	gl_TexCoord[1] = gl_MultiTexCoord1;
	gl_FrontColor = gl_Color * diffuseIntensity + ambient;
	gl_Position = ftransform();
}


Fragment:
uniform sampler2D texture0;
uniform sampler2D texture1;

void main()
{
	vec4 fragColor = texture2D(texture0, gl_TexCoord[0].st);
	gl_FragColor = fragColor * gl_Color;
}


You'll notice that at the moment I'm only dealing with a single texture at a time (I wanted to get that up and running before I started blending textures). However, both of the two following lines of shader code: vec4 fragColor = texture2D(texture0, gl_TexCoord[0].st); vec4 fragColor = texture2D(texture1, gl_TexCoord[1].st); use the second texture (the rock). The way I've set it up, I'd expect the first line to use the grass and the second to use the rock. I'm not sure if it's important, but here's a sample of how I bind the textures to a vertex:
		glNormal3f(-m_Faces[i].A->normal.GetX(), m_Faces[i].A->normal.GetY(), m_Faces[i].A->normal.GetZ());
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f(m_Faces[i].A->pos.GetX(), m_Faces[i].A->pos.GetY(), m_Faces[i].A->pos.GetZ());


Can anyone see what it is that I'm doing wrong? From what I've read, that should be it, or at least close. But not matter what I do, that second texture always gets used. Thanks in advance.

Share this post


Link to post
Share on other sites
A couple other things to mention:

I have checked to see if multitexturing is supported, and it is.

I also tried being rather blunt about which texture gets bound upon the time of the textures' creation:


glActiveTexture(GL_TEXTURE0);
m_Texture0 = new Texture("Data\\Textures\\grass.bmp");

m_Texture0->Bind(0);

int texture0Location = glGetUniformLocation(m_Program, "texture0");

glUniform1i(texture0Location, 0);

glActiveTexture(GL_TEXTURE1);
m_Texture1 = new Texture("Data\\Textures\\rock.bmp");




To no avail. I think I'll pass in the texture unit as a parameter of the texture constructor in the future. Right now, the texture just gets bound to the active unit (which I thought might be the problem, but the above rules that out).

Share this post


Link to post
Share on other sites
Do you have VBO or VA setup? Or are you using FFP code? If FFP you need to have the texture coordinates sent in to glMultiTexCoord2fARB() correctly.

e.g.
glMultiTexCoord2fARB();glVertex3f();//corner 0
glMultiTexCoord2fARB();glVertex3f();//corner 1
glMultiTexCoord2fARB();glVertex3f();//corner 2
glMultiTexCoord2fARB();glVertex3f();//corner 3

hope that explains it well enough.

Share this post


Link to post
Share on other sites
Most excellent. Things appear to be working now, thank you very much!

I have a question, though. Can I bind only bind each texture to a specific texture unit? For example, if I create my grass texture as follows:


glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &m_ID);
glBindTexture(GL_TEXTURE_2D, m_ID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_Bitmap.GetWidth(), m_Bitmap.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);



Can I only bind it to texture unit GL_TEXTURE0? I am certain I can bind other textures to GL_TEXTURE0, but do I have to create an entirely new texture object if I wish to bind something to GL_TEXTURE1?

I think I'm getting the hang of this...

Thanks again!

Share this post


Link to post
Share on other sites
I didn't get what you meant exactly.

If you want to blend 2 textures,


Make a texture unit active (say unit n; n < maximum texture units supported)

Bind the corresponding texture object

Make another texture unit active (better to use n+1; n+1 < maximum texture units supported)

Bind the corresponding texture object


I think GL will not give you any error if you bind same texture object on to different texture units. But doing so doesn't make any sense.

Share this post


Link to post
Share on other sites
Well the idea was to be able to bind one texture to an arbitrary texture unit. Not necessarily concurrently, but not being restricted to which unit to bind to. Your suggestion to remove my bind method, which switched the active texture unit, led me to think the problem was that I was attempting to switch texture units.

I guess I'm not entirely sure where the problem was in my app, now. It's working now, though. I'll try and reproduce the problem for my own understanding.

Thanks again, KumGame07!

Share this post


Link to post
Share on other sites
Sure you're able to bind each texture to any texture unit, just make the unit you want to bind the texture to the active one and bind a texture:

glActiveTexture(GL_TEXTURE<NR>);

glBindTexture(GL_TEXTURE_2D, <whatever ID>);





but if you dont want to use these texture units for lets say the next model you draw, you need to call this fo each active texture unit:

glActiveTexture(GL_TEXTURE<NR>);

glBindTexture(GL_TEXTURE_2D, 0); //0 tells OpenGL not to use any texture here





Only the units you want to use have to have a texture bound to it.

And if that's what your problem was, it doesnt matter which texture unit was active when you generated the texture!

#EDIT: And the problem was your Bind-method, because it boud a texture to the unit with the same number as the texture id. This does not make sense as texture IDs are generated when creating a new texture. So if you have 100 textures you'll probably have ids in that range too (althoguh I dont know whether OpenGL just increases this id for textures..). The problem is that some graphic cards only support 4/8/16 texture units. So you cant bind to texture unit 67. With texture units you can tell openGL to use several textures at the same time, no matter which ids they have.

[Edited by - Caste on June 6, 2007 2:26:43 AM]

Share this post


Link to post
Share on other sites
Thank you for your response, Caste.

Quote:

#EDIT: And the problem was your Bind-method, because it boud a texture to the unit with the same number as the texture id.


I don't quite follow this. This is your suggested bind:


glActiveTexture(GL_TEXTURE<NR>);

glBindTexture(GL_TEXTURE_2D, <whatever ID>);



And this is mine:


void Texture::Bind(const int& textureIndex) const
{
glActiveTexture(GL_TEXTURE0 + textureIndex);

if(glIsTexture(m_ID))
glBindTexture(GL_TEXTURE_2D, m_ID);
}



In both, the active texture unit is set to some specified offset of GL_TEXTURE0. In both, the texture ID is bound to that active texture. I don't see the difference...

Share this post


Link to post
Share on other sites
why don't you just pass in the GL defined type for the texture unit? Seems much cleaner and simpler. Less error prone.


void Texture::Bind(unsigned int textureIndex)
{
glActiveTexture(textureIndex);
if(glIsTexture(m_ID))
glBindTexture(GL_TEXTURE_2D, m_ID);
}

//now

foo.Bind(GL_TEXTURE0);
foo.Bind(GL_TEXTURE1);
//ect...



Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this