GLSL改变每个对象的均匀纹理

时间:2011-06-27 13:57:20

标签: opengl glsl textures

我目前正在尝试使用不同的纹理绘制简单的网格(使用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;
}

2 个答案:

答案 0 :(得分:2)

我终于解决了我的问题! 所有答案都完善了我的理解,并引导我解决了两个主要问题:

1)正如Calvin1602所说,在调用glTexImage2d之前绑定新创建的纹理非常重要。

2)UncleZeiv也引起了我对最后一个GL.Uniform1参数的注意。 OpenTK教程非常具有误导性,因为该人将纹理对象的id传递给函数,这恰好在这里起作用,因为纹理的生成顺序与使用的TextureUnit的id完全匹配。 由于我不确定我的理解是否准确,我错误地将此参数更改回geoset.TextureId。

谢谢!

答案 1 :(得分:0)

如果您要更改的唯一内容是纹理,则不需要多个着色器程序。在着色器程序的整个生命周期中,统一位置也是恒定的,因此不需要检索每个帧。但是,每次更改纹理时都需要重新绑定纹理,并且需要将每个不同的纹理绑定到单独的纹理ID。

因此,我会得出结论,您发布的内容应该起作用,因此问题很可能出现在代码中的其他位置。

编辑:更新后的版本应该仍然可以使用。但是我担心为什么以下行被注释掉了:

 //GL.BindTexture(TextureTarget.Texture2D, texture);

这应该在那里。否则你将继续写相同的纹理(这是荒谬的)。您需要在初始化之前绑定纹理。现在完全可以想象其他东西被打破,但鉴于我现在所看到的,这是唯一突然出现的错误。