MaxC
MaxC

Reputation: 953

What is the correct way to bind Multiple Textures on OpenTK?

I tried to implement a simple test program following the OpenTK official tutorial (here) but I can't achieve any result. Following the exact coding sequence proposed my output is just a white square. I tried several patches such as avoiding the fragment shader mix() function and what I found is that the tutorial-proposed code transferred to the fragment shader only the last loaded texture.

In particular, the proposed code lets you generate a texture and assign attributes without binding the texture, while I found some answers that suggest the binding should happen before assigning any attribute (for example here).

So I tried any combination of GL function calls, and I still don't have a working code, but some insights (my latest code below):

I run out of ideas and I can't find any resources. Thanks for helping.

Fragment Shader Source:

#version 330 core

in vec2 texCoord;
out vec4 outputColor;

uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D tex2;

void main() {
    //outputColor = mix(texture(tex0, TexCoord), texture(tex1, TexCoord), 0.2);
    outputColor = texture(tex0, texCoord);
    //outputColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Vertex Shader Source:

#version 330 core

layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec2 aTexCoord;

out vec2 texCoord;

void main(void)
{
    texCoord = aTexCoord;
    gl_Position = vec4(aPosition, 1.0);
}

Texture Class Source:

internal class Texture {

public int Handle { get; private set; }

public Texture(string sourceFile, TextureUnit unit = TextureUnit.Texture0)
{

    Image<Rgba32> image = Image.Load<Rgba32>(sourceFile);
    image.Mutate(x => x.Flip(FlipMode.Vertical));
    var pixels = new byte[4 * image.Width * image.Height];
    image.CopyPixelDataTo(pixels);

    GL.ActiveTexture(unit);

    this.Handle = GL.GenTexture();
    GL.BindTexture(TextureTarget.Texture2D, this.Handle);

    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

    GL.TexImage2D(
            TextureTarget.Texture2D,     // Texture type
            0,                           // Level of detail
            PixelInternalFormat.Rgba,    // GPU storing format
            image.Width, image.Height,   // Width, Height
            0,                           // Border of the image, legacy param
            PixelFormat.Rgba,            // Format: ImgSharp use this
            PixelType.UnsignedByte,      // Pixel type
            pixels);                     // Actual pixels
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Use()
{
    GL.BindTexture(TextureTarget.Texture2D, this.Handle);
}

//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Use(TextureUnit unit)
{
    // GL.ActiveTexture(unit);
    // GL.BindTexture(TextureTarget.Texture2D, this.Handle);
}

} // Texture Class

Shader Class Source (only relevant methods):

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Use()
{
    GL.UseProgram(program);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetInt(string name, int value)
{
    int location = GL.GetUniformLocation(program, name);
    GL.Uniform1(location, value);
}

Loading method of the game class:

    protected override void OnLoad()
    {
        GL.ClearColor(0.2f, 0.2f, 0.2f, 1.0f);

        VertexArrayObject = GL.GenVertexArray();
        VertexBufferObject = GL.GenBuffer();
        ElementBufferObject = GL.GenBuffer();

        // 1. bind Vertex Array Object
        GL.BindVertexArray(VertexArrayObject);

        // 2. copy our vertices array in a buffer for OpenGL to use
        GL.BindBuffer(BufferTarget.ArrayBuffer, VertexBufferObject);
        GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);

        // 3. then set our vertex attributes pointers
        GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
        GL.EnableVertexAttribArray(0);
        GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
        GL.EnableVertexAttribArray(1);

        // Indices to ElementBufferObject
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, ElementBufferObject);
        GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(uint), indices, BufferUsageHint.StaticDraw);

        shader = new Shader("./ShadersSrc/vshader6_textures2.glsl", "./ShadersSrc/fshader6_textures2.glsl");

        texture0 = new Texture("./Textures/src/container.jpg", TextureUnit.Texture0);
        shader.SetInt("tex0", 0);

        texture1 = new Texture("./Textures/src/awesomeface.png", TextureUnit.Texture1);
        shader.SetInt("tex1", 1);
        
        texture2 = new Texture("./Textures/src/wall.jpg", TextureUnit.Texture2);
        shader.SetInt("tex1", 2);

        base.OnLoad();
    }

Frame drawing method of the game class:

    protected override void OnRenderFrame(FrameEventArgs e)
    {
        GL.Clear(ClearBufferMask.ColorBufferBit);

        GL.BindVertexArray(VertexArrayObject);

        shader.Use();
        //texture0.Use(TextureUnit.Texture0);
        //texture1.Use(TextureUnit.Texture1);

        GL.DrawElements(PrimitiveType.Triangles, indices.Length, DrawElementsType.UnsignedInt, 0);

        Context.SwapBuffers();

        base.OnRenderFrame(e);
    }

Upvotes: 2

Views: 541

Answers (1)

Rabbid76
Rabbid76

Reputation: 211239

GL.Uniform1() sets the value of a uniform variable in the standard uniform block of the current program object. Therefore, you must install the program with GL.UseProgram before you can set a uniform:

shader.Use();
shader.SetInt("tex0", 0);
shader.SetInt("tex1", 1);

Upvotes: 2

Related Questions