Friday, April 9, 2010

Distance Field Based Rendering of AngelCode Fonts

This morning, we added support for distance field based font rendering to the BitSquid engine (from Valve's paper http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf). An example is shown below:


The top row shows the original font, below that is the original font rendered with alpha test. The third row is a distance field representation of the font and the final row shows the distance field representation rendered with alpha test. Note that the distance field version gives better quality in the diagonal lines.

(Note: The last row looks thicker than the second row, because it was generated from a large font size and scaled down, while the second row was generated from a small font size. Because of true type font hinting at small sizes, the result is different. The last row gives a truer representation of the "actual" thickness of the font.)

A quick Google search didn't show any good tools for generating distance field font maps, so I decided to write my own. We use the excellent AngelCode Bitmap Font Generator (http://www.angelcode.com/products/bmfont/) to generate our font maps, so I decided to make a tool that works with the files generated by AngelCode:


The tool takes a high resolution AngelCode .fnt file as input. It scales it down by the specified scale factor and converts it to a distance field. The spread specifies how many pixels the distance field should extend outside the character outline before it clamps to zero. (It is useful if you want to add things such as glow effects to the font rendering.) After the conversion, the tool outputs new scaled down .tga images of the fonts and a new .fnt file with all measurements converted to work with the scaled down textures.

So to use it, you first generate a font bitmap and .fnt file using AngelCode at 8 x the font size and 8 x the texture size you want in the final image. (Make sure to add 8 x spread pixels of padding around the characters or else the distance fields will bleed into each other.) Then you run the tool to convert it to a distance field texture.

The tool is a bit limited -- it only works with monochrome uncompressed .tga files. It only reads and writes the XML version of the AngelCode font format. The distance field generation isn't particularly clever or fast. But I thought I should share it anyway since I couldn't find any other tools for generating distance field based font maps. Modifying it to support more formats shouldn't be much work.

Grab a binary version here:


Or the C# project files here:


Feel free to do whatever you want with it!

13 comments:

  1. Here's some other source/papers/exe you might want to check on the font/AA images topic, it might interest you:
    http://contourtextures.wikidot.com/
    http://www.ogre3d.org/forums/viewtopic.php?f=1&t=50844

    ReplyDelete
  2. Thank you. The anti-aliased version is interesting. One drawback with the method I'm using here is that you loose true type font hinting when you scale up/scale down. As a result, some of the finer details in the font can get lost (like in the upper part of the r in the example above) because the sample points where we measure the distance do not get optimally placed to capture the features of the font. (At least I think that is what is happening.)

    Using the AA method on a correctly sized font texture (i.e. not an upscaled one) might give a better result since it respects hinting. Would be worth trying.

    ReplyDelete
  3. Hi Niklas, very nice !
    I was researching those techniques for some new effects and ended on your page, what a coincidence.
    We used a similar technique in God of War 3 for our font rendering. It's a nice way to avoid baking the texts in textures while maintaining a good quality.

    ReplyDelete
  4. Hi Cedric!

    What a coincidence indeed! The game engine world sometimes seems a lot smaller than you would think.

    ReplyDelete
  5. Hi Niklas,

    In you app you generate a fnt file that has width and height values with decimal values. Am I right to assume that you use the rounded values of these? Does the error inserted by this is noticeable?

    Thanks for this great tool :),
    Jorge

    ReplyDelete
  6. No, I actually use the decimal values.

    ReplyDelete
  7. Have you tried this with more complex signs like a hanzi/kanji? I can imagine you won’t be able to scale it down that much without losing important information.

    In a system supporting Chinese typing you would probably need about 2x 1024x32 textures to get a reasonable amount of details (and using any form of texture compression for the GPU is probably unthinkable). I fear that our alphabet is only a fraction of the required memory, and probably among the few signs that can greatly benefit from this technique? It would though be interesting to hear if you have done any experimentation with this.

    We are currently using 2x 1024x32 textures for supporting the full Unicode char set, and Chinese seam to by far be the biggest char set with in it. It works okay, but I am considering starting rasterizing whole paragraphs in software and save to temporary textures. Because just thinking about adding support for large fonts for HUD and headers hurts... would probably require at least double that memory even if the distance field method works reasonably for hanzi.

    Do you have any thoughts on the matter? I can guess software rasterizing and the extra memory associated with that would have a whole other impact on console than on PC. And I have yet to get any real experience with programming larger projects for a console.
    (You can always try to scale down this one; 簣)

    ReplyDelete
  8. No I haven't experimented that much with complex characters. But you need a certain minimum resolution for this to work. If your resolution is so low that the basic strokes of the characters "blur together", then the distance field approach can't "unblur" them. But as long as that doesn't happen you should be fine.

    A scaled up distance field font will never look as good as the "real font" rendered at a bigger font size though. (Just better than a scaled up bitmap font.) For maximum quality that is the way to go. Juggling all those temporary textures is annoying though.

    ReplyDelete
  9. Hi, I wanted to use this tool, but it isn't working for me. It is generating all-white tga files. Have you run into that problem before? I want a 36 pixel font ultimately, so I generated a 288-pixel angelcode font. I put 64 pixels of padding on all four sides because I want 8 pixels of spread. I used the xml fnt format, and get no errors. Any idea what I might be doing wrong?

    Also I am wondering why you need to generate so large a font initially? The original distance field article just used a bitmap the same size as the resulting distance field image, didn't it? It's no big deal since the BMFont output is temporary but I was just wondering.

    Thanks for any assistance!
    Bob

    ReplyDelete
  10. Hi Bob. No I haven't seen that error.

    I use big textures just to get better values for the distance field. (Since I estimate the distance to the glyph curve by counting pixels.)

    ReplyDelete
  11. Oh that is funny -- the files are fine, actually. They just show up as all white when you view them in Quicktime Picture Viewer. That's the only program on my Windows 7 machine apparently that can show .tif files. What do you use? I'm installing Gimp but that's a heavy program just to look at TIFs.

    Thanks
    Bob

    ReplyDelete
  12. BMFont doesn't seem to be able to generate 600px size fonts exported to 2048x2048 textures. I guess that's too large?

    I tried converting to distance field using the same scale -- I put '1' into the Scale Down By box, is that right or should it be 0?

    The fonts aren't rendering yet but displaying them with the old shader shows the new font to be smaller, and more closely packed together, than the original. Shouldn't it be exactly the same if the scale is the same? In fact shouldn't I be able to use the original .fnt file in that case? Or does the converter change things about the glyphs?

    Thanks
    Bob

    ReplyDelete
  13. Hello again,

    I made some modifications to your tool that might be of use to other people working with distance field fonts.

    The giant fonts exported from BMFont for use in your tool will usually consist of several pages of texture map images, especially if you are making the entire alphabet. But once they are shrunken down into DF fonts, there's no need for them to be spread across several images. It's more convenient if the smaller DF letterforms are placed on a single texture map.

    I modified your code to do this -- for example, a font exported at 480 x 480 pixels per character takes four 2048x2048 texture maps. Shrinking down by a factor of 8, the distance field font fits on a single 1024x256 map.

    I haven't tested it beyond what I needed for my single application, and I am not a C# programmer -- so there could be some bugs in there. But it worked for me. Here's the executable and source code for the modified version if anyone wants to try it:

    http://www.flatblackfilms.com/distance_field_exe.zip
    http://www.flatblackfilms.com/distance_field_source.zip


    Thanks,
    Bob

    ReplyDelete