Search Unity

Scaling Bitmap Fonts

Discussion in 'Made With Unity' started by KvanteTore, Nov 26, 2010.

  1. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Hi there!

    I've made an implementation of the Improved Alpha-Tested Magnification for Vector Textures and Special Effects (recently mentioned on Joakim Hårsmans blog) for scaling bitmap fonts in Unity.

    The implementation uses AngelCode's BMFont bitmap textures which are converted into distance field textures by a unity importer.

    Check it out on our blog.

    The code is of course freely available, but it is a bit rough around the edges, and could do with a bit of tlc. If anyone finds it interesting I might try to make it a bit cleaner though. :)

    cheers,
     
  2. simone007

    simone007

    Joined:
    Oct 30, 2008
    Posts:
    221
    Bitmap Font Generator is a great tool
     
  3. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Very cool. I'm surprised that everyone hasn't been using this method since Valve published that paper. I implemented an SDF generator for Unity earlier this year, but I didn't bother optimizing it at all, and abandoned it because I couldn't get it to play nice with Unity's fonts.

    I'll check out your solution this weekend.
     
  4. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    I've not tried to use the font system at all, and it's all a bit messy. I haven't been able to get very good results on small font sizes (which, i guess, is to be expected), so i guess it's best suited for large fonts like HUD elements and vector graphics.

    I'd love to hear what you think, though :)
     
  5. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    I just tried it out, and it looks very nice. There must be a better way of using it in GUI, though. You can't change the material used for GUI/Layout, but you can for GUIText.
     
  6. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    Very interesting stuff.
     
  7. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Are you sure? I haven't tried it out, but it seems like you can set Font.material.

    Yes, it would be great with a better integration with the GUI systems in unity.

    Making a GUIText replacement should be really straight forward.

    If we can replace the material on GUI.skin.font, I guess it would be possible to get Unity to rasterize a high res version of the font and then create a reduced scale SDF from that, although I'm not really impressed with the flexibility of the unity font rasterizer, so the optimal solution would be to replace their entire font thing with a BMFont based renderer (although I doubt that's currently possible)
     
  8. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    GUI/Layout ignores font materials and uses its own shader no matter what.
     
  9. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Ah, I see... :(

    I guess I'll go for a GUIText kind of approach then.
     
  10. itech

    itech

    Joined:
    Jul 28, 2010
    Posts:
    139
    How can I import font ? I chose Assets -> BitmapFonts -> Import font , and nothing happen.

    EDIT :
    Sorry it's works just fine, my mistake in import .


    PS.

    Great work, thanks a lot
     
    Last edited: Nov 30, 2010
  11. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Yes, you need to right-click the .fnt file in the project view and select BitmapFonts -> Import font from there (in case anyone else wonders) :)
     
  12. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    It's a good idea to use the MenuItem(string, bool) version of the attribute to provide a function that enables the menu item only when an appropriate file type is selected.
     
  13. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I'm trying to rotate a previously rendered bitmap which contains lines and shapes at various angles. When this is rotated, even though it was rendered originally with great antialiasing, it creates a `bitty` look when normally diagonal lines are rendered near horizontal or vertical. I wonder if it would be possible that this technique would create a smoother gradual result when the bilinear filter is applied?
     
  14. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    The actual rendering process doesn't do anything special when it renders the signed distance field. The reconstruction operation is just alpha testing. If you want higher quality data, you need to increase the resolution of the source bitmap you are using, and possibly the resolution of the distance field bitmap. That is, increase the quality of the original data first, and if that doesn't work then you need to increase the resolution of the extracted distance field so that it can better represent the original.
     
  15. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    I should also mention that antialiasing in the original image doesn't help, as the SDF generation method KvanteTore is using doesn't take grayscale into account.
     
  16. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Have you looked at the RotSprite algorithm (http://en.wikipedia.org/wiki/2xSaI#RotSprite), I came over that wikipedia article when I was reading about font scaling, but I haven't looked any more into those algorithms.

    The distance field approach works best for round features and is not awesome for thin lines or sharp features, and as Daniel is saying, you need a really high resolution version of the original image for the current algorithm to create a good distance field. There are other algorithms for creating distance fields though (http://contourtextures.wikidot.com/) which supposedly works with antialiased original images as well.
     
  17. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Awesome! That's a brilliantly hidden gem right there.

    Thank you for pointing it out. I've updated the github repo and the uploaded unity package :)
     
  18. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    A version without a shadow in the unity demo package would be sweet.
     
  19. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Hi Timmey,

    Thank you for checking it out! :) You can easily remove the shadow by setting 100% transparency on the shadow colour, or by setting both the upper and lower shadow alpha thresholds to 1

    When I have time I'll make a GUIText clone, only using the BMFont renderer and this scaling technique. When that's done, I'll be sure to include examples without shadow as well. Mabye even implement a simpler shader for text without outline/shadow as well.

    cheers,
     
  20. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
  21. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Yes, I suppose it would.

    I want to use this technique to get outline effects for my HUD fonts, but I suppose it would also make a lot of sense to use it for 3D fonts.

    I don't have all that much time to hack on this right now, but I have a weekly commute train that takes me about an hour each way, and I'll see what I can hack together on the way to work on tuesday :)
     
  22. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    I did not mean to put you out. I have had the Cg tutorial on my desk for a while now I really need to start reading it. I keep getting distracted with other programming tasks. Eventually this is one of the areas I plan to work on and contribute back to the community I think you have already given me a great head start. :)

    I am more interested in where or not you think my reasoning is right. That distance fields would be able to render floating text / textures over someone's head that does not become blurry as you zoom out, without having to scale the physical size of the text area.
     
    Last edited: Dec 10, 2010
  23. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    That should be completely possible, and wouldn't actually require Cg unless you wanted antialiasing. For AA, you can either do a fixed radius blur (which is what KvanteTore's shader does) or make it adaptive, which would be more complicated.

    I think the tricky part is getting it to work with Unity's font system and 3D text. So far I've had scaling and bias issues that I can't seem to figure out.
     
  24. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    Are you sure you have unity's 3d text working correctly first? You need to use the GUI/ Text shader plus you need to have the font material that corresponds to your font.

    If you have the wrong font material, your text will look all distorted. This sounds like what you are having. It may be easier to just extend unity editor which is really easy rather than using unity's 3d text.

    http://img207.imageshack.us/img207/4610/3dtext.jpg

    Note all the fonts will be imported with the same name for their font material, which can be confusing.
     
    Last edited: Dec 10, 2010
  25. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Using the GUI/Text shader would defeat the purpose of this whole thing, because it doesn't render SDF textures properly.

    In any case, it looks like I modified the font after generating the SDF, which was causing the UV coordinates not to match. I regenerated the SDF from the font, and now it works fine with text meshes and GUIText.
     
  26. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    Interesting so you are not using a bit map font then?
     
  27. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    The main problem I've found with using Unity's font system is that there's no way to specify a padding between characters, and without padding it's hard to get enough space between characters to create a nice SDF without it bleeding between the characters. Also, our artist was complaining about the difficulties of fitting an outline around the font.

    I really like the flexibility of the BMFont character packer, whether using SDF fonts or normal coverage fonts, so I think I'll make my own clones of TextMesh and GUIText based on the BMFont character packing.

    Ideally, BMFont or a similar interface could be integrated into the unity editor, but that's quite a bit more work than I have time to right now, so I think I'll stick to importing .fnt files
     
  28. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Those are definitely problems with trying to extend the built-in font system. For the record, though, here is the script I used to get Unity's imported font bitmaps:
    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.IO;
    4.  
    5. public class FontTextureSaver {
    6.    
    7.     [MenuItem("Assets/Copy Font Texture")]
    8.     public static void CopyFontTexture() {
    9.         var font = (Font)Selection.activeObject;
    10.        
    11.         // Create or find the textures we need
    12.         Texture fontTexture = font.material.mainTexture;
    13.        
    14.         var renderTexture = new RenderTexture(fontTexture.width, fontTexture.height, 0);
    15.        
    16.         var outputTexture = new Texture2D(
    17.             fontTexture.width,
    18.             fontTexture.height,
    19.             TextureFormat.ARGB32,
    20.             false
    21.         );
    22.        
    23.         // Copy from non-readable Texture2D to RenderTexture
    24.         Graphics.Blit(fontTexture, renderTexture);
    25.        
    26.         // Copy from RenderTexture to Texture2D
    27.         RenderTexture.active = renderTexture;
    28.         outputTexture.ReadPixels(
    29.             new Rect(0, 0, fontTexture.width, fontTexture.height),
    30.             0,
    31.             0,
    32.             false
    33.         );
    34.         RenderTexture.active = null;
    35.        
    36.         Object.DestroyImmediate(renderTexture);
    37.        
    38.         // Copy alpha channel to grayscale
    39.        
    40.         Color[] colors = outputTexture.GetPixels();
    41.        
    42.         for (int i = 0; i < colors.Length; ++i) {
    43.             colors[i].r = colors[i].g = colors[i].b = colors[i].a;
    44.             colors[i].a = 1f;
    45.         }
    46.         // Don't need to apply this change because it's not going to the graphics card
    47.         outputTexture.SetPixels(colors);
    48.        
    49.         // Get PNG data
    50.         byte[] bytes = outputTexture.EncodeToPNG();
    51.        
    52.         Object.DestroyImmediate(outputTexture);
    53.        
    54.         // Save PNG
    55.         string outputPath = AssetDatabase.GetAssetPath(font);
    56.        
    57.         outputPath = Path.ChangeExtension(outputPath, "png");
    58.        
    59.         File.WriteAllBytes(outputPath, bytes);
    60.        
    61.         // Notify Unity of the update
    62.         AssetDatabase.Refresh();
    63.     }
    64.    
    65.     // Make sure the user has a font selected
    66.     [MenuItem("Assets/Copy Font Texture", true)]
    67.     public static bool ValidateCopyFontTexture() {
    68.         return  Selection.activeObject
    69.                 Selection.activeObject as Font;
    70.     }
    71.    
    72. }
     
  29. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Cool,

    I don't think you need to blit the font texture to a render texture though, mainTexture on the font material is a Texture2D and you can simply cast it.

    I've incorporated a similar (although less comprehensive) script as well that uses the Unity Font texture as a basis for generating a SDF. The result is a font that can use the SDF shader and scale nicely and still use GUIText, but the characters bleed into each other a bit, so it's a bit rubbish. I guess that I could generate the SDFs for each character individually to avoid bleeding, but still the slim characters like the "l" have so little texture space that the resulting SDF isn't very nice (at least not with the SDF generation algorithm i'm using).

    I've also done as Timmey suggested, and made a mesh font (that can be placed/rotated/scaled anywhere in the unity scene).



    The changes have been pushed to the github repo and the unity package has been updated.

    cheers,
     
    Last edited: Dec 15, 2010
  30. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    The font texture isn't readable, though. I blit it to the RenderTexture in order to be able to copy that and save a PNG. Unless I'm missing something, because it does seem convoluted. I need the PNG to run through an SDF generator, which is external.
     
  31. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    In Unity3 at least the font texture seems to be readable as long as Character is set to something else than "Dynamic". At least that's working for me.
     
  32. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    Cool thanks this will be very helpful for a lot of people I believe.
    I was getting a few errors which I seemed to have fixed with the below changes.

    in BitmapFont
    Code (csharp):
    1.  
    2.  
    3.     public Material GetPageMaterial(int page)
    4.     {
    5. [COLOR="red"]        if (pageMaterials == null || Pages == null)
    6.         {
    7.             return null;
    8.         }[/COLOR]
    9.  
    10.         if (pageMaterials.Length != Pages.Length)
    11.         {
    12.             pageMaterials = new Material[Pages.Length];
    13.         }
    14.  
    15.         if (page >= Pages.Length)
    16.         {
    17.             return null;
    18.         }
    19.  
    20.         if (pageMaterials[page] == null)
    21.         {
    22.             pageMaterials[page] = CreateFontMaterial();
    23.             pageMaterials[page].mainTexture = Pages[page];
    24.         }
    25.  
    26.         UpdateFontMaterial(pageMaterials[page]);
    27.         return pageMaterials[page];
    28.     }
    29.  
    30.  
    and in BitmapMeshText

    Code (csharp):
    1.  
    2.   //Replace mesh
    3.              Mesh mesh = GenerateLineMesh(position - offset, Text, renderSize);
    4. [COLOR="red"]            //note calling gameObject.GetComponent<MeshFilter>().mesh If no mesh is assigned to the mesh filter a new mesh will be created and assigned.
    5.             gameObject.GetComponent<MeshFilter>().mesh = mesh;[/COLOR]
    6.  
    7.             //Get materials for each texture page
    8.             Material[] mats = new Material[mesh.subMeshCount];
    9.             for (int i = 0; i < mesh.subMeshCount; i++)
    10.             {
    11.                 mats[i] = Font.GetPageMaterial(i);
    12.             }
    13.             renderer.materials = mats;
    14.  
    15.             renderedText = Text;
    16.  
    and in BitmapMeshText
    Code (csharp):
    1.  
    2.  
    3.     void Update()
    4.     {
    5.         if (Font == null)
    6.         {
    7.             return;
    8.         }
    9.  
    10.         Vector3 renderSize = new Vector3(1, 1, 1);
    11.        [COLOR="red"] Vector2[/COLOR] renderSize2 = new [COLOR="red"]Vector2([/COLOR]renderSize.x, renderSize.y);
    12.         Vector3 position = new Vector3(0, 0, 0)
    13.  
    14.  
     
    Last edited: Dec 16, 2010
  33. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Thanks!

    I've pushed the fixes in the github repo.

    Regarding the one about a mesh getting generated if you try to access it: wouldn't your suggestion leak a mesh into the scene every time the text changes? If so, I think it would be better to get one empty mesh and destroy it the first time.

    cheers,
     
  34. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    yes this will happen and unity will give a message saying that it has cleaned up a leak. At least it was doing this when I added a text mesh to compare with the SDF. Note it does not give a warning or error.

    This seems to be the way that unity suggest to do this.
    http://unity3d.com/support/documentation/ScriptReference/Mesh.Mesh.html

    Not sure, how you can fix this because I do not believe that Destroying the mesh will fix the leak. Because the act of setting the mesh also creates a new one that will then be leaked anyway. If the mesh is null.

    You maybe able to use Mesh.Clear and then directly modify it.
    http://unity3d.com/support/documentation/ScriptReference/Mesh.Clear.html
     
    Last edited: Dec 16, 2010
  35. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    I think i've fixed it by using .sharedMesh rather than .mesh, and DestroyImmediate if it's Application.isPlaying is false. Have to go to work though, so I'll test it this afternoon :)
     
  36. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    Editor mode really does not seem to like .mesh. Here's a version that uses Start(), Clear and sharedMesh rather than making a new mesh. This version should not leak for sure.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. [ExecuteInEditMode()]
    6. [RequireComponent(typeof(MeshRenderer))]
    7. [RequireComponent(typeof(MeshFilter))]
    8. public class BitmapMeshText : MonoBehaviour
    9. {
    10.     public BitmapFont Font;
    11.     public string Text;
    12.     public TextAnchor Anchor;
    13.  
    14.     private string renderedText;
    15. [COLOR="red"]    private Mesh originalMesh;[/COLOR]
    16.     #region Quad Parameters
    17.    
    18.     private Vector3[] quadVerts =
    19.     {
    20.         new Vector3(0, 0),
    21.         new Vector3(0, 1),
    22.         new Vector3(1, 1),
    23.         new Vector3(1, 0)
    24.     };
    25.  
    26.     private Vector2[] quadUvs =
    27.     {
    28.         new Vector2(0, 0),
    29.         new Vector2(0, 1),
    30.         new Vector2(1, 1),
    31.         new Vector2(1, 0)
    32.     };
    33.  
    34.     private int[] quadTriangles =
    35.     {
    36.         0, 1, 2,
    37.         2, 3, 0
    38.     };
    39.  
    40.     #endregion
    41.  
    42. [COLOR="red"]    void Start()
    43.     {
    44.         originalMesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
    45.     }
    46. [/COLOR]
    47.     // Update is called once per frame
    48.     void Update()
    49.     {
    50.         if (Font == null)
    51.         {
    52.             return;
    53.         }
    54.  
    55.         Vector3 renderSize = new Vector3(1, 1, 1);
    56.         Vector2 renderSize2 = new Vector2(renderSize.x, renderSize.y);
    57.         Vector3 position = new Vector3(0, 0, 0);
    58.  
    59.         if (renderedText != Text)
    60.         {
    61.             //Calculate bounding box of rendered text
    62.             Vector2 bounds = Font.CalculateSize(Text, renderSize2);
    63.  
    64.             Vector3 offset = new Vector3(0, 0);
    65.             if (Anchor == TextAnchor.UpperCenter || Anchor == TextAnchor.UpperLeft || Anchor == TextAnchor.UpperRight)
    66.             {
    67.                 offset.y = bounds.y;
    68.             }
    69.             if (Anchor == TextAnchor.MiddleCenter || Anchor == TextAnchor.MiddleLeft || Anchor == TextAnchor.MiddleRight)
    70.             {
    71.                 offset.y = bounds.y / 2;
    72.             }
    73.             if (Anchor == TextAnchor.UpperRight || Anchor == TextAnchor.MiddleRight || Anchor == TextAnchor.LowerRight)
    74.             {
    75.                 offset.x = bounds.x;
    76.             }
    77.             if (Anchor == TextAnchor.UpperCenter || Anchor == TextAnchor.MiddleCenter || Anchor == TextAnchor.LowerCenter)
    78.             {
    79.                 offset.x = bounds.x / 2;
    80.             }
    81.  
    82. [COLOR="red"]            //Replace mesh
    83.             GenerateLineMesh(position - offset, Text, renderSize);[/COLOR]
    84.         }
    85.     }
    86.  
    87.     private [COLOR="red"]void [/COLOR]GenerateLineMesh(Vector3 position, string str, Vector3 renderSize)
    88.     {
    89.         //Set up mesh structures
    90.         int submeshCount = Font.Pages.Length;
    91.         List<int>[] Triangles = new List<int>[submeshCount];
    92.         for (int i = 0; i < submeshCount; i++)
    93.         {
    94.             Triangles[i] = new List<int>();
    95.         }
    96.         List<Vector3> vertices = new List<Vector3>();
    97.         List<Vector2> uvs = new List<Vector2>();
    98.  
    99.         //Keep track of position
    100.         Vector3 curPos = position;
    101.         Vector3 scale = renderSize / Font.Size;
    102.  
    103.         for (int idx = 0; idx < str.Length; idx++)
    104.         {
    105.             char c = str[idx];
    106.             BitmapChar charInfo = Font.GetBitmapChar((int)c);
    107.             int vertIndex = vertices.Count;
    108.  
    109.             //Set up uvs
    110.             Rect uvRect = Font.GetUVRect(charInfo);
    111.             Vector2 uvScale = new Vector2(uvRect.width, uvRect.height);
    112.             Vector2 uvOffset = new Vector2(uvRect.x, uvRect.y);
    113.             for (int i = 0; i < quadUvs.Length; i++)
    114.             {
    115.                 uvs.Add(Vector2.Scale(quadUvs[i], uvScale) + uvOffset);
    116.             }
    117.  
    118.             //Set up verts
    119.             Vector3 vertSize = Vector2.Scale(charInfo.Size, scale);
    120.             Vector3 vertOffset = Vector2.Scale(charInfo.Offset, scale);
    121.             vertOffset.y = renderSize.y - (vertOffset.y + vertSize.y);  // change offset from top to bottom
    122.             for (int i = 0; i < quadVerts.Length; i++)
    123.             {
    124.                 Vector3 vert = Vector3.Scale(quadVerts[i], vertSize) + curPos + vertOffset;
    125.                 vertices.Add(vert);
    126.             }
    127.  
    128.             //Set up triangles
    129.             for (int i = 0; i < quadTriangles.Length; i++)
    130.             {
    131.                 Triangles[charInfo.Page].Add(quadTriangles[i] + vertIndex);
    132.             }
    133.  
    134.             //Advance cursor
    135.             float krn = 0;
    136.             if (idx < Text.Length - 1)
    137.             {
    138.                 krn = Font.GetKerning(c, Text[idx + 1]);
    139.             }
    140.             curPos.x += (charInfo.XAdvance + krn) * scale.x;
    141.         }
    142. [COLOR="red"]
    143.         //Assign verts, uvs, tris and materials to mesh
    144.         originalMesh.Clear();
    145.         originalMesh.vertices = vertices.ToArray();
    146.         originalMesh.uv = uvs.ToArray();
    147.         originalMesh.subMeshCount = submeshCount;
    148.         for (int i = 0; i < submeshCount; i++)
    149.         {
    150.             originalMesh.SetTriangles(Triangles[i].ToArray(), i);
    151.         }
    152.  
    153.         //Get materials for each texture page
    154.         Material[] mats = new Material[originalMesh.subMeshCount];
    155.         for (int i = 0; i < originalMesh.subMeshCount; i++)
    156.         {
    157.             mats[i] = Font.GetPageMaterial(i);
    158.         }
    159.         renderer.materials = mats;
    160.         //
    161.         renderedText = Text;[/COLOR]
    162.     }
    163. }
     
    Last edited: Dec 16, 2010
  37. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    cool, thanks! :)
     
  38. Timmey

    Timmey

    Joined:
    Apr 13, 2010
    Posts:
    35
    I just realized that I had a mesh assigned to that mesh filtered for testing purposes. So, if you decide to use that code make sure you do.

    Code (csharp):
    1.  
    2.   void Start()
    3.     {
    4.         gameObject.GetComponent<MeshFilter>().sharedMesh = new Mesh();
    5.         originalMesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
    6.     }
    7.  
     
    Last edited: Dec 16, 2010
  39. noamgat

    noamgat

    Joined:
    Nov 22, 2009
    Posts:
    125
    Really cool stuff!

    Two things :
    1) Validation of the "Convert Unity TTF to SDF" is off, I think. It seems like you copy/pasted the validation code from the other one.
    This :
    Code (csharp):
    1.  
    2.     [MenuItem("Assets/BitmapFont/Convert Unity TTF to SDF", validate = true)]
    3.     static bool Validate()
    4.     {
    5.         foreach (Object o in Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets))
    6.         {
    7.             string path = AssetDatabase.GetAssetPath(o.GetInstanceID());
    8.             if (path.ToLower().EndsWith(".fnt"))
    9.             {
    10.                 return true;
    11.             }
    12.         }
    13.         return false;
    14.     }
    Should probably just check if a font if selected and return true if so.

    In the same subject, I don't exactly understand how converting unity fonts work. I thought I was suppose to run that script, get the texture+material and use the material in GUITexts instead of the font's default material. When doing this, I don't see any text. (I did make sure that the 'font' points to the same font). What am I doing wrong?

    2) I'm trying to use this for a large font (large in terms of number of characters), but its important for me to keep the batch count low. So I want to use large bitmap textures. The problem is a combination of two things :
    A - DistanceField heavily downsamples the input image
    B - Unity has a texture import limit of 4096x4096.

    This essentially means that we can't use textures of over 512x512 with this method. Does anyone have any workaround for this? (Perhaps a standalone tool that renders the distance field?). The other solution would be to create a texture atlas, but that might get messy with the BitmapFont object...


    And once again, this is really cool. Great stuff!
     
    Last edited: Dec 26, 2010
  40. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Sweet!

    Norman:
    Sorry I haven't responded before, but I've been out travelig and it seems like I missed the forum notification.

    Cheers Timmey! :)

    Norman:
    1a) yes, you are absolutely correct. It was fixed in the version on github, but I'd forgot to update the unitypackage on our blog
    1b) the problem was that the default colors for the shader were completely transparent :p. I'm sorry about that. Fixed now to be completely opaque instead
    2) That's a brilliant idea, and it was in fact quite straight forward to implement :)

    I've updated the importer to use Texture2D.PackTextures to pack all the distfield pages in to a single texture. This will fail if you have more than 16 pages, will that be a problem for you?

    You can grab the latest package (v4) from our blog, but it would probably be best if you clone the github repo, as that's always going to be more up-to-date than the unitypackage.

    Thanks for the tip :)
    -Tore
     
  41. noamgat

    noamgat

    Joined:
    Nov 22, 2009
    Posts:
    125
    Cool, thanks!
    I'll have a look at the code and see how hard it will be to modify it to my needs. I will definitely need more than 16 pages (I need to support the chinese character set), but it probably won't be hard to modify the code to merge 16 pages each time, since your original code had support for more than one texture...
     
  42. PolyMad

    PolyMad

    Joined:
    Mar 19, 2009
    Posts:
    2,350
    Great stuff!
     
  43. noamgat

    noamgat

    Joined:
    Nov 22, 2009
    Posts:
    125
    I got the code, and modified it a bit - it can now create multiple atlases if needed (if the input to PackTextures is too much, it scales the textures down, which is not what we want to happen).

    I attached a unitypackage with the modified code. (I modified the version from the github repository of a few hours ago)

    [Edit]
    I also modified the code to save the intermediate (non-atlased) versions of the distance fields, because unity was crashing due to out of memory errors. In this version, you can right click on textures to only generate the distance fields from them, and then the "Import Font" function will just load the generated distance field, separating between the two memory-consuming tasks and allowing for it to finish.
     

    Attached Files:

    Last edited: Feb 1, 2011
  44. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    This is a modified text shader that creates an outline, instead of a shadow. It can be useful for glowing effects too. This version only works on desktop (non gles). Could someone suggest how this could be ported on Iphone ? Thanks.
    Code (csharp):
    1.   Shader "BitmapFont/Plain" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _AlphaMin ("Alpha Min", Range(0.0,1.0)) = 0.49
    5.         _AlphaMax ("Alpha Max", Range(0.0,1.0)) = 0.54
    6.         _ShadowColor ("Shadow Color", Color) = (0.3,0.3,0.3,1)
    7.         _ShadowAlphaMin ("Shadow Alpha Min", Range(0.0,1.0)) = 0.28
    8.         _ShadowAlphaMax ("Shadow Alpha Max", Range(0.0,1.0)) = 0.54
    9.         _MainTex ("Base (RGB)", 2D) = "white" {}
    10.     }
    11.     SubShader {
    12.         Tags {
    13.             "Queue"="Transparent"
    14.             "IgnoreProjector"="True"
    15.             "RenderType"="Transparent"
    16.         }
    17.         Lighting Off
    18.         Cull Off
    19.         ZTest Always
    20.         ZWrite Off
    21.         Fog {
    22.             Mode Off
    23.         }
    24.         Blend SrcAlpha OneMinusSrcAlpha
    25.  
    26.         Pass {
    27.             CGPROGRAM
    28.            
    29.             #pragma vertex vert
    30.             #pragma fragment frag
    31.             #include "UnityCG.cginc"
    32.             #pragma exclude_renderers gles
    33.            
    34.             float4 _Color;
    35.             float _AlphaMin;
    36.             float _AlphaMax;
    37.             float4 _ShadowColor;
    38.             float _ShadowAlphaMin;
    39.             float _ShadowAlphaMax;
    40.             sampler2D _MainTex;
    41.            
    42.             //Unity-required vars
    43.             float4 _MainTex_ST;
    44.  
    45.             struct v2f {
    46.                 float4 pos : SV_POSITION;
    47.                 float2 uv: TEXCOORD0;
    48.                 float4 color : COLOR;
    49.             };
    50.  
    51.             v2f vert (appdata_base v)
    52.             {
    53.                 v2f o;
    54.                 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    55.                 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    56.                 return o;
    57.             }
    58.  
    59.             float4 frag(v2f i) : COLOR
    60.             {
    61.                 float4 base = tex2D(_MainTex, i.uv);
    62.                 float alpha = smoothstep(_AlphaMin, _AlphaMax, base.w);
    63.                 float shadowAlpha = smoothstep(_ShadowAlphaMin, _ShadowAlphaMax, base.w);
    64.                 float4 shadow = _ShadowColor * shadowAlpha;
    65.                
    66.                 return mix(shadow, _Color, alpha);
    67.                
    68.                
    69.             }
    70.  
    71.             ENDCG
    72.         }
    73.     }
    74. }
    75.  
     
    Last edited: Apr 12, 2011
  45. KvanteTore

    KvanteTore

    Joined:
    Feb 16, 2010
    Posts:
    87
    Hi Ippokratis,

    Unfortunately I don't have access to Unity for mobiles yet, so I can't test for you, but I notice the

    #pragma exclude_renderers gles

    which excludes compilation of the shader on gles targets. Mabye you can try to remove this and see if the shader works (it doesn't do anything special, so hopefully it works.

    On mobiles I reccomend you use the mesh-text replacement rather than the gui-text replacement, as the latter has not at all been optimized with respect to draw call count, and will probably hurt your performance significantly.

    cheers,
    Tore
     
  46. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    Thanks for the reply.
    It would be sooo great if I could understand the logic behind shaders. Anyway, by applying million monkeys typing techniques (ie keep typing until you see what you wish) I made a shader that works on the Iphone. It is plain, no shadows or outlines yet. Hopefully they will follow later. Any hints on how to add break lines to the custom textMesh? Or how to put the letters along a curve ? I would appreciate any advice.
    Here is the code:
    Code (csharp):
    1.  
    2. // Created by Ippokratis Bournellis
    3. //
    4. //  Site------------------> http://ippomed.com/unity/
    5. //  Batching Tools----> http://u3d.as/content/ippokratis-bournellis/batching-tools/2tT
    6. //  Toon Shader------> http://u3d.as/content/ippokratis-bournellis/toon-shader/2R0
    7. //
    8. // Cheers :)
    9.  
    10. Shader "Ippo/Font/Plain" {
    11.   Properties {
    12.         _Color ("Main Color", Color) = (1,1,1,1)
    13.         _AlphaMin ("Alpha Min", Range(0.0,1.0)) = 0.49
    14.         _AlphaMax ("Alpha Max", Range(0.0,1.0)) = 0.54
    15.         _MainTex ("Base (RGB)", 2D) = "white" {}
    16.     }
    17. SubShader {
    18. Tags {
    19.             "Queue"="Transparent"
    20.             "IgnoreProjector"="True"
    21.             "RenderType"="Transparent"
    22.         }
    23.         Lighting Off
    24.         Cull Off
    25.         ZTest Always
    26.         ZWrite Off
    27.         Fog {
    28.             Mode Off
    29.         }
    30.         Blend SrcAlpha OneMinusSrcAlpha
    31.     Pass {
    32.  
    33. CGPROGRAM
    34. #pragma vertex vert
    35. #pragma fragment frag
    36. #include "UnityCG.cginc"
    37.  
    38. sampler2D _MainTex;
    39. half4 _MainTex_ST;
    40. half4 _Color;
    41. half _AlphaMin;
    42. half _AlphaMax;
    43.  
    44. struct v2f {
    45.     half4 pos : SV_POSITION;
    46.     half2 uv: TEXCOORD0;
    47.     half3 color : COLOR0;
    48. };
    49.  
    50. v2f vert (appdata_base v)
    51. {
    52.     v2f o;
    53.     o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    54.     o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    55.     return o;
    56. }
    57.  
    58. half4 frag(v2f i) : COLOR
    59. {
    60.     half4 base = tex2D(_MainTex, i.uv);
    61.     half alpha = smoothstep(_AlphaMin, _AlphaMax, base.w);
    62.     half3 other = _Color * alpha;    
    63.     return half4 (other, alpha);
    64. }
    65. ENDCG
    66.  
    67.     }
    68. }
    69. Fallback "VertexLit"
    70. }  
     
    Last edited: Sep 7, 2012
  47. itech

    itech

    Joined:
    Jul 28, 2010
    Posts:
    139
    How can I create font in BitMap generator, I try this tool, and I import fnt file into Unity, then I right click on it and I chose import font, but in won't work , so I need help with this.
     
  48. itech

    itech

    Joined:
    Jul 28, 2010
    Posts:
    139
    In Bitmap font generator I do this actions :

    1) Options -> Font settings , I only chose Calibri font and I leave other values intact
    2) Export Options - > I change width and height to 512x512
    3) Save Bitmap Font as , I save fnt file on my local HDD
    4) Visualize

    I get this ?


    What I'm doing wrong !?
     

    Attached Files:

  49. itech

    itech

    Joined:
    Jul 28, 2010
    Posts:
    139
    Solved :)
     
  50. ant001

    ant001

    Joined:
    Dec 15, 2010
    Posts:
    116