Text Outline

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
(Unity locked itself with an incomprehensible UVs policy)
(prepared the section explaining about local UVs)
Line 9: Line 9:
  
 
[[File:Text outline.jpg]]
 
[[File:Text outline.jpg]]
 +
 +
 +
== Understanding the human nature behind the impossibility to get Text local UVs ==
  
  

Revision as of 23:03, 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


Understanding the human nature behind the impossibility to get Text local UVs

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, 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 )
                {
                    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;
 
                            fixed2 uv;
                            fixed2 uv_offset;
                            fixed4 sampling;
                            fixed  dst;
 
                            for( int x = -_Thickness; x <= _Thickness; ++x )
                            {
                                if( ( x < 0 ) && ( i.uv_local.x <= 0.0f ) ) continue;
                                if( ( x > 0 ) && ( i.uv_local.x >= 1.0f ) ) break;
 
                                for( int y = -_Thickness; y <= _Thickness; ++y )
                                {
                                    if( ( y < 0 ) && ( i.uv_local.y <= 0.0f ) ) continue;
                                    if( ( y > 0 ) && ( i.uv_local.y >= 1.0f ) ) break;
 
                                    uv_offset.x = x * texel_w;
                                    uv_offset.y = y * texel_h;
                                    uv          = i.uv + uv_offset;
                                    sampling    = Sample( _MainTex, uv, i.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 )
                            {
                                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, i.uv_local, 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"
}

License

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

Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox