How to do OpenGL live text-rendering for a GUI?
I'm implementing a GUI built on top of OpenGL. I came to the problem that each GUI will have -- text rend开发者_JAVA百科ering. I know of several methods of rendering text in OpenGL, however, I'm wonderin which of them would be best suited for a GUI.
Generally in a GUI we have two types of text -- static and live. Static is easy enough -- we can render a TTF to a texture and forget about it. It's the "live" text that is more bothering me -- imagine console, or a live chat in a multi-player game.
I thought of several options:
- no special cases -- render and load a texture each time the text changes, keeping in mind only to rerender it when actually new text appears, and trying to split the larger text into small parts (like per chat line). However this would still leave us hanging in cases like a score line that changes all the time, or a intro text that renders "per character" (typewriter style seen in some sci-fi games)
- quad-per character -- this also seems a popular solution, you prepare a texture with the ASCII table and render a textured quad character. However, I have serious doubts about the efficiency of such a solution. Tips how to make that faster would also be welcome.
- hybrid solutions -- however I have no idea how to implement that cleanly
The question hence is -- how to render text in OpenGL efficiently?
If this helps, I'm coding in STL/Boost-heavy C++ and aiming at GForce 6 and later graphics cards.
EDIT2: Sean Barrett just released Bitmap fonts for C/C++ 3D programmers.
EDIT: another code gem that's worth a look is Font Stash which leverages Sean Barrett's stb_truetype.h.
You can create a texture in which you render all the characters of your font. Then you just draw textured quads with orthographic projection and proper texture coordinates: this approach works when your text is in a language that doesn't contain much symbols: like english. The font texture is created once at the beginning of the application and then rendering quads is really fast.
That's what I'm using and I believe it's the fastest way to render text in OpenGL. At first, I used Angelcode's Bitmap Font Generator tool and then I integrated FreeType directly and built a big texture containing all the glyphs at application launch. As for tips to improve the quads rendering speed, I just used VBO as for the rest of the geometry in my application.
I'm surprised you have doubts about quad rendering while you don't seem to worry about the performance of generating a texture on the cpu, then uploading it, then binding it to finally render a rectangle with it, that for each frame. Changing OpenGL states is the bottleneck, not the 500 - 1000 quads you'll use for text.
Also, have a look at libraries like the FTGL library who converts the whole font into polygons (geometric fonts).
Try reading this: http://dmedia.dprogramming.com/?n=Tutorials.TextRendering1.
The approach described is the typical method for text rendering with graphics APIs. One character per quad, and all of the image data for text in a single texture. If you can fit your entire character set into one texture (actually, depending on the size of the texture, you might be able to fit several) the rendering is extremely fast.
The key is that texture binding is the operation you need to minimize. If you can render all of your text with a single texture, it won't matter how much text you need to put on the screen. Even if you have to switch textures a handful of times (different fonts, different font attributes [bold, underline, etc]) performance should still be good. Text performance might be more critical for a HUD, but it less important in the GUI.
You can render the text as triangulated faces, and avoid using a texture. This way you avoid unsharp edges when the text is zoomed. I authored a low poly ASCII font that is open sourced and available at my github.
Another way to have sharp edges on your text is SDF font rendering, but this suffers from some artefacts.
freetype-gl
https://github.com/rougier/freetype-gl is a library that integrates freetype and OpenGL.
imagine console
See console: https://github.com/rougier/freetype-gl/blob/a4cfb9abac19a0ab62b625a9b6f856e032fe3732/demos/console.c
How to get it to run on Ubuntu 15.10: https://github.com/rougier/freetype-gl/issues/82#issuecomment-216025527
More details at: How to draw text using only OpenGL methods?
Urho3D ConsoleInput.cpp
https://github.com/urho3d/Urho3D/blob/6b63f20065558cff1842bc8e1e3c6ee11f4bf577/Source/Samples/26_ConsoleInput/ConsoleInput.cpp
Per quad character handled thought a display list (updated only when text changes) is quite enough for most cases.
I used it with X11 fonts using the XLoadQueryFont, glGenLists, glXUseXFont, glCallLists you have an array of display lists describing each character.
the glCallLists function accepts a char * argument for your text and it can be embedded inside a display list.
So you end up with a single call to display blocks text.
The font texture suggested by G. Pakosz is similar, but you compute your own "by character" display lists.
Your first options will be quite slower as it will require processor based rendering at each text change.
could just use these two functions:
void renderstring2d(char string[], float r, float g, float b, float x, float y)
{
glColor3f(r, g, b);
glRasterPos2f(x, y);
for(unsigned int i = 0; i < strlen(string); i++)
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, string[i]);
}
void renderstring3d(char string[], float r, float g, float b, float x, float y, float z)
{
glDisable(GL_LIGHTING);
glColor3f(r, g, b);
glRasterPos3f(x, y, z);
for(unsigned int i = 0; i < strlen(string); i++)
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, string[i]);
}
then when you render 2D text, don't forget to reset both the modelview and projection matrices.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
// Render text and quads
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
You don't need to render to a quad using these functions, because these functions render directly to the framebuffer.
And if that doesn't work for you, check this out. http://www.opengl.org/resources/faq/technical/fonts.htm
It talks about rendering text using glBitmap, glDrawPixels, and alpha blending.
It is tricky to do, especially if you want to use subpixel font rendering techniques. Have a look at the ClanLib SDK. It uses a batch renderer to render an entire screen of text in a single OpenGL call. Since it has a zlib based license, you can extract its code if you don't wish to use the SDK itself
精彩评论