Reputation: 942
I'm currently trying to draw simple meshes using different textures (using C# and OpenTK). I read a lot about TextureUnit and bindings, and that's my current implementation (not working as expected) :
private void ApplyOpaquePass()
{
GL.UseProgram(this.shaderProgram);
GL.CullFace(CullFaceMode.Back);
while (this.opaqueNodes.Count > 0)
Draw(this.opaqueNodes.Pop());
GL.UseProgram(0);
}
And my draw method :
private void Draw(Assets.Model.Geoset geoset)
{
GL.ActiveTexture(TextureUnit.Texture1);
GL.BindTexture(TextureTarget.Texture2D, geoset.TextureId /*buffer id returned by GL.GenTextures*/ );
GL.Uniform1(GL.GetUniformLocation(this.shaderProgram, "Texture1"), 1 /*see note below*/ );
//Note: if I'm correct, it should be 1 when using TextureUnit.Texture1
// (2 for Texture2...), note that doesn't seem to work since no
// texture texture at all is sent to the shader, however a texture
// is shown when specifying any other number (0, 2, 3...)
// Draw vertices & indices buffers...
}
And my shader code (that shouldn't be the problem since uv mapping is ok):
uniform sampler2D Texture1;
void main(void)
{
gl_FragColor = texture2D(Texture1, gl_TexCoord[0].st);
}
Since geoset.TextureId can vary from one geoset to another, I'm expecting different texture to be sent to the shader.
Instead, always the same texture is applied to all objects (geosets).
Using different TextureUnit for each textures (working well), but what happens if we have 2000 different textures? If my understanding is right, we must use multiple TextureUnit only if we want to use multiple texture at the same time in the shader.
I first thought that uniforms couldn't be changed once defined, but a test with a boolean uniform told me that it was actually possible.
private void Draw(Assets.Model.Geoset geoset)
{
GL.ActiveTexture(TextureUnit.Texture1);
GL.BindTexture(TextureTarget.Texture2D, geoset.TextureId);
GL.Uniform1(GL.GetUniformLocation(this.shaderProgram, "Texture1"), 1 );
//added line...
GL.Uniform1(GL.GetUniformLocation(this.shaderProgram, "UseBaseColor"), (geoset.Material.FilterMode == Assets.Model.Material.FilterType.Blend) ? 1: 0);
// Draw vertices & indices buffers...
}
Shader code:
uniform sampler2D Texture1;
uniform bool UseBaseColor;
void main(void)
{
gl_FragColor = texture2D(Texture1, gl_TexCoord[0].st);
if (UseBaseColor)
gl_FragColor = mix(vec4(0,1,1,1), gl_FragColor , gl_FragColor .a);
}
This code works great, drawing some geoset with a base color instead of transparency, that (should ?) prove that uniforms can be changed here. Why this isn't working with my textures ?
Should I use a different shader program per geoset ?
Thanks in advance for your answers :)
Regards,
Bruce
EDIT: that's how I generate textures in the renderer:
override public uint GenTexture(Bitmap bmp)
{
uint texture;
GL.GenTextures(1, out texture);
//I disabled this line because I now bind the texture before drawing a geoset
//Anyway, uncommenting this line doesn't show a better result
//GL.BindTexture(TextureTarget.Texture2D, texture);
System.Drawing.Imaging.BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
//temp settings
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
return texture;
}
Upvotes: 1
Views: 5230
Reputation: 942
I finally solved my problem ! All the answers perfected my understanding and lead me to the solution which lied on two major problems:
1) as Calvin1602 said, this is very important to bind a newly created texture before calling glTexImage2d.
2) also UncleZeiv rose my attention about the last GL.Uniform1's parameter. The OpenTK tutorial is very misleading because the guy pass the id of the texture object to the function, that happens to work here because the order of generation of the texture exactly matches the id of used TextureUnit. As I was unsure that my comprehension was exact, I wrongly changed this parameter back to the geoset.TextureId.
Thanks !
Upvotes: 2
Reputation: 9326
You don't need multiple shader programs if the only thing you are changing is the texture. Also uniform locations are constant throughout the lifetime of a shader program, so there is no need to retrieve those each frame. However, you do need to rebind the texture each time you change it, and you will need to bind each distinct texture to a separate texture ID.
As a result, I would conclude that what you posted ought to work and so the problem is likely somewhere else in your code.
EDIT: After the updated version it should still work. However I am concerned about why the following line is commented out:
//GL.BindTexture(TextureTarget.Texture2D, texture);
This should be in there. Otherwise you will keep over writing the same texture (which is ridiculous). You need to bind the texture before you initialize. Now it is entirely conceivable that something else is broken, but given what I see now this is the only error that jumps out at me.
Upvotes: 0