Text Outline

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(canvas groups alpha support)
(canvas groups alpha proper handling)
Line 109: Line 109:
 
                     float4 color    : COLOR;
 
                     float4 color    : COLOR;
 
                     float2 uv_local : TEXCOORD1;
 
                     float2 uv_local : TEXCOORD1;
                 };
+
                 };  
  
                 static const fixed  alpha_threshold = 0.25f;
+
                static const fixed4 color_zero        = fixed4( 0.0f, 0.0f, 0.0f, 0.0f );
 
+
                 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 ) };
+
                 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 130: Line 130:
 
                 fixed    _Sampling_UV_MIN_Y;
 
                 fixed    _Sampling_UV_MIN_Y;
 
                 fixed    _Sampling_UV_MAX_Y;
 
                 fixed    _Sampling_UV_MAX_Y;
 +
 +
                //********************************************************************************************
 +
                //
 +
                //********************************************************************************************
 +
 +
                fixed4 Sample( sampler2D smplr, fixed2 uv, fixed2 uv_local, int lod )
 +
                {
 +
                    if( lod < 0 )
 +
                    {
 +
                        return tex2D( smplr, uv );
 +
                    }
 +
                   
 +
                    return tex2Dlod( smplr, fixed4( uv.x, uv.y, 0, lod ) );
 +
                }
  
 
                 //********************************************************************************************
 
                 //********************************************************************************************
Line 140: Line 154:
 
                     if ( valid_context )
 
                     if ( valid_context )
 
                     {
 
                     {
                         fixed  texel_w     = _MainTex_TexelSize.x;
+
                         fixed  texel_w = _MainTex_TexelSize.x;
                         fixed  texel_h     = _MainTex_TexelSize.y;  
+
                         fixed  texel_h = _MainTex_TexelSize.y;  
                         fixed  min_dst     = 1.0e38f;
+
                         fixed  min_dst = 1.0e38f;
  
 
                         fixed2 uv;
 
                         fixed2 uv;
Line 164: Line 178:
 
                                 uv.x    = i.uv.x + uv_offset.x;
 
                                 uv.x    = i.uv.x + uv_offset.x;
 
                                 uv.y    = i.uv.y + uv_offset.y;
 
                                 uv.y    = i.uv.y + uv_offset.y;
                                 sampling = tex2Dlod( _MainTex, fixed4( uv.x, uv.y, 0, 0 ) );
+
                                 sampling = Sample( _MainTex, uv, uv_local, 0 );
  
 
                                 bool outline = exterior ? ( sampling.a >= alpha_threshold ) : ( sampling.a <= alpha_threshold );
 
                                 bool outline = exterior ? ( sampling.a >= alpha_threshold ) : ( sampling.a <= alpha_threshold );
Line 207: Line 221:
 
                 fixed4 frag( v2f i ) : SV_Target
 
                 fixed4 frag( v2f i ) : SV_Target
 
                 {
 
                 {
                     fixed4 output = tex2D( _MainTex, i.uv );
+
                     fixed4 output = Sample( _MainTex, i.uv, i.uv_local, -1 );
                          output = fixed4( i.color.r, i.color.g, i.color.b, output.a * i.color.a );
+
  
 
                     if( _Exterior > 0.0f )
 
                     if( _Exterior > 0.0f )
Line 215: Line 228:
 
                         if( outline_alpha > 0.0f )  
 
                         if( outline_alpha > 0.0f )  
 
                         {
 
                         {
                             return fixed4( _OutlineColor.r, _OutlineColor.g, _OutlineColor.b, _OutlineColor.a * outline_alpha * i.color.a );
+
                             return fixed4( _OutlineColor.rgb, _OutlineColor.a * outline_alpha * i.color.a );
 
                         }
 
                         }
 
                     }
 
                     }
Line 224: Line 237:
 
                         if( outline_alpha > 0.0f )
 
                         if( outline_alpha > 0.0f )
 
                         {
 
                         {
                             fixed4 material_color = fixed4( i.color.r, i.color.g, i.color.b, 1.0f );
+
                             fixed4 material_color = fixed4( i.color.rgb, 1.0f );
 
                             fixed4 result        = lerp( material_color, _OutlineColor, outline_alpha );
 
                             fixed4 result        = lerp( material_color, _OutlineColor, outline_alpha );
                             return fixed4( result.r, result.g, result.b, result.a * i.color.a );
+
                             return fixed4( result.rgb, result.a * i.color.a );
 
                         }
 
                         }
 
                     }
 
                     }
 
                      
 
                      
 +
                    output = fixed4( i.color.rgb, output.a * i.color.a );
 
                     return output;
 
                     return output;
 
                 }
 
                 }

Revision as of 01:30, 2 November 2020

Author: Serge Billault

Contents

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.

Preview

Text outline.jpg


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 ) ) = 0.975
 
         [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;
                    float4 color    : COLOR;
                    float2 uv_local : TEXCOORD1;
                }; 
 
                static const fixed4 color_zero         = fixed4( 0.0f, 0.0f, 0.0f, 0.0f );
                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, fixed2 uv_local, 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 )
                {
                    bool valid_context = exterior ? ( a < alpha_threshold ) : ( a > alpha_threshold );
                    if ( valid_context )
                    {
                        fixed  texel_w = _MainTex_TexelSize.x;
                        fixed  texel_h = _MainTex_TexelSize.y; 
                        fixed  min_dst = 1.0e38f;
 
                        fixed2 uv;
                        fixed2 uv_local;
                        fixed2 uv_offset;
                        fixed4 sampling;
                        fixed  dst;
 
                        for( int x = -_Thickness; x <= _Thickness; ++x )
                        {
                            uv_offset.x = x * texel_w;
                            uv_local.x  = i.uv_local.x + uv_offset.x;
                            if( ( uv_local.x < _Sampling_UV_MIN_X ) || ( uv_local.x > _Sampling_UV_MAX_X ) ) continue;
 
                            for( int y = -_Thickness; y <= _Thickness; ++y )
                            {
                                uv_offset.y = y * texel_h;
                                uv_local.y  = i.uv_local.y + uv_offset.y;
                                if( ( uv_local.y < _Sampling_UV_MIN_Y ) || ( uv_local.y > _Sampling_UV_MAX_Y ) ) continue;
 
                                uv.x     = i.uv.x + uv_offset.x;
                                uv.y     = i.uv.y + uv_offset.y;
                                sampling = Sample( _MainTex, uv, uv_local, 0 );
 
                                bool outline = exterior ? ( sampling.a >= alpha_threshold ) : ( sampling.a <= alpha_threshold );
                                if ( outline )
                                {
                                    dst = ( x * x ) + ( y * y );
                                    if( min_dst > dst ) min_dst = dst;
                                }
                            }
                        }
 
                        if( min_dst < 1.0e38f )
                        {
                            fixed  alpha = 1.0f - ( clamp( ( min_dst ) / ( _Thickness * _Thickness ), 0.0f, 1.0f ) );
                            return alpha;
                        }
                    }
 
                    return -1.0f;
                }
 
                //********************************************************************************************
                //
                //********************************************************************************************
 
                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, i.uv_local, -1 );
 
                    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"
}

License

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

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox