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:
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.
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)