Search Unity

  1. Get the latest news, tutorials and offers directly to your inbox with our newsletters. Sign up now.
    Dismiss Notice

TextMesh Pro TextMesh Pro bitmap font outlines and shadows

Discussion in 'UGUI & TextMesh Pro' started by rempelj, Apr 23, 2017.

  1. rempelj

    rempelj

    Joined:
    Aug 3, 2013
    Posts:
    47
    What is the recommended solution for adding outlines or shadows to bitmap fonts (font assets created using the Hinted Raster setting)?

    I made a custom shader to do this in the meantime, but my shader skills are lacking. The shader uses a total of 5 passes to render the text 5 times with different offsets applied to the first 4 passes. Obviously this is not good for performance, and it's very limited in terms of customization.



    Attached is an image of what I'm working with below. I would love to see support for outlines and shadows in the material settings for the default bitmap font shader!

     
  2. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    5,683
    The ability to dynamically style the text in TextMesh Pro is a bi-product of the use of Signed Distance Field and associated shaders. When using SDF Font Assets, adding outline / shadows is pretty efficient.

    Adding shadow and outline to bitmap fonts is much more complex and not currently supported as it is not very efficient.
     
  3. rempelj

    rempelj

    Joined:
    Aug 3, 2013
    Posts:
    47
    Unfortunately I can't use SDF in this case because I need a crisp 12pt font and Hinted Raster seems to be the only way to achieve that.

    I was using Unity's Outline component with the UGUI Text component before I switched to TMP. I need to replace it with something.

    PS. TMP has been a lifesaver on many levels, especially with the fallback system for localization. Excited to make use of the native Unity integration.

    ---

    To anyone who stumbles into this thread and wants to use the shader: Here it is. Be warned it is a terrible hack job. If you improve it, please share.

    Code (csharp):
    1. Shader "TextMeshPro/BitmapShadow" {
    2.  
    3. Properties {
    4.     _MainTex        ("Font Atlas", 2D) = "white" {}
    5.     _FaceTex        ("Font Texture", 2D) = "white" {}
    6.     _FaceColor        ("Text Color", Color) = (1,1,1,1)
    7.  
    8.     _ShadowColor    ("Shadow Color", Color) = (0,0,0,0)
    9.     _Shadow1X        ("Shadow 1X", Float) = 0
    10.     _Shadow1Y        ("Shadow 1Y", Float) = 0
    11.     _Shadow2X        ("Shadow 2X", Float) = 0
    12.     _Shadow2Y        ("Shadow 2Y", Float) = 0
    13.     _Shadow3X        ("Shadow 1X", Float) = 0
    14.     _Shadow3Y        ("Shadow 1Y", Float) = 0
    15.     _Shadow4X        ("Shadow 2X", Float) = 0
    16.     _Shadow4Y        ("Shadow 2Y", Float) = 0
    17.  
    18.     _VertexOffsetX    ("Vertex OffsetX", float) = 0
    19.     _VertexOffsetY    ("Vertex OffsetY", float) = 0
    20.     _MaskSoftnessX    ("Mask SoftnessX", float) = 0
    21.     _MaskSoftnessY    ("Mask SoftnessY", float) = 0
    22.  
    23.     _ClipRect("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
    24.  
    25.     _StencilComp("Stencil Comparison", Float) = 8
    26.     _Stencil("Stencil ID", Float) = 0
    27.     _StencilOp("Stencil Operation", Float) = 0
    28.     _StencilWriteMask("Stencil Write Mask", Float) = 255
    29.     _StencilReadMask("Stencil Read Mask", Float) = 255
    30.  
    31.     _ColorMask("Color Mask", Float) = 15
    32. }
    33.  
    34. SubShader{
    35.  
    36.     Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
    37.  
    38.     Stencil
    39.     {
    40.         Ref[_Stencil]
    41.         Comp[_StencilComp]
    42.         Pass[_StencilOp]
    43.         ReadMask[_StencilReadMask]
    44.         WriteMask[_StencilWriteMask]
    45.     }
    46.  
    47.  
    48.     Lighting Off
    49.     Cull [_CullMode]
    50.     ZTest [unity_GUIZTestMode]
    51.     ZWrite Off
    52.     Fog { Mode Off }
    53.     Blend SrcAlpha OneMinusSrcAlpha
    54.     ColorMask[_ColorMask]
    55.  
    56.  
    57.     Pass {
    58.         CGPROGRAM
    59.         #pragma vertex vert
    60.         #pragma fragment frag
    61.  
    62.         #include "UnityCG.cginc"
    63.  
    64.  
    65.     #if UNITY_VERSION < 530
    66.         bool _UseClipRect;
    67.     #endif
    68.  
    69.         struct appdata_t {
    70.             float4 vertex        : POSITION;
    71.             fixed4 color        : COLOR;
    72.             float2 texcoord0    : TEXCOORD0;
    73.             float2 texcoord1    : TEXCOORD1;
    74.         };
    75.  
    76.         struct v2f {
    77.             float4    vertex        : POSITION;
    78.             fixed4    color        : COLOR;
    79.             float2    texcoord0    : TEXCOORD0;
    80.             float2    texcoord1    : TEXCOORD1;
    81.             float4    mask        : TEXCOORD2;
    82.         };
    83.  
    84.         uniform    sampler2D     _MainTex;
    85.         uniform    sampler2D     _FaceTex;
    86.         uniform float4        _FaceTex_ST;
    87.         uniform    fixed4        _FaceColor;
    88.  
    89.         uniform    fixed4        _ShadowColor;
    90.         uniform float        _Shadow1X;
    91.         uniform float        _Shadow1Y;
    92.         uniform float        _Shadow2X;
    93.         uniform float        _Shadow2Y;
    94.         uniform float        _Shadow3X;
    95.         uniform float        _Shadow3Y;
    96.         uniform float        _Shadow4X;
    97.         uniform float        _Shadow4Y;
    98.  
    99.         uniform float        _VertexOffsetX;
    100.         uniform float        _VertexOffsetY;
    101.         uniform float4        _ClipRect;
    102.         uniform float        _MaskSoftnessX;
    103.         uniform float        _MaskSoftnessY;
    104.  
    105.         float2 UnpackUV(float uv)
    106.         {
    107.             float2 output;
    108.             output.x = floor(uv / 4096);
    109.             output.y = uv - 4096 * output.x;
    110.  
    111.             return output * 0.001953125;
    112.         }
    113.  
    114.         v2f vert (appdata_t i)
    115.         {
    116.             float4 vert = i.vertex;
    117.             vert.x += _VertexOffsetX + _Shadow1X;
    118.             vert.y += _VertexOffsetY + _Shadow1Y;
    119.  
    120.             vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
    121.  
    122.             float4 vPosition = UnityPixelSnap(mul(UNITY_MATRIX_MVP, vert));
    123.  
    124.             fixed4 faceColor = i.color;
    125.             faceColor *= _ShadowColor;
    126.  
    127.             v2f o;
    128.             o.vertex = vPosition;
    129.             o.color = faceColor;
    130.             o.texcoord0 = i.texcoord0;
    131.             o.texcoord1 = TRANSFORM_TEX(UnpackUV(i.texcoord1), _FaceTex);
    132.             float2 pixelSize = vPosition.w;
    133.             pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
    134.  
    135.             // Clamp _ClipRect to 16bit.
    136.             float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
    137.             o.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
    138.          
    139.             return o;
    140.         }
    141.  
    142.         fixed4 frag (v2f i) : COLOR
    143.         {
    144.             //fixed4 c = tex2D(_MainTex, i.texcoord0) * tex2D(_FaceTex, i.texcoord1) * i.color;
    145.          
    146.             fixed4 c = tex2D(_MainTex, i.texcoord0);
    147.             c = fixed4 (tex2D(_FaceTex, i.texcoord1).rgb * i.color.rgb, i.color.a * c.a);
    148.  
    149.             #if UNITY_VERSION < 530
    150.                 if (_UseClipRect)
    151.                 {
    152.                     // Alternative implementation to UnityGet2DClipping with support for softness.
    153.                     half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    154.                     c *= m.x * m.y;
    155.                 }
    156.             #else
    157.                 // Alternative implementation to UnityGet2DClipping with support for softness.
    158.                 half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    159.                 c *= m.x * m.y;
    160.             #endif
    161.  
    162.             return c;
    163.         }
    164.         ENDCG
    165.     }
    166.  
    167.  
    168.     Pass {
    169.         CGPROGRAM
    170.         #pragma vertex vert
    171.         #pragma fragment frag
    172.  
    173.         #include "UnityCG.cginc"
    174.  
    175.  
    176.     #if UNITY_VERSION < 530
    177.         bool _UseClipRect;
    178.     #endif
    179.  
    180.         struct appdata_t {
    181.             float4 vertex        : POSITION;
    182.             fixed4 color        : COLOR;
    183.             float2 texcoord0    : TEXCOORD0;
    184.             float2 texcoord1    : TEXCOORD1;
    185.         };
    186.  
    187.         struct v2f {
    188.             float4    vertex        : POSITION;
    189.             fixed4    color        : COLOR;
    190.             float2    texcoord0    : TEXCOORD0;
    191.             float2    texcoord1    : TEXCOORD1;
    192.             float4    mask        : TEXCOORD2;
    193.         };
    194.  
    195.         uniform    sampler2D     _MainTex;
    196.         uniform    sampler2D     _FaceTex;
    197.         uniform float4        _FaceTex_ST;
    198.         uniform    fixed4        _FaceColor;
    199.  
    200.         uniform    fixed4        _ShadowColor;
    201.         uniform float        _Shadow1X;
    202.         uniform float        _Shadow1Y;
    203.         uniform float        _Shadow2X;
    204.         uniform float        _Shadow2Y;
    205.         uniform float        _Shadow3X;
    206.         uniform float        _Shadow3Y;
    207.         uniform float        _Shadow4X;
    208.         uniform float        _Shadow4Y;
    209.  
    210.         uniform float        _VertexOffsetX;
    211.         uniform float        _VertexOffsetY;
    212.         uniform float4        _ClipRect;
    213.         uniform float        _MaskSoftnessX;
    214.         uniform float        _MaskSoftnessY;
    215.  
    216.         float2 UnpackUV(float uv)
    217.         {
    218.             float2 output;
    219.             output.x = floor(uv / 4096);
    220.             output.y = uv - 4096 * output.x;
    221.  
    222.             return output * 0.001953125;
    223.         }
    224.  
    225.         v2f vert (appdata_t i)
    226.         {
    227.             float4 vert = i.vertex;
    228.             vert.x += _VertexOffsetX + _Shadow2X;
    229.             vert.y += _VertexOffsetY + _Shadow2Y;
    230.  
    231.             vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
    232.  
    233.             float4 vPosition = UnityPixelSnap(mul(UNITY_MATRIX_MVP, vert));
    234.  
    235.             fixed4 faceColor = i.color;
    236.             faceColor *= _ShadowColor;
    237.  
    238.             v2f o;
    239.             o.vertex = vPosition;
    240.             o.color = faceColor;
    241.             o.texcoord0 = i.texcoord0;
    242.             o.texcoord1 = TRANSFORM_TEX(UnpackUV(i.texcoord1), _FaceTex);
    243.             float2 pixelSize = vPosition.w;
    244.             pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
    245.  
    246.             // Clamp _ClipRect to 16bit.
    247.             float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
    248.             o.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
    249.          
    250.             return o;
    251.         }
    252.  
    253.         fixed4 frag (v2f i) : COLOR
    254.         {
    255.             //fixed4 c = tex2D(_MainTex, i.texcoord0) * tex2D(_FaceTex, i.texcoord1) * i.color;
    256.          
    257.             fixed4 c = tex2D(_MainTex, i.texcoord0);
    258.             c = fixed4 (tex2D(_FaceTex, i.texcoord1).rgb * i.color.rgb, i.color.a * c.a);
    259.  
    260.             #if UNITY_VERSION < 530
    261.                 if (_UseClipRect)
    262.                 {
    263.                     // Alternative implementation to UnityGet2DClipping with support for softness.
    264.                     half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    265.                     c *= m.x * m.y;
    266.                 }
    267.             #else
    268.                 // Alternative implementation to UnityGet2DClipping with support for softness.
    269.                 half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    270.                 c *= m.x * m.y;
    271.             #endif
    272.  
    273.             return c;
    274.         }
    275.         ENDCG
    276.     }
    277.  
    278.  
    279.  
    280.     Pass {
    281.         CGPROGRAM
    282.         #pragma vertex vert
    283.         #pragma fragment frag
    284.  
    285.         #include "UnityCG.cginc"
    286.  
    287.  
    288.     #if UNITY_VERSION < 530
    289.         bool _UseClipRect;
    290.     #endif
    291.  
    292.         struct appdata_t {
    293.             float4 vertex        : POSITION;
    294.             fixed4 color        : COLOR;
    295.             float2 texcoord0    : TEXCOORD0;
    296.             float2 texcoord1    : TEXCOORD1;
    297.         };
    298.  
    299.         struct v2f {
    300.             float4    vertex        : POSITION;
    301.             fixed4    color        : COLOR;
    302.             float2    texcoord0    : TEXCOORD0;
    303.             float2    texcoord1    : TEXCOORD1;
    304.             float4    mask        : TEXCOORD2;
    305.         };
    306.  
    307.         uniform    sampler2D     _MainTex;
    308.         uniform    sampler2D     _FaceTex;
    309.         uniform float4        _FaceTex_ST;
    310.         uniform    fixed4        _FaceColor;
    311.  
    312.         uniform    fixed4        _ShadowColor;
    313.         uniform float        _Shadow1X;
    314.         uniform float        _Shadow1Y;
    315.         uniform float        _Shadow2X;
    316.         uniform float        _Shadow2Y;
    317.         uniform float        _Shadow3X;
    318.         uniform float        _Shadow3Y;
    319.         uniform float        _Shadow4X;
    320.         uniform float        _Shadow4Y;
    321.  
    322.         uniform float        _VertexOffsetX;
    323.         uniform float        _VertexOffsetY;
    324.         uniform float4        _ClipRect;
    325.         uniform float        _MaskSoftnessX;
    326.         uniform float        _MaskSoftnessY;
    327.  
    328.         float2 UnpackUV(float uv)
    329.         {
    330.             float2 output;
    331.             output.x = floor(uv / 4096);
    332.             output.y = uv - 4096 * output.x;
    333.  
    334.             return output * 0.001953125;
    335.         }
    336.  
    337.         v2f vert (appdata_t i)
    338.         {
    339.             float4 vert = i.vertex;
    340.             vert.x += _VertexOffsetX + _Shadow3X;
    341.             vert.y += _VertexOffsetY + _Shadow3Y;
    342.  
    343.             vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
    344.  
    345.             float4 vPosition = UnityPixelSnap(mul(UNITY_MATRIX_MVP, vert));
    346.  
    347.             fixed4 faceColor = i.color;
    348.             faceColor *= _ShadowColor;
    349.  
    350.             v2f o;
    351.             o.vertex = vPosition;
    352.             o.color = faceColor;
    353.             o.texcoord0 = i.texcoord0;
    354.             o.texcoord1 = TRANSFORM_TEX(UnpackUV(i.texcoord1), _FaceTex);
    355.             float2 pixelSize = vPosition.w;
    356.             pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
    357.  
    358.             // Clamp _ClipRect to 16bit.
    359.             float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
    360.             o.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
    361.          
    362.             return o;
    363.         }
    364.  
    365.         fixed4 frag (v2f i) : COLOR
    366.         {
    367.             //fixed4 c = tex2D(_MainTex, i.texcoord0) * tex2D(_FaceTex, i.texcoord1) * i.color;
    368.          
    369.             fixed4 c = tex2D(_MainTex, i.texcoord0);
    370.             c = fixed4 (tex2D(_FaceTex, i.texcoord1).rgb * i.color.rgb, i.color.a * c.a);
    371.  
    372.             #if UNITY_VERSION < 530
    373.                 if (_UseClipRect)
    374.                 {
    375.                     // Alternative implementation to UnityGet2DClipping with support for softness.
    376.                     half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    377.                     c *= m.x * m.y;
    378.                 }
    379.             #else
    380.                 // Alternative implementation to UnityGet2DClipping with support for softness.
    381.                 half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    382.                 c *= m.x * m.y;
    383.             #endif
    384.  
    385.             return c;
    386.         }
    387.         ENDCG
    388.     }
    389.  
    390.  
    391.  
    392.     Pass {
    393.         CGPROGRAM
    394.         #pragma vertex vert
    395.         #pragma fragment frag
    396.  
    397.         #include "UnityCG.cginc"
    398.  
    399.  
    400.     #if UNITY_VERSION < 530
    401.         bool _UseClipRect;
    402.     #endif
    403.  
    404.         struct appdata_t {
    405.             float4 vertex        : POSITION;
    406.             fixed4 color        : COLOR;
    407.             float2 texcoord0    : TEXCOORD0;
    408.             float2 texcoord1    : TEXCOORD1;
    409.         };
    410.  
    411.         struct v2f {
    412.             float4    vertex        : POSITION;
    413.             fixed4    color        : COLOR;
    414.             float2    texcoord0    : TEXCOORD0;
    415.             float2    texcoord1    : TEXCOORD1;
    416.             float4    mask        : TEXCOORD2;
    417.         };
    418.  
    419.         uniform    sampler2D     _MainTex;
    420.         uniform    sampler2D     _FaceTex;
    421.         uniform float4        _FaceTex_ST;
    422.         uniform    fixed4        _FaceColor;
    423.  
    424.         uniform    fixed4        _ShadowColor;
    425.         uniform float        _Shadow1X;
    426.         uniform float        _Shadow1Y;
    427.         uniform float        _Shadow2X;
    428.         uniform float        _Shadow2Y;
    429.         uniform float        _Shadow3X;
    430.         uniform float        _Shadow3Y;
    431.         uniform float        _Shadow4X;
    432.         uniform float        _Shadow4Y;
    433.  
    434.         uniform float        _VertexOffsetX;
    435.         uniform float        _VertexOffsetY;
    436.         uniform float4        _ClipRect;
    437.         uniform float        _MaskSoftnessX;
    438.         uniform float        _MaskSoftnessY;
    439.  
    440.         float2 UnpackUV(float uv)
    441.         {
    442.             float2 output;
    443.             output.x = floor(uv / 4096);
    444.             output.y = uv - 4096 * output.x;
    445.  
    446.             return output * 0.001953125;
    447.         }
    448.  
    449.         v2f vert (appdata_t i)
    450.         {
    451.             float4 vert = i.vertex;
    452.             vert.x += _VertexOffsetX + _Shadow4X;
    453.             vert.y += _VertexOffsetY + _Shadow4Y;
    454.  
    455.             vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
    456.  
    457.             float4 vPosition = UnityPixelSnap(mul(UNITY_MATRIX_MVP, vert));
    458.  
    459.             fixed4 faceColor = i.color;
    460.             faceColor *= _ShadowColor;
    461.  
    462.             v2f o;
    463.             o.vertex = vPosition;
    464.             o.color = faceColor;
    465.             o.texcoord0 = i.texcoord0;
    466.             o.texcoord1 = TRANSFORM_TEX(UnpackUV(i.texcoord1), _FaceTex);
    467.             float2 pixelSize = vPosition.w;
    468.             pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
    469.  
    470.             // Clamp _ClipRect to 16bit.
    471.             float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
    472.             o.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
    473.          
    474.             return o;
    475.         }
    476.  
    477.         fixed4 frag (v2f i) : COLOR
    478.         {
    479.             //fixed4 c = tex2D(_MainTex, i.texcoord0) * tex2D(_FaceTex, i.texcoord1) * i.color;
    480.          
    481.             fixed4 c = tex2D(_MainTex, i.texcoord0);
    482.             c = fixed4 (tex2D(_FaceTex, i.texcoord1).rgb * i.color.rgb, i.color.a * c.a);
    483.  
    484.             #if UNITY_VERSION < 530
    485.                 if (_UseClipRect)
    486.                 {
    487.                     // Alternative implementation to UnityGet2DClipping with support for softness.
    488.                     half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    489.                     c *= m.x * m.y;
    490.                 }
    491.             #else
    492.                 // Alternative implementation to UnityGet2DClipping with support for softness.
    493.                 half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    494.                 c *= m.x * m.y;
    495.             #endif
    496.  
    497.             return c;
    498.         }
    499.         ENDCG
    500.     }
    501.  
    502.  
    503.     Pass {
    504.         CGPROGRAM
    505.         #pragma vertex vert
    506.         #pragma fragment frag
    507.  
    508.         #include "UnityCG.cginc"
    509.  
    510.  
    511.     #if UNITY_VERSION < 530
    512.         bool _UseClipRect;
    513.     #endif
    514.  
    515.         struct appdata_t {
    516.             float4 vertex        : POSITION;
    517.             fixed4 color        : COLOR;
    518.             float2 texcoord0    : TEXCOORD0;
    519.             float2 texcoord1    : TEXCOORD1;
    520.         };
    521.  
    522.         struct v2f {
    523.             float4    vertex        : POSITION;
    524.             fixed4    color        : COLOR;
    525.             float2    texcoord0    : TEXCOORD0;
    526.             float2    texcoord1    : TEXCOORD1;
    527.             float4    mask        : TEXCOORD2;
    528.         };
    529.  
    530.         uniform    sampler2D     _MainTex;
    531.         uniform    sampler2D     _FaceTex;
    532.         uniform float4        _FaceTex_ST;
    533.         uniform    fixed4        _FaceColor;
    534.  
    535.         uniform float        _VertexOffsetX;
    536.         uniform float        _VertexOffsetY;
    537.         uniform float4        _ClipRect;
    538.         uniform float        _MaskSoftnessX;
    539.         uniform float        _MaskSoftnessY;
    540.  
    541.         float2 UnpackUV(float uv)
    542.         {
    543.             float2 output;
    544.             output.x = floor(uv / 4096);
    545.             output.y = uv - 4096 * output.x;
    546.  
    547.             return output * 0.001953125;
    548.         }
    549.  
    550.         v2f vert (appdata_t i)
    551.         {
    552.             float4 vert = i.vertex;
    553.             vert.x += _VertexOffsetX;
    554.             vert.y += _VertexOffsetY;
    555.  
    556.             vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
    557.  
    558.             float4 vPosition = UnityPixelSnap(mul(UNITY_MATRIX_MVP, vert));
    559.  
    560.             fixed4 faceColor = i.color;
    561.             faceColor *= _FaceColor;
    562.  
    563.             v2f o;
    564.             o.vertex = vPosition;
    565.             o.color = faceColor;
    566.             o.texcoord0 = i.texcoord0;
    567.             o.texcoord1 = TRANSFORM_TEX(UnpackUV(i.texcoord1), _FaceTex);
    568.             float2 pixelSize = vPosition.w;
    569.             pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
    570.  
    571.             // Clamp _ClipRect to 16bit.
    572.             float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
    573.             o.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
    574.          
    575.             return o;
    576.         }
    577.  
    578.         fixed4 frag (v2f i) : COLOR
    579.         {
    580.             //fixed4 c = tex2D(_MainTex, i.texcoord0) * tex2D(_FaceTex, i.texcoord1) * i.color;
    581.          
    582.             fixed4 c = tex2D(_MainTex, i.texcoord0);
    583.             c = fixed4 (tex2D(_FaceTex, i.texcoord1).rgb * i.color.rgb, i.color.a * c.a);
    584.  
    585.             #if UNITY_VERSION < 530
    586.                 if (_UseClipRect)
    587.                 {
    588.                     // Alternative implementation to UnityGet2DClipping with support for softness.
    589.                     half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    590.                     c *= m.x * m.y;
    591.                 }
    592.             #else
    593.                 // Alternative implementation to UnityGet2DClipping with support for softness.
    594.                 half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(i.mask.xy)) * i.mask.zw);
    595.                 c *= m.x * m.y;
    596.             #endif
    597.  
    598.             return c;
    599.         }
    600.         ENDCG
    601.     }
    602. }
    603.  
    604. }
    605.  
     
    rayaneflx, Avalin, tosiabunio and 2 others like this.
  4. Kreep

    Kreep

    Joined:
    Aug 1, 2013
    Posts:
    13
    Same problem here, we have to use Hinted Raster param to have a good looking pixel art font with TM Pro. Why don't you make a shader to have some parameters with Bitmap Fonts (like outline, underlay...) ? I understand it's not "very efficient" but when it's the only solution...

    Thanks rempelj for your shader, I will try it on my project.
     
    Last edited: May 2, 2018
  5. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    5,683
    The ability to dynamically add an Outline and Shadow (underlay) using the shader in TMP is a by product of using Signed Distance Field (SDF). This is why this functionality is not available on the bitmap shaders.
     
  6. Kreep

    Kreep

    Joined:
    Aug 1, 2013
    Posts:
    13
    Ok, but it is possible to have outlines and shadows by customizing the TMP_Bitmap shader, so why not adding them ? I'm not good at all when it comes to write shaders so I would love to have this included in TextMesh Pro. I love it so much, I really miss it on my pixel art fonts.

    PS : I'm really upset to see that I'm paying a subscription for Unity every month and the answer is always "we won't do anything", even when it's a critical bug for us (it's not the first time I experience that).
     
    Last edited: May 4, 2018
  7. Hi_ImTemmy

    Hi_ImTemmy

    Joined:
    Jul 8, 2015
    Posts:
    92
    Consider your thread stumbled upon! Did you ever discover any other ways to do this, or is this shader still the best approach?
     
  8. markv12

    markv12

    Joined:
    Dec 31, 2013
    Posts:
    34
    Other than a shader it is also possible to get the same outline look using a mesh effect where you add additional vertices with the vertex color set to black.
    The old Unity text component supported mesh effects, but Textmesh Pro doesn't support them out of the box.
    This project is attempting to enable mesh effects with TextMesh Pro. It works, but it's a bit buggy and breaks in certain situations. The project comes with an example mesh effect that creates an outline using the method I mentioned above.
    https://github.com/mob-sakai/MeshEffectForTextMeshPro
    The developer is responsive. If more people ping them, maybe they will get around to fixing the remaining issues.
     
    Rock360 likes this.
  9. Clonze

    Clonze

    Joined:
    Dec 15, 2012
    Posts:
    25
    when i try to use their unitypackage, it says type or namespace 'UIEffect' could not be found. Any idea why? It seems like it doesn't exist in Unity2019.3.0f6, is it a custom class?
     
  10. Zebbi

    Zebbi

    Joined:
    Jan 17, 2017
    Posts:
    521
    Has the been any improvement to adding pixel text outlines without using SDF?
     
    Feezen and Eater_Games like this.
  11. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,121
    Yeah we need outline for bitmap font.
     
  12. Zebbi

    Zebbi

    Joined:
    Jan 17, 2017
    Posts:
    521
    Please?
     
unityunity