1
votes

I am writing an application with OpenTK and got to the point where i want to render text. From examples i patched together a version that creates a bitmap with the characters i need, using Graphics.DrawString(). That version works quite okay, but i am annoyed that it relies on GL.Begin(BeginMode.Quads) and GL.End() to render the text, which is why i want to use VAOs and VBOs from now on.

I am having a problem somewhere in my program, because i always get single colored squares, where the text should appear.

What i did so far to update my functions is the following: I create the bitmap the same as before, i don't see why the problem should lie there. After that i create a list of "Char" Objects, each creating a VBO, storing the position and texture coordinates like this:

float u_step = (float)GlyphWidth / (float)TextureWidth;
float v_step = (float)GlyphHeight / (float)TextureHeight;
float u = (float)(character % GlyphsPerLine) * u_step;
float v = (float)(character / GlyphsPerLine) * v_step;
float x = -GlyphWidth / 2, y = 0;


_vertices = new float[]{
    x/rect.Width, -GlyphHeight/rect.Height,    u,          v,
   -x/rect.Width, -GlyphHeight/rect.Height,    u + u_step, v,
   -x/rect.Width, y/rect.Height,               u + u_step, v + v_step,

    x/rect.Width, -GlyphHeight/rect.Height,    u,          v,
   -x/rect.Width, y/rect.Height,               u + u_step, v + v_step,
    x/rect.Width, y/rect.Height,               u,          v + v_step
};

_VBO = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, _VBO);
GL.BufferData<float>(BufferTarget.ArrayBuffer, (IntPtr)(sizeof(float) * _vertices.Length),
 _vertices, BufferUsageHint.DynamicDraw);

Next i generate a Texture, set texture0 as active and bind the Texture as TextureTarget.Texture2D. Then i load the bitmap to the texture doing the following:

_shader.Use();
_vertexLocation = _shader.GetAttribLocation("aPosition");
_texCoordLocation = _shader.GetAttribLocation("aTexCoord");

_fontTextureID = GL.GenTexture();

GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, _fontTextureID);

using (var image = new Bitmap("container.png")) //_fontBitmapFilename))// 
{
    var data = image.LockBits(
        new Rectangle(0, 0, image.Width, image.Height),
        ImageLockMode.ReadOnly,
        System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    GL.TexImage2D(TextureTarget.Texture2D,
        0,
        PixelInternalFormat.Rgba,
        image.Width,
        image.Height,
        0,
        OpenTK.Graphics.OpenGL.PixelFormat.Bgra,
        PixelType.UnsignedByte,
        data.Scan0);
}

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

I create a VAO now, bind it, and bind one VBO to it. Then i set up the VertexAttribPointers to interpret the VBO Data:

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

GL.BindBuffer(BufferTarget.ArrayBuffer, _charSheet[87].VBO);

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

GL.EnableVertexAttribArray(_texCoordLocation);

GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, _fontTextureID);
GL.VertexAttribPointer(_texCoordLocation, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 2);

GL.BindVertexArray(0);
_shader.StopUse();

My Render function starts by using the shader, binding the VAO and enabling the VertexAttribArrays. Then i bind the VBO, in this function a fixed one for testing, and reuse the VertexAttribPointer functions, so that the VAO updates its data (i might also be wrong thinking so..). At the end i draw two triangles, which makes a square, where the letter should appear.

_shader.Use();
GL.BindVertexArray(_VAO);
GL.EnableVertexAttribArray(_texCoordLocation);
GL.EnableVertexAttribArray(_vertexLocation);
GL.BindBuffer(BufferTarget.ArrayBuffer, _charSheet[87].VBO);
GL.VertexAttribPointer(_vertexLocation, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 0);

GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, _fontTextureID);
GL.VertexAttribPointer(_texCoordLocation, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 2);

GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
GL.BindVertexArray(0);
_shader.StopUse();

I do not know, where I'm doing something wrong in my program, maybe someone has a tip for me.

Vertex Shader

#version 330 core

layout(location = 0) in vec2 aPosition;

layout(location = 1) in vec2 aTexCoord;

out vec2 texCoord;

void main(void)
{
    texCoord = aTexCoord;

    gl_Position = vec4(aPosition, 0.0, 1.0);
}

Fragment Shader:

#version 330

out vec4 outputColor;

in vec2 texCoord;

uniform sampler2D texture0;

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

1 Answers

1
votes

If a named buffer object is bound, then the last parameter of GL.VertexAttribPointer is treated as byte offset to the buffer object's data store.
The offset has to be 2*sizeof(float) rather than 2:

GL.VertexAttribPointer(_texCoordLocation, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 2);

GL.VertexAttribPointer(_texCoordLocation, 2, 
    VertexAttribPointerType.Float, false, 4 * sizeof(float), 2*sizeof(float));

See glVertexAttribPointer and Vertex Specification.