C# OpenTK - 在 window 上绘制字符串
C# OpenTK - Draw string on window
我有一个大问题:我有一个 OpenTK window 打开,我可以在其中绘制纹理、图像等。我必须以这种方式做一个小视频游戏来进行测试,我会喜欢在上面显示显示游戏信息的文字。
实际上我只能打开一个 Window 带有文本的表单,这不是我需要的。
有没有办法在 OpenTK 中显示文本 window?
我不能用OpenTK 3.0,所以QuickFont不得不排除
我可以使用 GL Class。
非常感谢!
一种可能是使用 FreeType 库将 TrueType 字体加载到纹理对象。
SharpFont 为 C# 提供跨平台 FreeType 绑定。
可以在 GitHub - Robmaister/SharpFont.
找到源代码
(来自 MonoGame.Dependencies 的 x64 SharpFont.dll 和 freetype6.dll)
可以在 GitHub - Rabbid76/c_sharp_opengl 找到完整的示例。
示例 e 是基于 LearnOpenGL - Text Rendering.
加载字符的字体和字形信息并为每个字符创建一个纹理对象:
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);
}
}
创建一个 Vertex Array Object 用 2 个三角形绘制四边形:
// 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);
进一步创建一个在给定方向的指定位置绘制字符串的方法:
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);
}
顶点着色器:
#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);
}
片段着色器:
#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);
}
看例子:
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));
我有一个大问题:我有一个 OpenTK window 打开,我可以在其中绘制纹理、图像等。我必须以这种方式做一个小视频游戏来进行测试,我会喜欢在上面显示显示游戏信息的文字。
实际上我只能打开一个 Window 带有文本的表单,这不是我需要的。
有没有办法在 OpenTK 中显示文本 window?
我不能用OpenTK 3.0,所以QuickFont不得不排除
我可以使用 GL Class。
非常感谢!
一种可能是使用 FreeType 库将 TrueType 字体加载到纹理对象。
SharpFont 为 C# 提供跨平台 FreeType 绑定。
可以在 GitHub - Robmaister/SharpFont.
找到源代码
(来自 MonoGame.Dependencies 的 x64 SharpFont.dll 和 freetype6.dll)
可以在 GitHub - Rabbid76/c_sharp_opengl 找到完整的示例。
示例 e 是基于 LearnOpenGL - Text Rendering.
加载字符的字体和字形信息并为每个字符创建一个纹理对象:
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);
}
}
创建一个 Vertex Array Object 用 2 个三角形绘制四边形:
// 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);
进一步创建一个在给定方向的指定位置绘制字符串的方法:
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);
}
顶点着色器:
#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);
}
片段着色器:
#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);
}
看例子:
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));