C# OpenTK - Draw string on window

為{幸葍}努か 提交于 2020-05-09 08:38:07

问题


I've a big problem: I've an OpenTK window open where I draw textures, images, etc. I've to do a little videogame in this manner for a test and I'ld like to show text on it that shows game infos.

Actually I've been only able to open a Window form with text and it's not what I need.

Is there a manner to show text in a OpenTK window?

I can't use OpenTK 3.0, so QuickFont has to be excluded.

I can use GL Class.

Thank you very much!


回答1:


One possibility would be to use FreeType library to load a TrueType Font to texture objects.
SharpFont provides Cross-platform FreeType bindings for C#.
The source can be found at GitHub - Robmaister/SharpFont.
(x64 SharpFont.dll and freetype6.dll from MonoGame.Dependencies)

A full example can be found at GitHub - Rabbid76/c_sharp_opengl.
The example eis based on LearnOpenGL - Text Rendering.

Load the font and glyph information for the characters and create a texture object for each character:

public struct Character
{
    public int TextureID { get; set; }
    public Vector2 Size { get; set; }
    public Vector2 Bearing { get; set; }
    public int Advance { get; set; }
}
// initialize library
Library lib = new Library();
Face face = new Face(lib, "FreeSans.ttf");
face.SetPixelSizes(0, 32);

// set 1 byte pixel alignment 
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);

// Load first 128 characters of ASCII set
for (uint c = 0; c < 128; c++)
{
    try
    {
        // load glyph
        //face.LoadGlyph(c, LoadFlags.Render, LoadTarget.Normal);
        face.LoadChar(c, LoadFlags.Render, LoadTarget.Normal);
        GlyphSlot glyph = face.Glyph;
        FTBitmap bitmap = glyph.Bitmap;

        // create glyph texture
        int texObj = GL.GenTexture();
        GL.BindTexture(TextureTarget.Texture2D, texObj);
        GL.TexImage2D(TextureTarget.Texture2D, 0,
                      PixelInternalFormat.R8, bitmap.Width, bitmap.Rows, 0,
                      PixelFormat.Red, PixelType.UnsignedByte, bitmap.Buffer);

        // set texture parameters
        GL.TextureParameter(texObj, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
        GL.TextureParameter(texObj, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
        GL.TextureParameter(texObj, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
        GL.TextureParameter(texObj, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);

        // add character
        Character ch = new Character();
        ch.TextureID = texObj;
        ch.Size = new Vector2(bitmap.Width, bitmap.Rows);
        ch.Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop);
        ch.Advance = (int)glyph.Advance.X.Value;
        _characters.Add(c, ch);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

Create a Vertex Array Object which draws a quad by 2 trinagles:

// bind default texture
GL.BindTexture(TextureTarget.Texture2D, 0);

// set default (4 byte) pixel alignment 
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);

float[] vquad =
{
// x      y      u     v    
    0.0f, -1.0f,   0.0f, 0.0f,
    0.0f,  0.0f,   0.0f, 1.0f,
    1.0f,  0.0f,   1.0f, 1.0f,
    0.0f, -1.0f,   0.0f, 0.0f,
    1.0f,  0.0f,   1.0f, 1.0f,
    1.0f, -1.0f,   1.0f, 0.0f
};

// Create [Vertex Buffer Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Buffer_Object)
_vbo = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
GL.BufferData(BufferTarget.ArrayBuffer, 4 * 6 * 4, vquad, BufferUsageHint.StaticDraw);

// [Vertex Array Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object)
_vao = GL.GenVertexArray();
GL.BindVertexArray(_vao);
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * 4, 0);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * 4, 2 * 4);

Furthermore create a method which draws a string at specified position which a given direction:

public void RenderText(string text, float x, float y, float scale, Vector2 dir)
{
    GL.ActiveTexture(TextureUnit.Texture0);
    GL.BindVertexArray(_vao);

    float angle_rad = (float)Math.Atan2(dir.Y, dir.X);
    Matrix4 rotateM = Matrix4.CreateRotationZ(angle_rad);
    Matrix4 transOriginM = Matrix4.CreateTranslation(new Vector3(x, y, 0f));

    // Iterate through all characters
    float char_x = 0.0f;
    foreach (var c in text) 
    {
        if (_characters.ContainsKey(c) == false)
            continue;
        Character ch = _characters[c];

        float w = ch.Size.X * scale;
        float h = ch.Size.Y * scale;
        float xrel = char_x + ch.Bearing.X * scale;
        float yrel = (ch.Size.Y - ch.Bearing.Y) * scale;

        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        char_x += (ch.Advance >> 6) * scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))

        Matrix4 scaleM = Matrix4.CreateScale(new Vector3(w, h, 1.0f));
        Matrix4 transRelM = Matrix4.CreateTranslation(new Vector3(xrel, yrel, 0.0f));

        Matrix4 modelM = scaleM * transRelM * rotateM * transOriginM; // OpenTK `*`-operator is reversed
        GL.UniformMatrix4(0, false, ref modelM);

        // Render glyph texture over quad
        GL.BindTexture(TextureTarget.Texture2D, ch.TextureID);

        // Render quad
        GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
    }

    GL.BindVertexArray(0);
    GL.BindTexture(TextureTarget.Texture2D, 0);
}

Vertex shader:

#version 460

layout (location = 0) in vec2 in_pos;
layout (location = 1) in vec2 in_uv;

out vec2 vUV;

layout (location = 0) uniform mat4 model;
layout (location = 1) uniform mat4 projection;

void main()
{
    vUV         = in_uv.xy;
    gl_Position = projection * model * vec4(in_pos.xy, 0.0, 1.0);
}

Fragment shader:

#version 460

in vec2 vUV;

layout (binding=0) uniform sampler2D u_texture;

  layout (location = 2) uniform vec3 textColor;

out vec4 fragColor;

void main()
{
    vec2 uv = vUV.xy;
    float text = texture(u_texture, uv).r;
    fragColor = vec4(textColor.rgb*text, text);
}

See the example:

Matrix4 projectionM = Matrix4.CreateScale(new Vector3(1f/this.Width, 1f/this.Height, 1.0f));
projectionM = Matrix4.CreateOrthographicOffCenter(0.0f, this.Width, this.Height, 0.0f, -1.0f, 1.0f);

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

GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);

text_prog.Use();
GL.UniformMatrix4(1, false, ref projectionM);

GL.Uniform3(2, new Vector3(0.5f, 0.8f, 0.2f));
font.RenderText("This is sample text", 25.0f, 50.0f, 1.2f, new Vector2(1f, 0f));

GL.Uniform3(2, new Vector3(0.3f, 0.7f, 0.9f));
font.RenderText("(C) LearnOpenGL.com", 50.0f, 200.0f, 0.9f, new Vector2(1.0f, -0.25f));


来源:https://stackoverflow.com/questions/59800470/c-sharp-opentk-draw-string-on-window

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!