Search code examples
c#vb.netopenglglslopentk

OpenTK - GLSL - BufferData for texture coordinates stored separately


I was trying to do texturing in OpenGL 3.3 (OpenTK) in VB.NET. But I came across a weird problem resulting in the following output when trying to map textures to a Quad: Output

where the actual image is:

Original image

The Code is:

Imports OpenTK
Imports OpenTK.Graphics.OpenGL
Imports System.IO
Imports System.Drawing.Imaging

Public Class Form1



    Private vertices() As Vector3 = {New Vector3(0.5F, 0.5F, 0.0F),
                                     New Vector3(0.5F, -0.5F, 0.0F),
                                     New Vector3(-0.5F, -0.5F, 0.0F),
                                     New Vector3(-0.5F, 0.5F, 0.0F)}

    Private texcoords() As Vector2 = {New Vector2(1.0F, 1.0F),
                                     New Vector2(1.0F, 0.0F),
                                     New Vector2(0.0F, 0.0F),
                                     New Vector2(0.0F, 1.0F)}

    Private indices() As UInteger = {0, 1, 3,
                                    1, 2, 3}

    Private VBO, VAO, EBO As Integer

    Private vert_shader_source, frag_shader_source As String
    Private vertex_shader, fragment_shader, shader_program As Integer

    '--------------
    Private texture As Integer
    Private texture_bmp As New Bitmap("Textures/tex4.jpg")
    Private TBO As Integer


    Private Sub load_texture(ByRef tex_bmp As Bitmap, ByRef texture_index As Integer)

        GL.Enable(EnableCap.Texture2D)

        GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest)

        GL.GenTextures(1, texture_index)
        GL.BindTexture(TextureTarget.Texture2D, texture_index)
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, CInt(TextureMinFilter.Linear))
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, CInt(TextureMagFilter.Linear))

        Dim data As BitmapData = tex_bmp.LockBits(New System.Drawing.Rectangle(0, 0, tex_bmp.Width, tex_bmp.Height), 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)

        tex_bmp.UnlockBits(data)

    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        vert_shader_source = openShader("Shaders/vertex.glsl")
        frag_shader_source = openShader("Shaders/fragment.glsl")

        createShaders()
        create_handles()

        createVAO()

        '---------------
        load_texture(texture_bmp, texture)

    End Sub

    Sub create_handles()

        VAO = GL.GenVertexArray()
        VBO = GL.GenBuffer()
        EBO = GL.GenBuffer()

        '-------------
        TBO = GL.GenBuffer()

    End Sub

    Private Sub GlControl1_Load(sender As Object, e As EventArgs) Handles GlControl1.Load

    End Sub

    Sub copy_to_gpu()

        GL.BindBuffer(BufferTarget.ArrayBuffer, VBO)
        GL.BufferData(Of Vector3)(BufferTarget.ArrayBuffer, New IntPtr(vertices.Length * Vector3.SizeInBytes),
                                  vertices, BufferUsageHint.StaticDraw)

        GL.BindBuffer(BufferTarget.ElementArrayBuffer, EBO)
        GL.BufferData(BufferTarget.ElementArrayBuffer, New IntPtr(indices.Length * Len(New UInteger)),
                                  indices, BufferUsageHint.StaticDraw)


        '-----------
        GL.BindBuffer(BufferTarget.TextureBuffer, TBO)
        GL.BufferData(Of Vector2)(BufferTarget.TextureBuffer, New IntPtr(texcoords.Length * Vector2.SizeInBytes),
                                  texcoords, BufferUsageHint.StaticDraw)

    End Sub

    Sub link_vertex_attribs()

        GL.EnableVertexAttribArray(0)
        GL.BindBuffer(BufferTarget.ArrayBuffer, VBO)
        GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, False, Vector3.SizeInBytes, 0)

        '-------------
        GL.EnableVertexAttribArray(1)
        GL.BindBuffer(BufferTarget.TextureBuffer, TBO)
        GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, False, Vector3.SizeInBytes, 0)

    End Sub


    Sub createVAO()

        GL.BindVertexArray(VAO)

        copy_to_gpu()
        link_vertex_attribs()

        GL.BindBuffer(BufferTarget.ArrayBuffer, 0)
        GL.BindVertexArray(0)

    End Sub

    Sub createShaders()

        'vertex shader
        vertex_shader = GL.CreateShader(ShaderType.VertexShader)
        GL.ShaderSource(vertex_shader, vert_shader_source)
        GL.CompileShader(vertex_shader)

        'fragment shader
        fragment_shader = GL.CreateShader(ShaderType.FragmentShader)
        GL.ShaderSource(fragment_shader, frag_shader_source)
        GL.CompileShader(fragment_shader)

        'create shader program
        shader_program = GL.CreateProgram()


        'attach shader program
        GL.AttachShader(shader_program, vertex_shader)
        GL.AttachShader(shader_program, fragment_shader)


        'link shader program
        GL.LinkProgram(shader_program)


        GL.DeleteShader(vertex_shader)
        GL.DeleteShader(fragment_shader)

    End Sub

    Function openShader(location As String) As String

        Dim ret As String = ""

        Try
            ' Open the file using a stream reader.
            Using sr As New StreamReader(location)
                Dim line As String
                ' Read the stream to a string and write the string to the console.
                line = sr.ReadToEnd()

                ret &= line
            End Using
        Catch e As Exception
            MsgBox("Error Opening shader", MsgBoxStyle.Critical, "Error")
            Me.Close()
        End Try

        Return ret

    End Function

    Private Sub GlControl1_Paint(sender As Object, e As PaintEventArgs) Handles GlControl1.Paint

        GL.ClearColor(0.2F, 0.3F, 0.3F, 1.0F)
        GL.Clear(ClearBufferMask.ColorBufferBit)

        GL.UseProgram(shader_program)

        ''''''''

        GL.ActiveTexture(TextureUnit.Texture0)
        GL.BindTexture(TextureTarget.Texture2D, texture)
        GL.Uniform1(GL.GetUniformLocation(shader_program, "ourTexture"), 0)

        ''''''''

        GL.BindVertexArray(VAO)

        GL.DrawElements(BeginMode.Triangles, 6, DrawElementsType.UnsignedInt, 0)
        GL.BindVertexArray(0)

        GlControl1.SwapBuffers()


    End Sub

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing

        GL.DeleteVertexArray(VAO)
        GL.DeleteBuffer(VBO)
        GL.DeleteBuffer(EBO)

        GL.DeleteTexture(texture)

    End Sub
End Class

I am storing the vertex data and texture coordinates seperately and is trying to buffer it the GPU separately using BufferData.

The problem seems to be that the BufferData isn't working properly. Because whatever values I assign to the texture coordinates, the output is unaffected!

Here is the vertex shader:

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(position.x, position.y, position.z, 1.0);

    TexCoord = texCoord;
}

Fragment Shader:

#version 330 core

out vec4 color;

in vec2 TexCoord;
uniform sampler2D ourTexture;

void main()
{
    color = texture(ourTexture, TexCoord);
}

However, If I tweak the fragment shader into this:

color = texture(ourTexture, vec2(TexCoord.x-1.0f,1.0f-TexCoord.y));

It works.. But the texture coordinates does not seem to affect the output.


Solution

  • There are several things going wrong here

    1) A buffer that stores texture coordinates is not a texture buffer. A texture buffer is used to handle data for a texture, not for storing texture coordinates. Texture coordinates are attributes, similar to positions, colors, etc. and have to be stored in a GL_ARRAY_BUFFER.

    2) Since texture buffers cannot be used as input for attributes, the second call to VertexAttribPointer still uses the position vbo, which gives you exactly the outcome you have (texture coordinates ranging from -0.5 to 0.5). Bind the texture coordinate vbo here also to GL_ARRAY_BUFFER.

    3) When uploading data you use 2 dimensional vectors, but in the VertexAttribPointer call, you tell OpenGL that your data consists of 3d vectors. The correct code would be:

    GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, False, Vector2.SizeInBytes, 0)