VSFontLib – Very Simple Font Library
With immediate mode gone in core OpenGL versions, so are the vast majority of font libs that worked with OpenGL. Immediate mode was terribly slow, and code wise very extensive. Vertex Buffers are clearly the way to go.
Text rendering is very useful to display information on top of a 3D world. Since I couldn’t find a solution for this problem I implemented a simple one to render bitmapped text. If there are other public libs available out there to add to our applications to perform this task, please let me know and I’ll add a link to them here.
I just needed an application that produced a texture with the characters and a file with the texture coordinates to access each individual character.
I found a solution with an “old” application called FontStudio by Michael Pote. The site (www.nitrogen.za.org) no longer exists but I was able to find a copy on the Classic Web Archive (be patient, it takes a while to load but it will get there).
FontStudio is fairly complete and provides a lot of options for character placement and text effects. It also produces an image with all the chars (and an alpha channel) as well as a XML file with the needed texture coordinates.
The VSFontLib loads these files, image (TGA) and xml, to load the font data. With VSMathLib you can load as many fonts as you want simultaneously.
After these files are processed, VSFontLib is ready to render strings. To make life easier for the programmer, VSFontLib takes care of every detail, from setting the correct perspective so that the fonts are properly drawn on the screen, building VAOs to store the triangles for each char of the sentence, and restore your old settings for the modelview and projection matrices. To handle the matrices, VSFontLib , uses another Very Simple Lib, VSMathLib.
VSFontLib also does full memory and resource (Vertex Array and Buffers) management so you don’t have to worry about it.
Requirements
VSFontLib aims at providing users with the ability to render bitmapped text in an OpenGL application using the core profile. The font library is a subclass of VSResourceLib.
As usual, in OpenGL core versions we need shaders to render the fonts. These can be pretty ordinary shaders, the only requirement is texture support.
For this lib to work properly, the math lib must be properly initialized. Considering the shaders below, we need to set the semantics of the uniforms projMatrix
and viewModelMatrix
. This can be achieved with the following code:
VSMathLib *vsml; ... vsml = VSMathLib::getInstance(); vsml->setUniformBlockName("Matrices"); vsml->setUniformName(VSMathLib::PROJECTION, "projMatrix"); vsml->setUniformName(VSMathLib::VIEW_MODEL, "viewModelMatrix");
As an example, here is a pair of vertex and fragment shaders that can do the job perfectly.
Vertex shader:
#version 330 layout (std140) uniform Matrices { mat4 projMatrix; mat4 viewModelMatrix; }; in vec3 position; in vec2 texCoord; out vec4 vertexPos; out vec2 TexCoord; void main() { TexCoord = vec2(texCoord); gl_Position = projMatrix * viewModelMatrix* vec4(position,1.0); }
And here goes the fragment shader:
#version 330 uniform sampler2D texUnit; in vec2 TexCoord; out vec4 output; void main() { output = texture2D(texUnit, TexCoord) }
In Action
To start working with VSFontLib declare a variable of type VSFontLib
such as:
VSFontLib vsfl;
Class VSFontLib
stores the information and strings for a particular font. So if you want to work with more than one font you’ll need to declare as many variables.
A font is defined by a pair of files. An image, such as the one below, that stores a set of characters (in FontStudio you can select the range of characters to include), and a XML file with the texture coordinates and font definition data. In VSFontLib it is assumed that these files have the same name and extensions tga and xml respectively, so for instance when working with font Arial size 10, the files could be arial10.tga and arial10.xml. Below is a snapshot of arial10.tga (not the actual tga though, go to here to download the font files).
To load a font into VSFontLib do as follows:
vsfl.loadFont("arial10");
Some fonts, such as Courier New, can be treated as fixed size fonts. When loading a font it is possible to specify if the font is to be used as fixed size or not. To use it as fixed size just write:
vsfl.loadFont("couriernew10"); vsfl.setFixedSize(true);
To set the semantics of material uniforms this lib uses the method from the super class VSResourceLib:
vsfl.setMaterialBlockName("Material");
OK, now everything is ready. To manage sentences, or strings, VSFL adopts an approach similar to OpenGL. First a slot is created, then it is filled with a sentence, and finally it can be rendered.
Below is an example of how it works:
unsigned int s1 = vsfl.genSentence(); vsfl.prepareSentence(s1,"this is a sentence!"); vsfl.renderSentence(10,30,s1);
The function genSentence
creates a slot for a sentence, much like the glGen*
functions of OpenGL.
prepareSentence
receives a slot index and a string and prepares the VAO for future rendering. From now on the string can be rendered multiple times calling renderSentence
. This last function receives the window coordinates (origin is top left) to render the string stored in a slot, s1
indicates the slot index.
If a sentence changes over time, the same slot can be used to prepare a new sentence. The previous VAO and atrtribute buffers are released.
For instance, suppose we want a to display the number of frames per second. So having a global variable vsfl
of type VSFontLib
, and assuming that in our main we have created a slot with genSentence
with a value aSentenceIndex
, we can write in our rendering function:
... sprintf(s,"FPS:%4.2f",fps); vsfl.prepareSentence(aSentenceIndex,s); vsfl.renderSentence(10,10,aSentenceIndex) ...
Once a sentence slot is no longer required its resources (VAO and vertex attribute buffers) can be released with:
vsfl.deleteSentence(aSentenceIndex);
And thats it!
Dependencies
VSL
- VSResourceLib – the super class
- VSMathLib to handle matrices
External
- GLEW to access OpenGL new functionality
- DevIL to load the TGA image with the drawn chars
- TinyXML to load the XML file with the texture coordinates.
Specification
To use the lib you have to include vsFontLib .h. In all the examples below it is assumed that vsfl
is an instance of VSFontLib
.
VSFontLib vsfl;
Class Initialization
bool loadFont(std::string fontName)
Load a font. This function assumes that there are two files for each font, where both share the same name, one containing the image with the chars (extension tga) and another with the texture coordinates and other information required to properly render strings (extension xml). These files were generated with FontStudio, see the main page of VSFontLib.
The parameter is the name of the files without the extension.
The return value indicates if loading was successful.
Example assuming that the font is defined by the files couriernew10.xml and couriernew10.tga, and used them as fixed size:
vsfl.loadFont(couriernew10"); vsfl.setFixedFont(true);
To use the font with optimal individual char length just do
vsfl.loadFont(couriernew10");
setFixedFont(bool isFixed)
If using a fixed size font, the spacing is set in a different form. Calling this function makes sure that the characters are properly spaced.
Sentences
Sentences are processed strings ready for rendering.
unsigned int genSentence()
VSFLFont adopts a strategy very similar to OpenGL. For each string that we want to render we must first create a slot. This function returns the index of the allocated slot. The index is required for the next steps.
Example:
GLuint aSentence = vsfl.genSentence();
void deleteSentence(unsigned int aSentence)
Frees VAO and vertex buffers allocated for the sentence.
Example:
vsfl.deleteSentence(aSentence)
void prepareSentence(unsigned int index, std::string sentence)
Receives an index for a previously allocated slot (with genSentence
) and a string. It creates a VAO with the vertex buffers containing the vertex and texture coordinates, using the attribute locations for these two attributes (see setVertexAttribLocs)
.
If the string includes a char that is not part of the font definition then that char is skipped. A newline (‘\n’) is a valid char and it causes the vertex coordinates to drop a line, where a line is the number of pixels for the font height, as defined in the xml file.
Example:
GLuint aSentence = vsfl.genSentence(); vsfl.prepareSentence(aSentence, "Display this\nand this");
void renderSentence(int x, int y, unsigned int index)
Renders the string stored in slot index
, starting at the position (x,y)
in window coordinates. The origin is the top left of the window.
This function builds projection and modelview matrices that provide a 1:1 mapping between pixels in the window and pixels in the texture that contains the font chars. Rendering is performed with the VAO previously created with prepareSentence
. Users must call glUseProgram
with the program shader intended for string rendering sometime prior to call the renderSentence
function.
Example assuming that p
is the program shader used to render the string:
glUseProgram(p); vsfl.render(10,10,aSentence);
void renderAndDiscard(int x, int y, std::string aString)
This function is a shortcut for when a string is rendered only once. Calling
vsfl.renderAndDiscard(10,10,"Display this");
is equivalent to:
unsigned int s = vsfl.genSentence(); vsfl.prepareSentence(s, "Display this"); vsfl.renderSentence(10,10,s); vsfl.deleteSentence(s);
Thanks for providing this tutorial. Can this text be rotated?
check this font library
http://digestingduck.blogspot.com/2009/08/font-stash.html
What’s with the downloads? I can’t find a source repo and the zip doesn’t contain the header file…
Fixed the zip. Thanks.
Good job! Some minor things:
Don’t call glEnable(GL_TEXTURE_2D), this is only needed when no shader is active.
vsfl.h should also include , as std::vector is used.
should be included before “vsfl.h”, as std::string is used in vsfl.h. A god idea is to always include system files before local files.
Hi Lars,
The glEnable thing … old habits die hard 🙂
As for the includes you’re right. I forgot to include both vector and string in vsfl.h. Windows is very relaxed regarding includes …
Thanks!
You should take a look at the Angelcode Bitmap Font Generator, http://www.angelcode.com/products/bmfont/
I’ve seen it used a lot more often than Font Studio. That said, you could likely whip together your own bitmap font generator that works way better than either of those solutions, as they’re both fairly deficient in certain important ways. And yours could be cross-platform and open, plzkthxbai 😉
Hi Sean,
Thanks for the tip. Angelcode’s solution seems very similar to FontStudio regarding both results and options. FontStudio is no longer being maintained, and its almost gone (except from the classic-web-archive) so probably that’s why you see Angelcode more often. There is a nice feature in Angelcode’s solution though, character selection. the As for VSFL, it would only require a small modification on the XML reader.
http://www.freetype.org/freetype2/docs/tutorial/step2.html
Check how FT_Glyph_To_Bitmap works!
Regards, Andreas
Hi,
As far as I could get it that’s for a single char. Is there any function to convert a set of chars to a single bitmap, or do you have to do it manually?
Best Regards,
Antonio
You do it manually by simply placing a for-loop around the call…
Yes, you can do it that way, and then copy the bitmaps to a larger image. But to do it this way I would prefer to have it as a pre-processing stage. Generating textures on the fly would put a cost on the start of the application. Pre made texture atlas, which is basically what VSFL does, is more efficient. I believe that the code could be optimised so that the burden of texture generation can be minimal, but that’s outside the scope of this site. Any volunteers?
You may want to look at FreeType, the core font render library in all Linux distros. It renders the glyph into a bitmap buffer before pasting it. Assigning that buffer to a texture is a simple procedure. Imo you should use that as a backend instead because it’s very proven to work and have a lot of nifty features such as cleartype rendering and what not.
Keep up the good work!
Hi Andreas,
I did have a look at freetype. I settled the current approach because it is simpler, at least it was simpler to me to implement. And FontStudio also has a lot of features, such as cleartype, drop shadows, etc… Freetype could give you more flexibility though because font data is generated on the fly. I used FTGL which was based on freetype, but it uses old style OpenGL.
I assume there is a way to create an image with a set of characters using Freetype? If so then using Freetype in VSFL should be very simple. If this is the case then can anyone point me to a tutorial on how to generate this image?
Best Regards,
Antonio