Search Unity

TextMesh Pro Creating a Pixel Font from a Bitmap Image

Discussion in 'UGUI & TextMesh Pro' started by wickedpopular, Mar 30, 2022.

  1. wickedpopular

    wickedpopular

    Joined:
    Oct 29, 2016
    Posts:
    33
    Hello! I've been back and forth with this for quite a while now, and unfortunately have never been able to get this working properly.

    I'm trying to create a usable Text Mesh Pro font asset from a Sprite Sheet bitmap image, and keep that font aligned to a "pixel grid" for display.

    Here is the sprite sheet I started with:

    Font.png

    Here are all of the steps I went through:

    Scene Setup
    • Create a new scene
    • Add PixelPerfectCamera to the Main Camera.
      • Assets per PPU: 1
      • Reference Resolution: 160 x 144
    • Create a TMP Text Asset (UI -> Text - Text Mesh Pro)
    • Change the Canvas Settings to the following:
      • World Space
      • Canvas Scaler Dynamic/Reference PPU: 1
      • Canvas Position: (0,0,0)
      • Canvas Width and Height: 160 x 144
    • The TMP Text Asset may need to have its position adjusted as well.
    Font Creation
    • Import Font Sprite Sheet Image
      • Sprite Mode: Single
      • PPU: 1
      • Filter: Point
      • Compression: None
    • Create a Font Asset (Window -> Text Mesh Pro -> Font Asset Creator)
      • Source Font File: LiberationSans
      • Character Set: Custom Characters using the string in the Font Sprite Sheet (In my case, "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ")
      • Generate Font Atlas and Save
    • Under Font Asset, set all values under Face Info to Zero except:
      • Scale: 1
      • Line Height: 8
      • Ascent Line: 8
    • Click Arrow beside Font Asset and view the Material
    • Change Material type to "Bitmap Custom Atlas"
    • Under Material's "Debug Settings," set Font Atlas to Font Sprite Sheet
    • Go to Font Asset
    • Click 3 dots beside lock in the Inspector, and select "Debug"
    • Under "Atlas Textures," under Element Zero, change reference to Font Sprite Sheet
    • Under "Atlas," select Font Sprite Sheet
    • Under Atlas Width and Height, enter Sprite Sheet Width and Height (in our case, 104x24)
    • Click 3 dots again, and return to "Normal"
    • Go to Unicode # of Glyph (I started with 0030, "0".)
    • Input the Bounding Box of the Glyph
      • Glyph Bounding boxes start from the bottom left corner
      • [X,Y] is the starting position on the sprite sheet; [W,H] is the width and height of the box on the sprite sheet
      • [W,H] is the World Width and Height
      • I don't know what [BX] does but it only works when set to Zero.
      • [BY] should be character size (8 in our case)
      • [AD] is how far the to step forward after a character is drawn (8 in our case)
    This means my first glyph had the following settings:
    Glyph Rect.PNG


    But this gave me the following results:
    Font Demo Behavior.PNG

    As you can see, this is mostly off the grid, but my 8x8 characters are slightly out of scale. By using my scaler image, I can see that the characters are around 10 pixels tall. The characters also seem to be positioned slightly off the screen, rather than directly in the corner as I'd expect. It seems like an extra line or two of pixels are getting attached to the glyph, but I don't know why.

    What steps have I performed incorrectly in my workflow above? I've attached my package to this post so that you can see all of my settings in action.
     

    Attached Files:

  2. wickedpopular

    wickedpopular

    Joined:
    Oct 29, 2016
    Posts:
    33
    Hello! Unfortunately I've remained stuck at this point. What I believe is happening is that the top of the character below each character on the sprite sheet is getting clipped into the character above it, but I'm not sure why.

    I edited my sprite sheet to remove the "A" character below the Zero for testing, and with some adjustment was able to come up with the following:

    New Glyph Settings.PNG

    New Glyph.PNG

    I guessed that TMP might not be reading pixels starting at Zero and was instead either grabbing pixels off the edge of the sheet or looping around to the other side, accounting for the extra pixels in height. Along with adjusting the Glyph BX and BY this seems to have fixed it, but strangely, that extra 2x2 block of pixels in the bottom right is also included in the Zero glyph. Why would that be?
     
  3. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I'll try to take a look at your demo package as soon as possible and provide feedback thereafter.
     
    wickedpopular likes this.
  4. wickedpopular

    wickedpopular

    Joined:
    Oct 29, 2016
    Posts:
    33
    Thank you very much! I've had this problem for quite a while, and I've seen a few other people have this problem as well on this board. I'd like to try and create a step-by-step workflow for this process so that people won't need to continuously ask about this.
     
  5. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I had a chance to look into this earlier today and the behavior is due to mesh padding where a padding value of 1 is used to prevent clipping on bitmap text. As such, the geometry ends up with this extra padding where in your case it is revealing pixels from adjacent glyphs in your texture.

    A quick way around this is to set the padding on the material to -1 where as seen below and provided the GlyphRect values are set correctly renders as expected.

    upload_2022-4-5_15-11-29.png

    Here is an example with the padding value at zero resulting in the behavior you have reported. Looking at the scene with Shaded Wireframe shows how the geometry has the extra padding. I made the text transparent to make it easier to see this.

    upload_2022-4-5_15-14-16.png

    So be sure to use a padding value of -1 on the material using the Bitmap Custom Atlas shader as seen below.

    upload_2022-4-5_15-15-16.png

    I will be taking a closer look at this as having to set the value to -1 is confusing and misleading. I will revised this where the default value will be 1 to more accurately reflect what is going on and prevent clipping. For those cases where there is no padding between glyphs in the texture, users will be able to set this value to zero.

    Just for reference here are the Glyph Metrics for the '0' and '1' which use glyph index 19 and 20.

    upload_2022-4-5_15-21-51.png

    Notice how these values match what we get in the Sprite Editor while looking at the '0'.

    upload_2022-4-5_15-23-12.png
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Below are the values for the '1'

    upload_2022-4-5_15-24-13.png

    Note that I also edited the font face metrics as follows to set the Line Height, Ascender, Descender and other relevant metrics.

    upload_2022-4-5_15-25-37.png

    Adding a TMP_TextInfoDebugTool.cs script (contained in the TMP Examples & Extras) to the text component enables you to visualize these metrics so that you can fine tune them for your custom font asset.

    This is how you can also verify that you have set the BX and BY and Advance value for each glyphs.

    upload_2022-4-5_15-27-35.png
     
  7. wickedpopular

    wickedpopular

    Joined:
    Oct 29, 2016
    Posts:
    33
    I have deleted my previous font asset and created a new one from scratch. Since my goal is to produce a fixed-width font, each character will be a uniform 8x8 "pixels" in size. Here is the new workflow I have followed:

    Creating a Font Asset from a Bitmap Image
    • Create a Font Asset (Window -> Text Mesh Pro -> Font Asset Creator)
      • Source Font File: LiberationSans
      • Character Set: Custom Characters using the string in the Font Sprite Sheet (In my case, "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ")
      • Generate Font Atlas and Save
    • Under Font Asset, set all values under Face Info to Zero except:
      • Scale: 1
      • Line Height: 8
      • Ascent Line: 8
      • Cap Line: 4
      • Mean Line: 4 (This is the middle of the character)
      • Descent Line: 0
      • Strikethrough Offset: 2
    • Click Arrow beside Font Asset and view the Material
    • Change Material type to "Bitmap Custom Atlas"
    • Under Material's "Debug Settings," set Font Atlas to Font Sprite Sheet
    • Change "Padding" to -1
    • Go to Font Asset
    • Click 3 dots beside lock in the Inspector, and select "Debug"
    • Under "Atlas Textures," under Element Zero, change reference to Font Sprite Sheet
    • Under "Atlas," select Font Sprite Sheet
    • Under Atlas Width and Height, enter Sprite Sheet Width and Height (in our case, 104x24)
    • Click 3 dots again, and return to "Normal"
    • Go to Unicode # of Glyph (I started with 0030, "0".)
    • Input the Bounding Box of the Glyph
      • Glyph Bounding boxes start from the bottom left corner
      • [X,Y] is the starting position on the sprite sheet; [W,H] is the width and height of the box on the sprite sheet
      • [W,H] is the World Width and Height
      • I don't know what [BX] does but it only works when set to Zero.
      • [BY] should be character size (8 in our case)
      • [AD] is how far the to step forward after a character is drawn (8 in our case)
    Using the above settings, I was able to achieve the following:
    Font Demo.PNG

    I have attached my working package below; if anyone else has this problem in the future, they should be able to use my demo package with their own sprite sheet. I do have a couple of questions, though:

    I have adjusted the Cap and Mean line to the center of my character, which differs from the settings in your post above. Will this cause any problems?

    Secondly, in my workflow above, I have a section highlighted in red. Those steps are:
    • Click 3 dots beside lock in the Inspector, and select "Debug"
    • Under "Atlas Textures," under Element Zero, change reference to Font Sprite Sheet
    • Under "Atlas," select Font Sprite Sheet
    • Under Atlas Width and Height, enter Sprite Sheet Width and Height (in our case, 104x24)
    • Click 3 dots again, and return to "Normal"
    In my experience, this is the only way to get a bitmap to display properly. However, going into the debug properties and changing things doesn't seem like the intended behavior. Is there a better way to do this?

    Finally, when creating my font, I had to manually type in the coordinates of each character. Is there a way to expedite this, such as being able to copy properties from one glyph to another existing glyph? Since my font's characters only differed in the X and Y coordinates, it would be ideal if there was a way to adjust all of a font's glyph properties at the same time, so that I'd only have to adjust 2 values for each character rather than all 9.

    Thank you for your help!

    Edit: Upon further inspection, I've noticed that there are some small artifacts near the bases of my glyphs. Would you happen to know why this is happening? It occurs in the attached package; the Debug lines covered them up when I first inspected it.

    Artifacts.PNG
     

    Attached Files:

    Last edited: Apr 7, 2022
    Olipool likes this.