├── .gitignore ├── LICENSE ├── Merlin ├── Scripts │ ├── MSDFAtlasGenerator.cs │ └── MSDFAtlasGenerator.cs.meta ├── Shaders │ ├── MSDFTMFont.shader │ ├── MSDFTMFont.shader.meta │ ├── MSDFUIFont.shader │ └── MSDFUIFont.shader.meta └── bin │ ├── msdfgen.exe │ └── msdfgen.exe.meta └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Mm]emoryCaptures/ 12 | 13 | # Asset meta data should only be ignored when the corresponding asset is also ignored 14 | !/[Aa]ssets/**/*.meta 15 | 16 | # Uncomment this line if you wish to ignore the asset store tools plugin 17 | # /[Aa]ssets/AssetStoreTools* 18 | 19 | # Autogenerated Jetbrains Rider plugin 20 | [Aa]ssets/Plugins/Editor/JetBrains* 21 | 22 | # Visual Studio cache directory 23 | .vs/ 24 | 25 | # Gradle cache directory 26 | .gradle/ 27 | 28 | # Autogenerated VS/MD/Consulo solution and project files 29 | ExportedObj/ 30 | .consulo/ 31 | *.csproj 32 | *.unityproj 33 | *.sln 34 | *.suo 35 | *.tmp 36 | *.user 37 | *.userprefs 38 | *.pidb 39 | *.booproj 40 | *.svd 41 | *.pdb 42 | *.mdb 43 | *.opendb 44 | *.VC.db 45 | 46 | # Unity3D generated meta files 47 | *.pidb.meta 48 | *.pdb.meta 49 | *.mdb.meta 50 | 51 | # Unity3D generated file on crash reports 52 | sysinfo.txt 53 | 54 | # Builds 55 | *.apk 56 | *.unitypackage 57 | 58 | # Crashlytics generated file 59 | crashlytics-build.properties 60 | 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Merlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Merlin/Scripts/MSDFAtlasGenerator.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * MIT License 4 | * 5 | * Copyright(c) 2019 Merlin 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #if UNITY_EDITOR 27 | 28 | using System.Collections; 29 | using System.Collections.Generic; 30 | using System.IO; 31 | using UnityEditor; 32 | using UnityEngine; 33 | 34 | public class MSDFAtlasGenerator : EditorWindow 35 | { 36 | public Font FontToConvert = null; 37 | 38 | public Texture2D AtlasToSave = null; 39 | public bool UseTextureCompression = false; 40 | 41 | private const string MSDFGenPath = "Assets/Merlin/MSDF/bin/msdfgen.exe"; 42 | private const string MSDFTempPath = "Assets/Merlin/MSDF/gen/glyph{0}.png"; 43 | 44 | [MenuItem("Window/Merlin/MSDF Font Generator")] 45 | public static void ShowWindow() 46 | { 47 | EditorWindow window = EditorWindow.GetWindow(typeof(MSDFAtlasGenerator)); 48 | window.maxSize = new Vector2(400, 200); 49 | } 50 | 51 | void OnGUI() 52 | { 53 | EditorGUILayout.LabelField("MSDF Atlas Generator", EditorStyles.boldLabel); 54 | 55 | FontToConvert = (Font)EditorGUILayout.ObjectField("Font Asset:", FontToConvert, typeof(Font), false); 56 | 57 | UseTextureCompression = EditorGUILayout.Toggle("Compress Font Atlas", UseTextureCompression); 58 | 59 | if (UseTextureCompression) 60 | { 61 | EditorGUILayout.HelpBox("Enabling compression can cause visible artifacts on text depending on the font. On most fonts the artifacts may make the text look wobbly along edges. Check to make sure artifacts do not appear when you enable this.", MessageType.Warning); 62 | } 63 | 64 | EditorGUI.BeginDisabledGroup(FontToConvert == null); 65 | if (GUILayout.Button("Generate Atlas")) 66 | { 67 | GenerateAtlas(); 68 | } 69 | EditorGUI.EndDisabledGroup(); 70 | 71 | //AtlasToSave = (Texture2D)EditorGUILayout.ObjectField("Texture to save:", AtlasToSave, typeof(Texture2D), false); 72 | 73 | //if (GUILayout.Button("Save Atlas to PNG")) 74 | //{ 75 | // SaveToPNG(); 76 | //} 77 | } 78 | 79 | private void SaveToPNG() 80 | { 81 | string assetPath = AssetDatabase.GetAssetPath(AtlasToSave).Replace(".asset", ".png"); 82 | 83 | File.WriteAllBytes(assetPath, ImageConversion.EncodeToPNG(AtlasToSave)); 84 | } 85 | 86 | private void GenerateAtlas() 87 | { 88 | TrueTypeFontImporter fontImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(FontToConvert)) as TrueTypeFontImporter; 89 | 90 | if (fontImporter == null) 91 | Debug.LogError("Could not import mesh asset! Builtin Unity fonts like Arial don't work unless you put them in the project directory!"); 92 | 93 | fontImporter.characterSpacing = 4; 94 | fontImporter.characterPadding = 2; 95 | 96 | fontImporter.SaveAndReimport(); 97 | 98 | // Hacky method to get the generated font texture so that we can figure out where to put pixels 99 | Texture2D fontTexture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(FontToConvert)); 100 | 101 | Dictionary characterGlyphMap = new Dictionary(); 102 | 103 | CharacterInfo[] characterInfos = FontToConvert.characterInfo; 104 | 105 | Texture2D newAtlas = new Texture2D(fontTexture.width, fontTexture.height, TextureFormat.ARGB32, false, true); 106 | for (int x = 0; x < newAtlas.width; ++x) 107 | { 108 | for (int y = 0; y < newAtlas.height; ++y) 109 | { 110 | newAtlas.SetPixel(x, y, Color.black); 111 | } 112 | } 113 | 114 | int charCount = 0; 115 | 116 | foreach (CharacterInfo info in characterInfos) 117 | { 118 | charCount++; 119 | 120 | EditorUtility.DisplayProgressBar("Generating MSDF Atlas...", string.Format("Glyph {0}/{1}", charCount, characterInfos.Length), charCount / (float)characterInfos.Length); 121 | 122 | Texture2D currentGlyphTex = GenerateGlyphTexture(info.index, info.glyphWidth, info.glyphHeight); 123 | 124 | if (currentGlyphTex == null) 125 | continue; 126 | 127 | for (int x = 0; x < currentGlyphTex.width; ++x) 128 | { 129 | for (int y = 0; y < currentGlyphTex.height; ++y) 130 | { 131 | float progressX = (x) / (float)(currentGlyphTex.width); 132 | float progressY = (y) / (float)(currentGlyphTex.height); 133 | 134 | float uvProgressX = Mathf.Lerp(info.uvTopLeft.x, info.uvTopRight.x, progressX) * fontTexture.width; 135 | float uvProgressY = Mathf.Lerp(info.uvBottomLeft.y, info.uvTopLeft.y, progressY) * fontTexture.height; 136 | 137 | // flipped iS dEpRiCaTeD uSiNg ThE Uv WiLl bE CoNSiStEnT. It's not consistent in my limited experience. 138 | // Maybe I'm doing something wrong, but I don't want to try fighting with Unity trying to fix an issue that may be on their end.. I've wasted enough time on trusting Unity to do things correctly. 139 | #pragma warning disable 0618 140 | if (info.flipped) 141 | #pragma warning restore 0618 142 | { 143 | uvProgressY = Mathf.Lerp(info.uvTopLeft.y, info.uvTopRight.y, progressX) * fontTexture.height; 144 | uvProgressX = Mathf.Lerp(info.uvBottomLeft.x, info.uvTopLeft.x, progressY) * fontTexture.width; 145 | } 146 | 147 | 148 | int targetX = Mathf.RoundToInt(uvProgressX); 149 | int targetY = Mathf.RoundToInt(uvProgressY) - 1; 150 | 151 | Color glyphCol = currentGlyphTex.GetPixel(x, y); 152 | 153 | newAtlas.SetPixel(targetX, targetY, glyphCol); 154 | } 155 | } 156 | 157 | } 158 | 159 | newAtlas.Apply(false); 160 | 161 | if (UseTextureCompression) 162 | { 163 | EditorUtility.DisplayProgressBar("Generating MSDF Atlas...", "Compressing Atlas...", 1f); 164 | EditorUtility.CompressTexture(newAtlas, TextureFormat.BC7, UnityEditor.TextureCompressionQuality.Best); 165 | } 166 | 167 | EditorUtility.ClearProgressBar(); 168 | 169 | string fontPath = AssetDatabase.GetAssetPath(FontToConvert); 170 | string savePath = Path.Combine(Path.GetDirectoryName(fontPath), Path.GetFileNameWithoutExtension(fontPath) + "_msdfAtlas.asset"); 171 | 172 | AssetDatabase.CreateAsset(newAtlas, savePath); 173 | 174 | EditorGUIUtility.PingObject(newAtlas); 175 | } 176 | 177 | private Texture2D GenerateGlyphTexture(int UTFChar, int glyphWidth, int glyphHeight) 178 | { 179 | System.Diagnostics.Process msdfProcess = new System.Diagnostics.Process(); 180 | 181 | msdfProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 182 | msdfProcess.StartInfo.CreateNoWindow = true; 183 | msdfProcess.StartInfo.UseShellExecute = false; 184 | msdfProcess.StartInfo.FileName = Path.GetFullPath(MSDFGenPath); 185 | msdfProcess.EnableRaisingEvents = true; 186 | 187 | string fontPath = Path.GetFullPath(AssetDatabase.GetAssetPath(FontToConvert)); 188 | //string glyphLocalPath = string.Format(MSDFTempPath, UTFChar); 189 | string glyphLocalPath = string.Format(MSDFTempPath, 0); 190 | string glyphPath = Path.GetFullPath(glyphLocalPath); 191 | 192 | Directory.CreateDirectory(Path.GetDirectoryName(string.Format(MSDFTempPath, 0))); 193 | string argStr = string.Format("msdf -o \"{0}\" -font \"{1}\" {4} -size {2} {3} -pxrange 4 -autoframe", glyphPath, fontPath, glyphWidth, glyphHeight, UTFChar); 194 | 195 | msdfProcess.StartInfo.Arguments = argStr; 196 | 197 | msdfProcess.Start(); 198 | msdfProcess.WaitForExit(); 199 | 200 | if (!File.Exists(glyphLocalPath)) 201 | { 202 | Debug.LogWarning("Could not load glyph " + UTFChar); 203 | return null; 204 | } 205 | 206 | Texture2D loadedGlyph = new Texture2D(glyphWidth, glyphHeight); 207 | ImageConversion.LoadImage(loadedGlyph, File.ReadAllBytes(glyphLocalPath), false); 208 | 209 | return loadedGlyph; 210 | 211 | #if false // Old import code that bounces through the Asset Database so it's much slower. 212 | AssetDatabase.ImportAsset(glyphLocalPath); 213 | 214 | TextureImporter glyphImporter = AssetImporter.GetAtPath(glyphLocalPath) as TextureImporter; 215 | 216 | if (glyphImporter != null) 217 | { 218 | glyphImporter.npotScale = TextureImporterNPOTScale.None; 219 | glyphImporter.sRGBTexture = false; 220 | glyphImporter.mipmapEnabled = false; 221 | glyphImporter.textureCompression = TextureImporterCompression.Uncompressed; 222 | glyphImporter.isReadable = true; 223 | 224 | glyphImporter.SaveAndReimport(); 225 | } 226 | else 227 | { 228 | Debug.LogWarning("Failed to import glyph " + glyphPath); 229 | } 230 | 231 | return AssetDatabase.LoadAssetAtPath(glyphLocalPath); 232 | #endif 233 | } 234 | } 235 | 236 | #endif 237 | 238 | -------------------------------------------------------------------------------- /Merlin/Scripts/MSDFAtlasGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33562808359b8f94f9f44aff33d1edfd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Merlin/Shaders/MSDFTMFont.shader: -------------------------------------------------------------------------------- 1 | Shader "Merlin/MSDF Text Mesh Font" 2 | { 3 | Properties 4 | { 5 | [HideInInspector]_MainTex ("Texture", 2D) = "white" {} 6 | [HDR]_Color("Text Color", Color) = (1, 1, 1, 1) 7 | [NoScaleOffset]_MSDFTex("MSDF Texture", 2D) = "black" {} 8 | [HideIninspector]_PixelRange("Pixel Range", Float) = 4.0 9 | } 10 | SubShader 11 | { 12 | Tags { "RenderType"="Cutout" "Queue"="AlphaTest" 13 | "IgnoreProjector" = "True" } 14 | 15 | Pass 16 | { 17 | //Blend SrcAlpha OneMinusSrcAlpha 18 | AlphaToMask On 19 | 20 | CGPROGRAM 21 | #pragma vertex vert 22 | #pragma fragment frag 23 | 24 | #include "UnityCG.cginc" 25 | 26 | struct appdata 27 | { 28 | float4 vertex : POSITION; 29 | float4 color : COLOR; 30 | float2 uv : TEXCOORD0; 31 | }; 32 | 33 | struct v2f 34 | { 35 | float4 vertex : SV_POSITION; 36 | float4 color : COLOR; 37 | float2 uv : TEXCOORD0; 38 | }; 39 | 40 | float4 _Color; 41 | sampler2D _MSDFTex; float4 _MSDFTex_TexelSize; 42 | float _PixelRange; 43 | 44 | float median(float r, float g, float b) 45 | { 46 | return max(min(r, g), min(max(r, g), b)); 47 | } 48 | 49 | v2f vert (appdata v) 50 | { 51 | v2f o; 52 | o.vertex = UnityObjectToClipPos(v.vertex); 53 | o.uv = v.uv; 54 | o.color = v.color * _Color; 55 | 56 | return o; 57 | } 58 | 59 | float4 frag (v2f i) : SV_Target 60 | { 61 | float2 msdfUnit = _PixelRange / _MSDFTex_TexelSize.zw; 62 | 63 | float4 sampleCol = tex2D(_MSDFTex, i.uv); 64 | float sigDist = median(sampleCol.r, sampleCol.g, sampleCol.b) - 0.5; 65 | sigDist *= max(dot(msdfUnit, 0.5/fwidth(i.uv)), 1); // Max to handle fading out to quads in the distance 66 | float opacity = clamp(sigDist + 0.5, 0.0, 1.0); 67 | float4 color = float4(i.color.rgb, i.color.a * opacity); 68 | 69 | clip(color.a - 0.005); 70 | 71 | return color; 72 | } 73 | ENDCG 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Merlin/Shaders/MSDFTMFont.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7be749266c0a5c846b04c626cd19aade 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Merlin/Shaders/MSDFUIFont.shader: -------------------------------------------------------------------------------- 1 | // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt) 2 | // Modified to support MSDF fonts - Merlin, also MIT license 3 | 4 | Shader "Merlin/UI/MSDF UI Font" 5 | { 6 | Properties 7 | { 8 | [HideInInspector]_MainTex ("Sprite Texture", 2D) = "white" {} 9 | [HDR]_Color ("Tint", Color) = (1,1,1,1) 10 | 11 | [NoScaleOffset]_MSDFTex("MSDF Texture", 2D) = "black" {} 12 | [HideInInspector]_PixelRange("Pixel Range", Float) = 4.0 13 | 14 | _StencilComp ("Stencil Comparison", Float) = 8 15 | _Stencil ("Stencil ID", Float) = 0 16 | _StencilOp ("Stencil Operation", Float) = 0 17 | _StencilWriteMask ("Stencil Write Mask", Float) = 255 18 | _StencilReadMask ("Stencil Read Mask", Float) = 255 19 | 20 | _ColorMask ("Color Mask", Float) = 15 21 | 22 | [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 1 23 | } 24 | 25 | SubShader 26 | { 27 | Tags 28 | { 29 | "Queue"="Transparent" 30 | "IgnoreProjector"="True" 31 | "RenderType"="Transparent" 32 | "PreviewType"="Plane" 33 | "CanUseSpriteAtlas"="True" 34 | } 35 | 36 | Stencil 37 | { 38 | Ref [_Stencil] 39 | Comp [_StencilComp] 40 | Pass [_StencilOp] 41 | ReadMask [_StencilReadMask] 42 | WriteMask [_StencilWriteMask] 43 | } 44 | 45 | Cull Off 46 | Lighting Off 47 | ZWrite Off 48 | ZTest [unity_GUIZTestMode] 49 | Blend SrcAlpha OneMinusSrcAlpha 50 | ColorMask [_ColorMask] 51 | 52 | Pass 53 | { 54 | Name "Default" 55 | CGPROGRAM 56 | #pragma vertex vert 57 | #pragma fragment frag 58 | 59 | // Target 3.5 for centroid support on OpenGL ES 60 | #pragma target 3.5 61 | 62 | #include "UnityCG.cginc" 63 | #include "UnityUI.cginc" 64 | 65 | #pragma multi_compile __ UNITY_UI_CLIP_RECT 66 | #pragma multi_compile __ UNITY_UI_ALPHACLIP 67 | 68 | struct appdata_t 69 | { 70 | float4 vertex : POSITION; 71 | float4 color : COLOR; 72 | float2 texcoord : TEXCOORD0; 73 | }; 74 | 75 | struct v2f 76 | { 77 | float4 vertex : SV_POSITION; 78 | fixed4 color : COLOR; 79 | float2 texcoord : TEXCOORD0; 80 | float4 worldPosition : TEXCOORD1; 81 | UNITY_VERTEX_OUTPUT_STEREO 82 | }; 83 | 84 | float4 _Color; 85 | float4 _TextureSampleAdd; 86 | float4 _ClipRect; 87 | sampler2D _MSDFTex; float4 _MSDFTex_TexelSize; 88 | float _PixelRange; 89 | 90 | v2f vert(appdata_t v, uint vertID : SV_VertexID) 91 | { 92 | v2f OUT; 93 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); 94 | OUT.worldPosition = v.vertex; 95 | OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); 96 | 97 | OUT.color = v.color * _Color; 98 | OUT.texcoord = v.texcoord; 99 | 100 | return OUT; 101 | } 102 | 103 | sampler2D _MainTex; 104 | 105 | float median(float r, float g, float b) 106 | { 107 | return max(min(r, g), min(max(r, g), b)); 108 | } 109 | 110 | 111 | half4 frag(v2f IN) : SV_Target 112 | { 113 | float2 texcoord = IN.texcoord; 114 | 115 | float2 msdfUnit = _PixelRange / _MSDFTex_TexelSize.zw; 116 | 117 | float4 sampleCol = tex2D(_MSDFTex, texcoord); 118 | float sigDist = median(sampleCol.r, sampleCol.g, sampleCol.b) - 0.5; 119 | sigDist *= max(dot(msdfUnit, 0.5 / fwidth(texcoord)), 1); // Max to handle fading out to quads in the distance 120 | float opacity = clamp(sigDist + 0.5, 0.0, 1.0); 121 | float4 color = float4(1, 1, 1, opacity); 122 | 123 | color += _TextureSampleAdd; 124 | color *= IN.color; 125 | 126 | #ifdef UNITY_UI_CLIP_RECT 127 | color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); 128 | #endif 129 | 130 | #ifdef UNITY_UI_ALPHACLIP 131 | clip (color.a - 0.001); 132 | #endif 133 | 134 | return color; 135 | } 136 | ENDCG 137 | } 138 | } 139 | 140 | Fallback "UI/Default" 141 | } 142 | -------------------------------------------------------------------------------- /Merlin/Shaders/MSDFUIFont.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 42e41b6643ef1ea4bbe08766df1741cf 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Merlin/bin/msdfgen.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MerlinVR/Unity-MSDF-Fonts/3ca9e8f49858e34a7e83d8a74eb030b248d9739d/Merlin/bin/msdfgen.exe -------------------------------------------------------------------------------- /Merlin/bin/msdfgen.exe.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c5269deeaab55d4a981f307bb404a6d 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-MSDF-Fonts 2 | ### Basic tool to convert Unity fonts to use Multichannel Signed Distance Field fonts 3 | 4 | Multichannel SDF font rendering provides much sharper text rendering that maintains its sharpness without pixelization no matter how large the text is. 5 | 6 | This just takes Unity fonts and converts them to MSDF fonts using [MSDFGen](https://github.com/Chlumsky/msdfgen). At the moment this is a hack with a few hours of work and will probably be extended into a more useful thing in the future if people care enough. 7 | 8 | This tool is made for static fonts that are baked out in the editor. It was mostly made targeted at VRChat worlds because Text Mesh Pro is fairly broken in VRChat. The main advantage that multi channel SDFs have is that they can maintain corners. Normal SDFs will usually get rounded corners on text. The MSDFGen github has a good example comparting to regular SDFs https://github.com/Chlumsky/msdfgen. 9 | 10 | #### Default Unity Text Rendering 11 | 12 | 13 | #### MSDF Text Rendering 14 | 15 | 16 | ### Usage 17 | 1. Install the package from the releases page 18 | 2. Find a font that you like and use it on UI Text or Text Meshes 19 | 3. Select the font asset and change the font size to somewhere between 30 and 60 and change the Character from Default to ASCII Default Set and click the Apply button. **This step is important, if you do not change the character, the atlas generator will not know what to generate!** If you need to support non-latin languages, you will need to change the Character to UTF and use a font that supports the extra characters. At some point I might make the script look at what characters are used in the scene to find the necessary characters. 20 | 4. Open the atlas generator under the Window drop down Merlin > MSDF Font Generator 21 | 5. Drag the font asset into the Font Asset slot in the generator and click *Generate Atlas* 22 | 6. Once the atlas generation has finished, the new font atlas will be selected in your project files. 23 | 7. Make a new material for your text. If you are putting this on a Text Mesh, select the `Merlin/MSDF Text Mesh Font` shader. If it is UIText use the `Merlin/UI/MSDF UI Font` shader. 24 | 8. Drag the generated atlas texture into the MSDF Texture slot on the material. 25 | 9. Apply the new material to your UI Text or Text Meshes. 26 | 10. You should now have MSDF text on your text now. If you see artifacts that look like melting, go back to step 3 and increase the font size until the artifacts go away. 27 | --------------------------------------------------------------------------------