Text Outline
From Unify Community Wiki
(Difference between revisions)
(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
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.