Rendering Text using DirectX 11
One of the first things I want to do when creating code on any platform is render text into the frame buffer. This is not only useful for presentation of menus and the like, it is very useful for debugging. The latest version of DirectX doesn't have a built in mechanism for doing this. One solution is to fall back to using regular windows code for text, but I wanted a more portable and flexible solution.
Bitmapped Font using a Texture Atlas
The concept of a bit mapped font is simple. Generate a texture containing all of the characters you need to render along with some data about their texture coordinates and then at runtime generate a vertex buffer specifying the characters to render.
MudgeFont
Mudgefont is a great little tool that makes constructing bitmapped fonts very easy. It allows you to enter a string of characters and then generates a texture that contains the characters along with an xml file with the texture coordinates required to render them.
You can get MudgeFont from here: http://www.larryhastings.com/programming/mudgefont/
To render all of the characters that might be needed I wrote a little c program to generate them in sequence and entered them into the characters box. (Note the leading space.)
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
With this done you can play around with different fonts and texture resolutions until you find some that you like and then save the project file and export.
Export generates a .tga file and the .xml file containing the texture coordinates.
You can get MudgeFont from here: http://www.larryhastings.com/programming/mudgefont/
To render all of the characters that might be needed I wrote a little c program to generate them in sequence and entered them into the characters box. (Note the leading space.)
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
With this done you can play around with different fonts and texture resolutions until you find some that you like and then save the project file and export.
Export generates a .tga file and the .xml file containing the texture coordinates.
Using the XML File
The .xml file format looks like this:
<char height="37" width="31" y="0" x="0" id="32"/>
<spacing id="32" c="8" b="1" a="0"/>
<char height="37" width="31" y="0" x="2" id="33"/>
<spacing id="33" c="3" b="4" a="3"/> ....
You can see that the first character, is space, ASCII value 32. It is 32 pixels high, 31 pixels wide, and starts at 0,0
The spacing fields a,b and c are not well documented. I've found that simply using B as the gap between the current character and the next one works nicely.
Depending on your needs you could load the .xml file at run time using something like tinyxml or convert it to a binary format. I took a lazy option and wrote a python script that generates a header file from the .xml.
#pragma once
// id,w,h,x,y,c,b,a
FONTDESC timesnewroman24Desc[]={
{ 32 , 31 , 37 , 0 , 0 , 8 , 1 , 0 , },
{ 33 , 31 , 37 , 2 , 0 , 3 , 4 , 3 , }
....
};
<char height="37" width="31" y="0" x="0" id="32"/>
<spacing id="32" c="8" b="1" a="0"/>
<char height="37" width="31" y="0" x="2" id="33"/>
<spacing id="33" c="3" b="4" a="3"/> ....
You can see that the first character, is space, ASCII value 32. It is 32 pixels high, 31 pixels wide, and starts at 0,0
The spacing fields a,b and c are not well documented. I've found that simply using B as the gap between the current character and the next one works nicely.
Depending on your needs you could load the .xml file at run time using something like tinyxml or convert it to a binary format. I took a lazy option and wrote a python script that generates a header file from the .xml.
#pragma once
// id,w,h,x,y,c,b,a
FONTDESC timesnewroman24Desc[]={
{ 32 , 31 , 37 , 0 , 0 , 8 , 1 , 0 , },
{ 33 , 31 , 37 , 2 , 0 , 3 , 4 , 3 , }
....
};
Run time Texture format
The other thing you will most likely want to do is convert the texture into a more run time friendly format. I use the Nvidia DDS Utilities which be be downloaded from here : http://developer.nvidia.com/content/legacy-texture-tools
Use the command line tools to generate a dds file. Make sure you use a dds format that supports alpha blending. Also you may want to disable mip mapping. The idea is to render the font at the exact resolution is was created. This will give the crispest results.
Use the command line tools to generate a dds file. Make sure you use a dds format that supports alpha blending. Also you may want to disable mip mapping. The idea is to render the font at the exact resolution is was created. This will give the crispest results.
Rendering the Text
Most of the time when we create a vertex buffer, the data in it is fixed, we don't generally want to change them every frame. However there are few systems where this is a requirement. Debug lines and Text rendering are good examples of this usage pattern.
I implemented a print font function that simply aggregates all of the text to render for a frame and then a flush function that actually updates the vertex buffer and submits it for rendering. This way any expensive buffer locking only has to be done once. I say buffer locking because what happens if we are updating the vertex buffer whilst the GPU is rendering from it. Using UpdateSubresource allows us to update the vertex buffer safely. However there can possibly be performance issues as if there is contention between the CPU and GPU copies of the data must be made. I double buffered my buffers to ensure this cannot happen.
I use exactly the same pattern for my debug line rendering code. This stuff allows me to draw bounding boxes and normals etc.
Note that you will want to create and set an output/merger state that supports alpha blending unless you want the text displayed on a black background.
I implemented a print font function that simply aggregates all of the text to render for a frame and then a flush function that actually updates the vertex buffer and submits it for rendering. This way any expensive buffer locking only has to be done once. I say buffer locking because what happens if we are updating the vertex buffer whilst the GPU is rendering from it. Using UpdateSubresource allows us to update the vertex buffer safely. However there can possibly be performance issues as if there is contention between the CPU and GPU copies of the data must be made. I double buffered my buffers to ensure this cannot happen.
I use exactly the same pattern for my debug line rendering code. This stuff allows me to draw bounding boxes and normals etc.
Note that you will want to create and set an output/merger state that supports alpha blending unless you want the text displayed on a black background.
Results
Here you can see the results. It looks pretty nice, is portable and didn't take that much effort to add to the project