Search code examples
c#opengltexturesopentk

OpenGL draws wrapped textures (OpenTK)


I have a texture:

Original texture

But OpenGL draws this:

Texture drawed by OpenGL

Can someone explain me why and help me solve this problem?

This code loads texture (i'm using ImageSharp to load image)

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

            Image<Rgba32> image = Image.Load(File.ReadAllBytes(path));

            image.Mutate(x => x.Flip(FlipMode.Vertical));

            List<byte> pixels = new List<byte>(4 * image.Width * image.Height);

            for (int y = 0; y < image.Height; y++)
            {
                var row = image.GetPixelRowSpan(y);
                
                for (int x = 0; x < image.Width; x++)
                {
                    pixels.Add(row[x].R);
                    pixels.Add(row[x].G);
                    pixels.Add(row[x].B);
                    pixels.Add(row[x].A);
                }
            }

            byte count = 1;
            foreach (byte pixel in pixels)
            {
                Console.Write(pixel + " ");
                if (count == 4)
                {
                    Console.WriteLine();
                    count = 0;
                }
                count++;
            }

            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.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba,
                PixelType.UnsignedByte, pixels.ToArray());

            GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);

And this draws (Shader and Texture classes are working, Texture constructor is shown above):

        private int VAO;
        private int VBO;
        private int EBO;

        private Shader shader;
        private Texture texture;

        float[] vertices =
        {
            //Position          Texture coordinates
             0.5f,  0.5f, 0.0f, 0.0f, 1.0f, // top right
             0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
            -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
            -0.5f,  0.5f, 0.0f, 0.0f, 1.0f  // top left
        };

        uint[] indices =
        {
            0, 1, 3,
            1, 2, 3,
        };

        VAO = GL.GenVertexArray();
        GL.BindVertexArray(VAO);

        shader.Use();
        texture.Use();

        EBO = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ElementArrayBuffer, EBO);
        GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(uint), indices, BufferUsageHint.StaticDraw);

        VBO = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
        GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);

        GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
        GL.EnableVertexAttribArray(0);

        GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
        GL.EnableVertexAttribArray(1);

        Matrix4 model = Matrix4.CreateRotationX(0);

        shader.SetMatrix4("projection", Transform.projection);
        shader.SetMatrix4("model", model);
        shader.SetMatrix4("view", Camera.viewMatrix);

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

Fragment shader

#version 330

out vec4 outputColor;

in vec2 texCoord;

uniform sampler2D texture0;

void main()
{
    outputColor = texture(texture0, texCoord);
}

Vertex shader

#version 330 core

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

uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;

void main(void)
{
    gl_Position = vec4(aPosition, 1.0) * model * view * projection;
    texCoord = aTexCoord;
}

Solution

  • Your texture is wrapped incorrectly because you are using the x and y components of the vertex coordinates for the texture coordinates.
    When a named buffer object is bound, to the ArrayBuffer,target, the last argument of VertexAttribPointer is treated as a byte offset into the buffer object's data store.

    You need to specify the offset for the texture coordinates. The offset is 3 * sizeof (float), since the attribute buffer begins with the three components of the vertex coordinates, followed by the texture coordinates:

    GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);

    GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 
        5 * sizeof(float), 3*sizeof(float));