我目前正在尝试使用不同的纹理绘制简单的网格(使用C#和OpenTK)。我读了很多关于TextureUnit和绑定的内容,这是我当前的实现(没有按预期工作):
private void ApplyOpaquePass()
{
GL.UseProgram(this.shaderProgram);
GL.CullFace(CullFaceMode.Back);
while (this.opaqueNodes.Count > 0)
Draw(this.opaqueNodes.Pop());
GL.UseProgram(0);
}
我的画法:
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...
}
我的着色器代码(由于uv映射确定不应该是问题):
uniform sampler2D Texture1;
void main(void)
{
gl_FragColor = texture2D(Texture1, gl_TexCoord[0].st);
}
由于geoset.TextureId可能因地理设备而异,我希望将不同的纹理发送到着色器。
相反,始终将相同的纹理应用于所有对象(地理位置)。
为每个纹理使用不同的TextureUnit(运行良好),但如果我们有2000种不同的纹理会发生什么?如果我的理解是正确的,那么只有当我们想在着色器中同时使用多个纹理 时,我们才必须使用多个TextureUnit。
我首先想到的是,制服一旦定义就无法改变,但是布尔制服的测试告诉我它实际上是可能的。
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...
}
着色器代码:
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);
}
这段代码效果很好,用基色而不是透明度绘制一些geoset,(应该?)证明可以在这里更改制服。为什么这不适合我的纹理?
我应该为每个geoset使用不同的着色器程序吗?
提前感谢您的回答:)
此致
布鲁斯
编辑:这就是我在渲染器中生成纹理的方式:
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;
}
答案 0 :(得分:2)
我终于解决了我的问题! 所有答案都完善了我的理解,并引导我解决了两个主要问题:
1)正如Calvin1602所说,在调用glTexImage2d之前绑定新创建的纹理非常重要。
2)UncleZeiv也引起了我对最后一个GL.Uniform1参数的注意。 OpenTK教程非常具有误导性,因为该人将纹理对象的id传递给函数,这恰好在这里起作用,因为纹理的生成顺序与使用的TextureUnit的id完全匹配。 由于我不确定我的理解是否准确,我错误地将此参数更改回geoset.TextureId。
谢谢!
答案 1 :(得分:0)
如果您要更改的唯一内容是纹理,则不需要多个着色器程序。在着色器程序的整个生命周期中,统一位置也是恒定的,因此不需要检索每个帧。但是,每次更改纹理时都需要重新绑定纹理,并且需要将每个不同的纹理绑定到单独的纹理ID。
因此,我会得出结论,您发布的内容应该起作用,因此问题很可能出现在代码中的其他位置。
编辑:更新后的版本应该仍然可以使用。但是我担心为什么以下行被注释掉了:
//GL.BindTexture(TextureTarget.Texture2D, texture);
这应该在那里。否则你将继续写相同的纹理(这是荒谬的)。您需要在初始化之前绑定纹理。现在完全可以想象其他东西被打破,但鉴于我现在所看到的,这是唯一突然出现的错误。