How to render text using OpenGL - jeske/opentk GitHub Wiki
One simple way to render text is to rasterize the text on the CPU using a drawing library, and then ask OpenGL to render the bitmap as a quad texture.
We will cover doing this with System.Drawing, which only reliably works on Windows. This method can be easily generalized to other text or graphics drawing libraries, like Pango#.
This approach has three steps:
- Use Graphics.DrawString to render text to a Bitmap.
- Upload the Bitmap to an OpenGL texture.
- Render the OpenGL texture as a fullscreen quad.
This approach is extremely efficient for text that changes infrequently, because only step 3 has to be performed every frame. Additionally, dynamic text can be reasonably efficient as long as care is taken to update only regions that are actually modified.
The downside of this approach is that (a) rendering is constrained by the capabilities of your drawing API and (b) it only supports planar 2d text. Moreover, care should be taken to recreate the Bitmap and OpenGL texture whenever the parent window changes size.
Sample code:
using System.Drawing;
using OpenTK.Graphics.OpenGL;
Bitmap text_bmp;
int text_texture;
window.OnLoad += (sender, e) => {
// Create Bitmap and OpenGL texture
text_bmp = new Bitmap(ClientSize.Width, ClientSize.Height); // match window size
text_texture = GL.GenTexture();
GL.BindTexture(text_texture);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, intAll.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, intAll.Linear);
// just allocate memory, so we can update efficiently using TexSubImage2D
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, text_bmp.Width, text_bmp.Height, 0,
PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
};
window.Resize += (sender, e) => {
// Ensure Bitmap and texture match window size
text_bmp.Dispose();
text_bmp = new Bitmap(ClientSize.Width, ClientSize.Height);
GL.BindTexture(text_texture);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, text_bmp.Width, text_bmp.Height,
PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
};
// Render text using System.Drawing.
// Do this only when text changes.
using Graphics gfx = Graphics.FromImage(text_bmp) {
gfx.Clear(Color.Transparent);
gfx.DrawString...; // Draw as many strings as you need
}
// Upload the Bitmap to OpenGL.
// Do this only when text changes.
BitmapData data = text_bmp.LockBits(
new Rectangle(0, 0, text_bmp.Width, text_bmp.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
Width, Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0;
text_bmp.UnlockBits(data);
// Finally, render using a quad.
// Do this every frame.
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(0, Width, Height, 0, -1, 1);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDst.OneMinusSourceAlpha);
GL.Begin(BeginMode.Quads);
GL.TexCoord(0f, 1f); GL.Vertex2(0f, 0f);
GL.TexCoord(1f, 1f); GL.Vertex2(1f, 0f);
GL.TexCoord(1f, 0f); GL.Vertex2(1f, 1f);
GL.TexCoord(0f, 0f); GL.Vertex2(0f, 1f);
GL.End();