Text Outline

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(fix code formatting)
m (faster clip region (for what it's worth, given that we cant relay rely on UVs))
 
(14 intermediate revisions by one user not shown)
Line 11: Line 11:
  
  
 +
== Understanding the human nature behind the impossibility to get Texts local UVs ==
 +
 +
A shader can only access individual vertices and has no infos on the vertices of a triangle other than the one being processed. It is therefore impossible to get some informations such as local UVs if you dont pass these informations in some way or another.
 +
The only thing that stands between you and being free of all the... "funy" things you have to do to perform simple operations is the fact that despite Unity knowing how to insert informations it need for itself into the geometry,
 +
when it comes to the users needs they suddenly dont know any more how to do it. As result you get this kind of thread on the forums (notice the date: 2020, we have been dealing with Unity decision for a decade):
 +
* https://forum.unity.com/threads/shader-graph-getting-local-sprite-uv-from-sprite-sheet.865834/
 +
 +
 +
Should they one day decide to include local UVs infos for everything quad based like texts and images, then
 +
* night becomes day.
 +
* Text packages are no more necessary for 90% of us.
 +
* fonts, no matter which, do not need any padding any more.
 +
 +
 +
I mentioned padding because that is actually what prevent you from adding arbitrary outlines of any size. In their Atlas, glyphs are so tightly packed that you often end up sampling part of glyphs that are not the one being rendered (bleeding effect).
 +
If you have access to local UVs you overcome it directly in the shader by scaling the quad and the UVs (so that the glyph size remain unchanged but the area we can draw to, increases). When sampling you then return fixed4(0,0,0,0) for everything outside of the glyph clip rectangle, but draw outlines using the whole quad.
 +
 +
 +
But it has never been deemed usefull to service the users by passing local UVs as informations in spite of all the users over the world asking for it for 10 years.
 +
So we keep writing our own Text components, rewriting mesh populatiing / drawing functions or buying packages from wich we use only 1% of the features.
 +
 +
 +
Below: the expected vertices order by the shader in this page. We can see that Unity find it funy to not only not pass the local UVs requested by thousand of users since a decade but that they also enjoy passing quads with varying vertices orders. Because.
 +
The result is that you must not rely on the local UVs in this shader for anything other than bounds checks.
 +
 +
[[File:UnityInconsistencies.jpg]]
  
 
== Code (  UI_TextOutline.shader ) ==
 
== Code (  UI_TextOutline.shader ) ==
 
<syntaxhighlight lang="CSharp">
 
<syntaxhighlight lang="CSharp">
Shader "Unlit/UI_TextOutline"
+
Shader "Unlit/UITextOutline"
 
{
 
{
 
     Properties
 
     Properties
 
     {
 
     {
                                              _MainTex    ( " Texture",      2D                ) = "white" {}
+
        /*                */                _MainTex    ( " Texture",      2D                ) = "white" {}
 
         /*[PerRendererData]*/                _OutlineColor( " Outline Color", Color              ) = ( 0.0, 0.0, 0.0, 1.0 )
 
         /*[PerRendererData]*/                _OutlineColor( " Outline Color", Color              ) = ( 0.0, 0.0, 0.0, 1.0 )
 
         /*[PerRendererData]*/                _Thickness  ( " Thickness",    Range( 1.0, 16.0 ) ) = 2.1
 
         /*[PerRendererData]*/                _Thickness  ( " Thickness",    Range( 1.0, 16.0 ) ) = 2.1
 
         /*[PerRendererData]*/[MaterialToggle] _Exterior    ( " Exterior", Float ) = 1.0
 
         /*[PerRendererData]*/[MaterialToggle] _Exterior    ( " Exterior", Float ) = 1.0
 
         /*[PerRendererData]*/[MaterialToggle] _Interior    ( " Interior", Float ) = 0.0
 
         /*[PerRendererData]*/[MaterialToggle] _Interior    ( " Interior", Float ) = 0.0
 +
        /*[PerRendererData]*/                _Sampling_UV_MIN_X  ( " Sampling UV Min X", Range( 0.0, 1.0 ) ) = 0.0
 +
        /*[PerRendererData]*/                _Sampling_UV_MAX_X  ( " Sampling UV Max X", Range( 0.0, 1.0 ) ) = 1.0
 +
        /*[PerRendererData]*/                _Sampling_UV_MIN_Y  ( " Sampling UV Min Y", Range( 0.0, 1.0 ) ) = 0.0
 +
        /*[PerRendererData]*/                _Sampling_UV_MAX_Y  ( " Sampling UV Max Y", Range( 0.0, 1.0 ) ) = 1.0
 +
       
 +
        [HideInInspector]_StencilComp    ( "Stencil Comparison", Float ) = 8
 +
        [HideInInspector]_Stencil        ( "Stencil ID",        Float ) = 0
 +
        [HideInInspector]_StencilOp      ( "Stencil Operation",  Float ) = 0
 +
        [HideInInspector]_StencilWriteMask( "Stencil Write Mask", Float ) = 255
 +
        [HideInInspector]_StencilReadMask ( "Stencil Read Mask",  Float ) = 255
 +
        [HideInInspector]_ColorMask      ( "Color Mask",        Float ) = 15
 
     }
 
     }
  
 
     SubShader
 
     SubShader
 
     {
 
     {
 +
        //****************************************************************************************************
 +
        //
 +
        //****************************************************************************************************
 +
 
         Tags  
 
         Tags  
 
         {  
 
         {  
             "Queue"     = "Transparent"
+
             "Queue"             = "Transparent"
             "RenderType" = "Opaque"
+
            "IgnoreProjector"  = "True"
 +
             "RenderType"        = "Transparent"
 +
            "PreviewType"      = "Plane"
 +
            "CanUseSpriteAtlas" = "True"
 
         }
 
         }
  
         LOD 100
+
         //****************************************************************************************************
 +
        //
 +
        //****************************************************************************************************
  
 +
        Stencil
 +
        {
 +
            Ref      [_Stencil]
 +
            Comp      [_StencilComp]
 +
            Pass      [_StencilOp]
 +
            ReadMask  [_StencilReadMask]
 +
            WriteMask [_StencilWriteMask]
 +
        }
 +
 +
        //****************************************************************************************************
 +
        //
 +
        //****************************************************************************************************
 +
 +
        Cull          Off
 +
        Lighting      Off
 +
        ZWrite        Off
 +
        ZTest        [unity_GUIZTestMode]
 +
        ColorMask    [_ColorMask]
 
         Blend SrcAlpha OneMinusSrcAlpha
 
         Blend SrcAlpha OneMinusSrcAlpha
 +
 +
        //****************************************************************************************************
 +
        //
 +
        //****************************************************************************************************
  
 
         Pass
 
         Pass
Line 56: Line 124:
 
                 struct appdata
 
                 struct appdata
 
                 {
 
                 {
                     float4 vertex : POSITION;
+
                     float4 vertex   : POSITION;
                     float2 uv     : TEXCOORD0;
+
                     float2 uv       : TEXCOORD0;
                     float4 color : COLOR;
+
                     float4 color   : COLOR;
 
                 };
 
                 };
  
 
                 struct v2f
 
                 struct v2f
 
                 {
 
                 {
                     float4 vertex : SV_POSITION;
+
                     float4 vertex   : SV_POSITION;
                     float2 uv     : TEXCOORD0;
+
                     float2 uv       : TEXCOORD0;
                     float4 color : COLOR;
+
                    float2 uv_local : TEXCOORD1;
                 };
+
                     float4 color   : COLOR;
 +
                 };  
  
                 static const fixed alpha_threshold = 0.6f;
+
                 static const fixed alpha_threshold = 0.25f;
 +
                static const fixed2 quads_uvs[ 4 ]  = { fixed2( 0.0f, 1.0f ), fixed2( 1.0f, 1.0f ), fixed2( 1.0f, 0.0f ), fixed2( 0.0f, 0.0f ) };
  
 
                 //********************************************************************************************
 
                 //********************************************************************************************
Line 81: Line 151:
 
                 fixed    _Exterior;
 
                 fixed    _Exterior;
 
                 fixed    _Interior;
 
                 fixed    _Interior;
 +
                fixed    _Sampling_UV_MIN_X;
 +
                fixed    _Sampling_UV_MAX_X;
 +
                fixed    _Sampling_UV_MIN_Y;
 +
                fixed    _Sampling_UV_MAX_Y;
  
 
                 //********************************************************************************************
 
                 //********************************************************************************************
Line 86: Line 160:
 
                 //********************************************************************************************
 
                 //********************************************************************************************
  
                 fixed GetOutineAlpha( v2f i, fixed a, bool exterior )
+
                 fixed4 Sample( sampler2D smplr, fixed2 uv, int lod )
 
                 {
 
                 {
 +
                    if( lod < 0 )
 +
                    {
 +
                        return tex2D( smplr, uv );
 +
                    }
 +
                   
 +
                    return tex2Dlod( smplr, fixed4( uv.x, uv.y, 0, lod ) );
 +
                }
 +
 +
                //********************************************************************************************
 +
                //
 +
                //********************************************************************************************
 +
 +
                fixed GetOutlineAlpha( v2f i, fixed a, bool exterior )
 +
                {
 +
                    fixed alpha = -1.0f;
 +
 
                     bool valid_context = exterior ? ( a < alpha_threshold ) : ( a > alpha_threshold );
 
                     bool valid_context = exterior ? ( a < alpha_threshold ) : ( a > alpha_threshold );
 +
 
                     if ( valid_context )
 
                     if ( valid_context )
 
                     {
 
                     {
                         fixed  texel_w = _MainTex_TexelSize.x;
+
                         bool valid_region = ( i.uv_local.x >= _Sampling_UV_MIN_X ) &&
                        fixed  texel_h = _MainTex_TexelSize.y;
+
                                            ( i.uv_local.x <= _Sampling_UV_MAX_X ) &&
                        fixed  min_dst = 1.0e38f;
+
                                            ( i.uv_local.y >= _Sampling_UV_MIN_Y ) &&
                        fixed  dst;
+
                                            ( i.uv_local.y <= _Sampling_UV_MAX_Y );
                        fixed2 uv;
+
                        fixed4 sampling;
+
  
                         for( int x = -_Thickness; x <= _Thickness; ++x )
+
                         if( valid_region )
 
                         {
 
                         {
                             for( int y = -_Thickness; y <= _Thickness; ++y )
+
                             fixed  texel_w = _MainTex_TexelSize.x;
                            {
+
                            fixed  texel_h = _MainTex_TexelSize.y;  
                                uv.x     = i.uv.x + ( x * texel_w );
+
                            fixed  min_dst = 1.0e38f;
                                uv.y    = i.uv.y + ( y * texel_h );
+
                                sampling = tex2Dlod( _MainTex, fixed4( uv.x, uv.y, 0, 0 ) );
+
  
 +
                            int x_s = ( i.uv_local.x <= _Sampling_UV_MIN_X ) ? 0 : -_Thickness;
 +
                            int x_e = ( i.uv_local.x >= _Sampling_UV_MAX_X ) ? 0 :  _Thickness;
 +
                            int y_s = ( i.uv_local.y <= _Sampling_UV_MIN_Y ) ? 0 : -_Thickness;
 +
                            int y_e = ( i.uv_local.y >= _Sampling_UV_MAX_Y ) ? 0 :  _Thickness;
  
                                bool outline = exterior ? ( sampling.a >= alpha_threshold ) : ( sampling.a <= alpha_threshold );
+
                            for( int x = x_s; x <= x_e; ++x )
                                 if ( outline )
+
                            {
 +
                                 for( int y = y_s; y <= y_e; ++y )
 
                                 {
 
                                 {
                                     dst = ( x * x ) + ( y * y );
+
                                     fixed2 offset  = fixed2( x * texel_w, y * texel_h );
                                    if( min_dst > dst ) min_dst = dst;  
+
 
 +
                                    fixed4 sampling = Sample( _MainTex, i.uv + offset, 0 );
 +
 
 +
                                    bool  outline  = exterior ? ( sampling.a >= alpha_threshold ) : ( sampling.a <= alpha_threshold );
 +
 
 +
                                    if( outline )
 +
                                    {
 +
                                        fixed dst = ( x * x ) + ( y * y );
 +
                                        if( min_dst > dst ) min_dst = dst;
 +
                                    }
 
                                 }
 
                                 }
 
                             }
 
                             }
                        }
+
 
                       
+
                            if( min_dst < 1.0e38f )
                        if( min_dst < 1.0e38f )
+
                            {
                        {
+
                                alpha = 1.0f - ( clamp( ( min_dst ) / ( _Thickness * _Thickness ), 0.0f, 1.0f ) );
                            fixed  alpha = 1.0f - ( clamp( ( min_dst ) / ( _Thickness * _Thickness ), 0.0f, 1.0f ) );
+
                             }
                             return alpha;
+
 
                         }
 
                         }
 
                     }
 
                     }
  
                     return -1.0f;
+
                     return alpha;
 
                 }
 
                 }
  
Line 130: Line 230:
 
                 //********************************************************************************************
 
                 //********************************************************************************************
  
                 v2f vert( appdata v )
+
                 v2f vert( appdata v, uint id : SV_VertexID )
 
                 {
 
                 {
 
                     v2f o;
 
                     v2f o;
                     o.vertex = UnityObjectToClipPos( v.vertex );
+
 
                     o.uv     = TRANSFORM_TEX( v.uv, _MainTex );
+
                     o.vertex   = UnityObjectToClipPos( v.vertex );
                     o.color = v.color;
+
                     o.uv       = TRANSFORM_TEX( v.uv, _MainTex );
 +
                     o.color   = v.color;
 +
                    o.uv_local = quads_uvs[ id & 3 ];
 +
 
 
                     return o;
 
                     return o;
 
                 }
 
                 }
Line 145: Line 248:
 
                 fixed4 frag( v2f i ) : SV_Target
 
                 fixed4 frag( v2f i ) : SV_Target
 
                 {
 
                 {
                     fixed4 output = tex2D( _MainTex, i.uv );
+
                     fixed4 output = Sample( _MainTex, i.uv, 0 );
  
 
                     if( _Exterior > 0.0f )
 
                     if( _Exterior > 0.0f )
 
                     {
 
                     {
                         fixed outline_alpha = GetOutineAlpha( i, output.a, true  );
+
                         fixed outline_alpha = GetOutlineAlpha( i, output.a, true  );
                         if( outline_alpha > 0.0f ) return fixed4( _OutlineColor.r, _OutlineColor.g, _OutlineColor.b, _OutlineColor.a * outline_alpha * i.color.a );
+
                         if( outline_alpha > 0.0f )  
 +
                        {
 +
                            return fixed4( _OutlineColor.rgb, _OutlineColor.a * outline_alpha * i.color.a );
 +
                        }
 
                     }
 
                     }
  
 
                     if( _Interior > 0.0f )
 
                     if( _Interior > 0.0f )
 
                     {
 
                     {
                         fixed outline_alpha = GetOutineAlpha( i, output.a, false );
+
                         fixed outline_alpha = GetOutlineAlpha( i, output.a, false );
                         fixed4 material_color = fixed4( i.color.r, i.color.g, i.color.b, 1.0f );
+
                         if( outline_alpha > 0.0f )
                        if( outline_alpha > 0.0f ) return lerp( material_color, _OutlineColor, outline_alpha ) * i.color.a;
+
                        {
 +
                            fixed4 material_color = fixed4( i.color.rgb, 1.0f );
 +
                            fixed4 result        = lerp( material_color, _OutlineColor, outline_alpha );
 +
                            return fixed4( result.rgb, result.a * i.color.a );
 +
                        }
 
                     }
 
                     }
                   
+
 
                     return output.a * i.color;
+
                     output = fixed4( i.color.rgb, output.a * i.color.a );
 +
                    return output;
 
                 }
 
                 }
  
Line 166: Line 277:
 
         }
 
         }
 
     }
 
     }
 +
 +
    Fallback "UI/Default"
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>

Latest revision as of 08:36, 3 November 2020

Author: Serge Billault

Contents

[edit] Description

Text outlining with independantly selectable interior and exterior. The pixel being rendered sample its surrounding according to outline _Thickness (in pixels) and deduce the outline alpha from the distance with the nearest outline inducing texture texel.

[edit] Preview

Text outline.jpg


[edit] Understanding the human nature behind the impossibility to get Texts local UVs

A shader can only access individual vertices and has no infos on the vertices of a triangle other than the one being processed. It is therefore impossible to get some informations such as local UVs if you dont pass these informations in some way or another. The only thing that stands between you and being free of all the... "funy" things you have to do to perform simple operations is the fact that despite Unity knowing how to insert informations it need for itself into the geometry, when it comes to the users needs they suddenly dont know any more how to do it. As result you get this kind of thread on the forums (notice the date: 2020, we have been dealing with Unity decision for a decade):


Should they one day decide to include local UVs infos for everything quad based like texts and images, then

  • night becomes day.
  • Text packages are no more necessary for 90% of us.
  • fonts, no matter which, do not need any padding any more.


I mentioned padding because that is actually what prevent you from adding arbitrary outlines of any size. In their Atlas, glyphs are so tightly packed that you often end up sampling part of glyphs that are not the one being rendered (bleeding effect). If you have access to local UVs you overcome it directly in the shader by scaling the quad and the UVs (so that the glyph size remain unchanged but the area we can draw to, increases). When sampling you then return fixed4(0,0,0,0) for everything outside of the glyph clip rectangle, but draw outlines using the whole quad.


But it has never been deemed usefull to service the users by passing local UVs as informations in spite of all the users over the world asking for it for 10 years. So we keep writing our own Text components, rewriting mesh populatiing / drawing functions or buying packages from wich we use only 1% of the features.


Below: the expected vertices order by the shader in this page. We can see that Unity find it funy to not only not pass the local UVs requested by thousand of users since a decade but that they also enjoy passing quads with varying vertices orders. Because. The result is that you must not rely on the local UVs in this shader for anything other than bounds checks.

UnityInconsistencies.jpg

[edit] Code ( UI_TextOutline.shader )

Shader "Unlit/UITextOutline"
{
    Properties
    {
        /*                 */                 _MainTex     ( " Texture",       2D                 ) = "white" {}
        /*[PerRendererData]*/                 _OutlineColor( " Outline Color", Color              ) = ( 0.0, 0.0, 0.0, 1.0 )
        /*[PerRendererData]*/                 _Thickness   ( " Thickness",     Range( 1.0, 16.0 ) ) = 2.1
        /*[PerRendererData]*/[MaterialToggle] _Exterior    ( " Exterior", Float ) = 1.0
        /*[PerRendererData]*/[MaterialToggle] _Interior    ( " Interior", Float ) = 0.0
        /*[PerRendererData]*/                 _Sampling_UV_MIN_X   ( " Sampling UV Min X", Range( 0.0, 1.0 ) ) = 0.0
        /*[PerRendererData]*/                 _Sampling_UV_MAX_X   ( " Sampling UV Max X", Range( 0.0, 1.0 ) ) = 1.0
        /*[PerRendererData]*/                 _Sampling_UV_MIN_Y   ( " Sampling UV Min Y", Range( 0.0, 1.0 ) ) = 0.0
        /*[PerRendererData]*/                 _Sampling_UV_MAX_Y   ( " Sampling UV Max Y", Range( 0.0, 1.0 ) ) = 1.0
 
         [HideInInspector]_StencilComp     ( "Stencil Comparison", Float ) = 8
         [HideInInspector]_Stencil         ( "Stencil ID",         Float ) = 0
         [HideInInspector]_StencilOp       ( "Stencil Operation",  Float ) = 0
         [HideInInspector]_StencilWriteMask( "Stencil Write Mask", Float ) = 255
         [HideInInspector]_StencilReadMask ( "Stencil Read Mask",  Float ) = 255
         [HideInInspector]_ColorMask       ( "Color Mask",         Float ) = 15
    }
 
    SubShader
    {
        //****************************************************************************************************
        //
        //****************************************************************************************************
 
        Tags 
        { 
            "Queue"             = "Transparent"
            "IgnoreProjector"   = "True"
            "RenderType"        = "Transparent"
            "PreviewType"       = "Plane"
            "CanUseSpriteAtlas" = "True"
        }
 
        //****************************************************************************************************
        //
        //****************************************************************************************************
 
        Stencil
        {
            Ref       [_Stencil]
            Comp      [_StencilComp]
            Pass      [_StencilOp]
            ReadMask  [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }
 
        //****************************************************************************************************
        //
        //****************************************************************************************************
 
        Cull          Off
        Lighting      Off
        ZWrite        Off
        ZTest         [unity_GUIZTestMode]
        ColorMask     [_ColorMask]
        Blend SrcAlpha OneMinusSrcAlpha
 
        //****************************************************************************************************
        //
        //****************************************************************************************************
 
        Pass
        {
            CGPROGRAM
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                #pragma vertex   vert
                #pragma fragment frag
 
                #include "UnityCG.cginc"
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                struct appdata
                {
                    float4 vertex   : POSITION;
                    float2 uv       : TEXCOORD0;
                    float4 color    : COLOR;
                };
 
                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    float2 uv       : TEXCOORD0;
                    float2 uv_local : TEXCOORD1;
                    float4 color    : COLOR;
                }; 
 
                static const fixed  alpha_threshold = 0.25f;
                static const fixed2 quads_uvs[ 4 ]  = { fixed2( 0.0f, 1.0f ), fixed2( 1.0f, 1.0f ), fixed2( 1.0f, 0.0f ), fixed2( 0.0f, 0.0f ) };
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                sampler2D _MainTex;
                fixed4    _MainTex_TexelSize;
                fixed4    _MainTex_ST;
                fixed4    _OutlineColor;
                fixed     _Thickness;
                fixed     _Exterior;
                fixed     _Interior;
                fixed     _Sampling_UV_MIN_X;
                fixed     _Sampling_UV_MAX_X;
                fixed     _Sampling_UV_MIN_Y;
                fixed     _Sampling_UV_MAX_Y;
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                fixed4 Sample( sampler2D smplr, fixed2 uv, int lod )
                {
                    if( lod < 0 )
                    {
                        return tex2D( smplr, uv );
                    }
 
                    return tex2Dlod( smplr, fixed4( uv.x, uv.y, 0, lod ) );
                }
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                fixed GetOutlineAlpha( v2f i, fixed a, bool exterior )
                {
                    fixed alpha = -1.0f;
 
                    bool valid_context = exterior ? ( a < alpha_threshold ) : ( a > alpha_threshold );
 
                    if ( valid_context )
                    {
                        bool valid_region = ( i.uv_local.x >= _Sampling_UV_MIN_X ) && 
                                            ( i.uv_local.x <= _Sampling_UV_MAX_X ) &&
                                            ( i.uv_local.y >= _Sampling_UV_MIN_Y ) &&
                                            ( i.uv_local.y <= _Sampling_UV_MAX_Y );
 
                        if( valid_region )
                        {
                            fixed  texel_w = _MainTex_TexelSize.x;
                            fixed  texel_h = _MainTex_TexelSize.y; 
                            fixed  min_dst = 1.0e38f;
 
                            int x_s = ( i.uv_local.x <= _Sampling_UV_MIN_X ) ? 0 : -_Thickness;
                            int x_e = ( i.uv_local.x >= _Sampling_UV_MAX_X ) ? 0 :  _Thickness;
                            int y_s = ( i.uv_local.y <= _Sampling_UV_MIN_Y ) ? 0 : -_Thickness;
                            int y_e = ( i.uv_local.y >= _Sampling_UV_MAX_Y ) ? 0 :  _Thickness;
 
                            for( int x = x_s; x <= x_e; ++x )
                            {
                                for( int y = y_s; y <= y_e; ++y )
                                {
                                    fixed2 offset   = fixed2( x * texel_w, y * texel_h );
 
                                    fixed4 sampling = Sample( _MainTex, i.uv + offset, 0 );
 
                                    bool   outline  = exterior ? ( sampling.a >= alpha_threshold ) : ( sampling.a <= alpha_threshold );
 
                                    if( outline )
                                    {
                                        fixed dst = ( x * x ) + ( y * y );
                                        if( min_dst > dst ) min_dst = dst;
                                    }
                                }
                            }
 
                            if( min_dst < 1.0e38f )
                            {
                                alpha = 1.0f - ( clamp( ( min_dst ) / ( _Thickness * _Thickness ), 0.0f, 1.0f ) );
                            }
                        }
                    }
 
                    return alpha;
                }
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                v2f vert( appdata v, uint id : SV_VertexID )
                {
                    v2f o;
 
                    o.vertex   = UnityObjectToClipPos( v.vertex );
                    o.uv       = TRANSFORM_TEX( v.uv, _MainTex );
                    o.color    = v.color;
                    o.uv_local = quads_uvs[ id & 3 ];
 
                    return o;
                }
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                fixed4 frag( v2f i ) : SV_Target
                {
                    fixed4 output = Sample( _MainTex, i.uv, 0 );
 
                    if( _Exterior > 0.0f )
                    {
                        fixed outline_alpha = GetOutlineAlpha( i, output.a, true  );
                        if( outline_alpha > 0.0f ) 
                        {
                            return fixed4( _OutlineColor.rgb, _OutlineColor.a * outline_alpha * i.color.a );
                        }
                    }
 
                    if( _Interior > 0.0f )
                    {
                        fixed outline_alpha = GetOutlineAlpha( i, output.a, false );
                        if( outline_alpha > 0.0f )
                        {
                            fixed4 material_color = fixed4( i.color.rgb, 1.0f );
                            fixed4 result         = lerp( material_color, _OutlineColor, outline_alpha );
                            return fixed4( result.rgb, result.a * i.color.a );
                        }
                    }
 
                    output = fixed4( i.color.rgb, output.a * i.color.a );
                    return output;
                }
 
            ENDCG
        }
    }
 
    Fallback "UI/Default"
}

[edit] License

Well, it's one of my first shaders, so... . Planet free.

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox