OpenGL Programming/Modern OpenGL Tutorial Text Rendering 02



= Introduction =

In the first text rendering tutorial, we were uploading a new texture to the graphics card for each character that we drew. This is of course very wasteful. It would be much better to have all possible character images stored permanently on the graphics card. A simple way to do this is to have many textures, one for each character, so that we just have to switch between textures when drawing a quad. However, this means we can only draw a quad at a time, and if we have a lot of text that means a lot of OpenGL calls. A slightly more complex way is to have a single, large texture that contains all the characters, and to properly set the texture coordinates for each quad so that the right character will be rendered. This is also known as a texture atlas.

= Creating a texture atlas =

A texture atlas is basically a big texture which contains many small images that are packed together. If all the subimages have the same size, it is rather easy to create a tightly packed atlas. However, we have already seen that the glyph images that FreeType produces have wildly varying sizes. This is true even when using monospaced fonts! Although there are various methods to efficiently pack a set of arbitrarily sized rectangles, we will use a very simple method in this tutorial: we will put all characters next to each other in a single row.

Before we can create the atlas itself though, we need to know the combined width of all the glyph images, and the height of the tallest glyph. Assuming we have already initialized FreeType, have loaded a font, and set the font size, it is a simple matter:

Remember that the variable "g" is just a shortcut to save some typing, and in that spirit, we also used the  function, so you should put   at the top of the source. We skip the first 32 ASCII characters, since they are just control codes.

Now that we know the width and height of our atlas, we can create an empty texture for it:

We used a null pointer to tell OpenGL that we will fill in the contents of the texture later. Again, don't forget about the GL_UNPACK_ALIGNMENT. Now we are ready to paste the glyph images into the texture atlas. We will use the very convenient  function for this:

That's it, our texture atlas is finished... except that we should try to remember where in the atlas we can find individual characters.

= Caching glyph metrics and texture offsets =

When we are building our vertex buffer, we need to know the right texture coordinates to use. Most importantly, we need to know the x offset, the rest of the information can be found in FreeType's  struct. However, calling  all the time is also not so efficient, so the best thing to do is to store all the information we need to render text in our own cache. Since we only use the ASCII character set, we can just make an array containing the information for all ASCII characters:

In the for loop where we upload the glyph images to the texture, we fill in this array:

Although the struct contains only members of type float, you could use (u)int16_t or even (u)int8_t for them, depending on the maximum size of the glyphs and the atlas. Since we are dealing with ASCII, we could also have omitted the copy of the  value, since it should be 0 anyway. If you want to go beyond ASCII though, it is best not to make any assumptions.

= Rendering lines of text using the atlas =

Now that we really have all the information we need, we can build a vertex buffer that contains all the information necessary to render a complete line of text. Let's adapt the  function from the previous tutorial to use the texture atlas:

First we define a struct to hold the vertex and texture coordinates for each point, to make it slightly easier to write the code to fill in all the values. We need 6 points for each character we will render, since we have to use GL_TRIANGLES instead of a GL_TRIANGLE_STRIP to be able to render separate quads. In the loop, we perform the same calculations as before, but using the cached glyph metrics. We add an optimisation to skip drawing glyphs that have zero width and/or height, such as the space character. After the loop has filled in our vertex buffer, we just need to upload it to the graphics card, and tell it to render all the triangles.

Exercises

 * Make a  that holds the texture and cache for a given font and font size, so that you do not have any global variables anymore. Make it so you can pass a pointer to such as struct to.
 * If you draw misaligned text, you should see much more rendering artifacts than in the previous tutorial. Can you explain why this happens? How would you prevent this from happening?
 * Use an index buffer object so that you only need to have 4 points per character in the vertex buffer. Is this a worthwhile optimization?
 * Since we are dealing with pixel-aligned coordinates, we don't really need floating point coordinates. OpenGL also supports 16 bit integer coordinates (GL_SHORT). Convert the glyph metrics cache and vertex buffer to use that instead, and change the shaders as necessary.
 * If you have a VBO with GL_SHORT coordinates, is it still worthwhile to use an IBO?
 * The texture atlas techniques also makes it easy to cache the VBO (and IBO), so that you don't have to recalculate anything if the text doesn't change. It is also very common that only the last part of the text changes. How could you optimize this?

Troubleshooting
If you are having trouble getting it to render correctly, go through this list.


 * In newer versions of OpenGL the value GL_ALPHA is deprecated for use in the function glTexImage2D. Replace it with GL_RED and modify the shader to read the red channel to fix this. See the glTexImage2D documentation for list of valid values.