Yuckfu Dev Diary #5 – OpenGL, Fonts and You
After my last post I read a bit more about displaying text with OpenGL. One of the more popular solutions is to have a single texture containing all (ASCII) characters of a font. As it turned out, there are several applications to build such a texture for you. Bitmap Font Builder is one of them. It looks a bit clunky, but gets the job done nicely – and it’s free!
Here’s a quick test I did with the Helvetica typeface. I exported the texture as a transparent PNG file, cropped it and added a simple drop shadow effect in Photoshop. For now, I only used the upper case letters. I’ll probably come back later and export the full set of Latin-1 characters.
Bitmap Font Builder can also export an INI file with the pixel width of each character. I used a simple Regular Expression to convert this into a comma separated list, so I can use it directly in C.
const unsigned char charWidthMap[] = {
// ! " # $ % & ' ( ) * + , - . /
3,4,6,6,6,10,8,3,4,4,5,8,3,5,3,4,
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
6,6,6,6,6,6,6,6,6,6,3,3,8,8,8,6,
// @ A B C D E F G H I J K L M N O
10,7,7,7,7,6,6,7,7,3,6,7,6,10,7,7,
// P Q R S T U V W X Y Z
7,7,7,7,6,7,7,10,7,7,6
};
To prepare displaying text on top of my 3D scene, I push the current projection matrix to the stack, load an orthographic (2D) view matrix and disable depth testing and lighting. I can then render my previously computed arrays of vertices and texture coordinates for a particular string in screen space. The vertex coordinates for each character are computed based on the previous characters width.
int offset = 0;
for( int i = 0; text[i] != 0; i++ ) {
vertices[0].x = offset; // top left
vertices[0].y = 0;
…
vertices[3].x = offset + charSizeInTexture.x; // bottom right
vertices[3].y = charSizeInTexture.y;
// Increase the offset for the next character.
// My charWidthMap starts at ASCII character 32 (space)
offset += charWidthMap[ text[i] - 32 ];
}
The normalized texture coordinates for each character are computed based on its position in the texture. E.g. for the top left corner:
int x = ((text[i] - 32) % charsPerRow) * charSizeInTexture.x;
int y = ((text[i] - 32) / charsPerRow) * charSizeInTexture.y;
texCoords[0].x = (float)x / textureSize.x;
texCoords[0].y = (float)y / textureSize.y;
The Result:
As you can see, the result looks pretty good. I’ve got a proportional, anti aliased font with a nice shadow effect that blends in perfectly with the 3D scene. Still, no kerning (look at the spacing between the W and A in “WAR”), but I can live with that – after all, it’s an arcade game not an E-Book reader.
Update: Bitmap Font Builder is only free for non-commercial projects, but there are alternatives. On the other hand, with the current dollar course, I could also spend some 20 bucks.
Update 2: But then again, why pay for something that can be easily implemented with Javascript/HTML?