Search Unity

  1. We are looking for feedback on our SRP Rendering APIs through this Survey.
    Dismiss Notice
  2. Unity 2020.2 has been released.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Help Wanted Shader to create an outline inside a polygon

Discussion in 'Shaders' started by Phil_42, Nov 20, 2020.

  1. Phil_42

    Phil_42

    Joined:
    Mar 15, 2017
    Posts:
    1
    Hey everyone

    I am trying to make a shader that creates an outline inside a polygon, like in screenshot 1. The polygons are two-dimensional and can have any shape since they are generated procedurally. I have achieved the effect in the first screenshot by creating mask texture as a Texture2D that I generate and then overlay on the polygon with a custom shader. However this is really really bad performance-wise (and looks ugly up-close) and I hope it could be done way faster with a shader.


    What I have achieved so far can be seen in the next screenshot.

    I made this with a very basic multipass shader with the following code. But it only creates the inside outline in one direction, which is not what I want.

    Code (CSharp):
    1. Shader "Custom/DistrictShader"
    2. {
    3.     Properties
    4.     {
    5.         _BaseColor("Base Color", Color) = (1,1,1,1)
    6.         _OutlineColor("Outline Color", Color) = (0,0,0,1)
    7.     }
    8.         SubShader
    9.     {
    10.         Pass
    11.         {
    12.             ZWrite Off
    13.             ColorMask 0 // do not render any color
    14.             Stencil {
    15.             Ref 1
    16.             Comp Always
    17.             Pass Replace
    18.             }
    19.  
    20.             CGPROGRAM
    21.             #pragma vertex vert
    22.             #pragma fragment frag
    23.  
    24.             #include "UnityCG.cginc"
    25.  
    26.             struct appdata
    27.             {
    28.                 float4 vertex:POSITION;
    29.                 float3 normal:NORMAL;
    30.                 float4 tangent:TANGENT;
    31.             };
    32.             struct v2f
    33.             {
    34.                 float4 clipPos:SV_POSITION;
    35.             };
    36.             v2f vert(appdata v)
    37.             {
    38.                 v2f o;
    39.                 o.clipPos = UnityObjectToClipPos(v.vertex * 0.98);
    40.                 return o;
    41.             }
    42.             fixed4 frag(v2f i) : SV_Target
    43.             {
    44.                 return fixed4(0,0,0,0);
    45.             }
    46.             ENDCG
    47.         }
    48.  
    49.  
    50.         Pass
    51.         {
    52.             ZWrite On
    53.  
    54.             CGPROGRAM
    55.             #pragma vertex vert
    56.             #pragma fragment frag
    57.  
    58.             #include "UnityCG.cginc"
    59.  
    60.             fixed4 _BaseColor;
    61.  
    62.             struct appdata
    63.             {
    64.                 float4 vertex:POSITION;
    65.             };
    66.             struct v2f
    67.             {
    68.                 float4 clipPos:SV_POSITION;
    69.             };
    70.             v2f vert(appdata v)
    71.             {
    72.                 v2f o;
    73.                 o.clipPos = UnityObjectToClipPos(v.vertex);
    74.                 return o;
    75.             }
    76.             fixed4 frag(v2f i) : SV_Target
    77.             {
    78.                 return _BaseColor;
    79.             }
    80.             ENDCG
    81.         }
    82.  
    83.         Pass
    84.         {
    85.             ZWrite Off
    86.             Stencil {
    87.             Ref 1
    88.             Comp NotEqual
    89.             }
    90.  
    91.             CGPROGRAM
    92.             #pragma vertex vert
    93.             #pragma fragment frag
    94.  
    95.             #include "UnityCG.cginc"
    96.  
    97.             float4 _OutlineColor;
    98.  
    99.             struct appdata
    100.             {
    101.                 float4 vertex:POSITION;
    102.             };
    103.             struct v2f
    104.             {
    105.                 float4 clipPos:SV_POSITION;
    106.             };
    107.             v2f vert(appdata v)
    108.             {
    109.                 v2f o;
    110.                 o.clipPos = UnityObjectToClipPos(v.vertex);
    111.                 return o;
    112.             }
    113.             fixed4 frag(v2f i) : SV_Target
    114.             {
    115.                 return _OutlineColor;
    116.             }
    117.             ENDCG
    118.         }
    119.     }
    120. }
    121.  
    Can anyone help me with this problem? The shader only has to support a base color and an outline color - no transparency or smooth transition. It doesn't even have to be affected by lighting.

    I have seen several resources for outlines but haven't really found anything for outlines that should be drawn inside the mesh. This thread (https://forum.unity.com/threads/outline-shader-with-outline-inside-of-mesh.921809/) describes a similar problem and if I understand it correctly it is not really possible the way I want to do it.
    My current solution (from screenshot 2) is based on this thread: https://forum.unity.com/threads/shader-for-adding-an-outline-to-the-inside-of-the-mesh.531288/

    I am very thankful for any help or ideas.
     
  2. anthonytrianh

    anthonytrianh

    Joined:
    Dec 7, 2018
    Posts:
    6
    If your polygons are 2D then perhaps I have a similar effect you might want. My shader has 2 passes: an outline pass and an albedo pass. Here's the function for calculating the outline color (I use a custom .cginc file since my shader is quite long):

    Code (CSharp):
    1. inline fixed4 outline(float4 input_color, float2 input_texcoord, float4 outlineColor, float lineWidth) {
    2.     if (input_color.a != 0) {
    3.         fixed2 width = _MainTex_TexelSize.xy * lineWidth;
    4.  
    5.         // Get the neighbouring four pixels.
    6.         fixed4 pixelUp         = tex2D(_MainTex, input_texcoord.xy + float2(0, width.y));
    7.         fixed4 pixelDown    = tex2D(_MainTex, input_texcoord.xy - float2(0, width.y));
    8.         fixed4 pixelRight     = tex2D(_MainTex, input_texcoord.xy + float2(width.x, 0));
    9.         fixed4 pixelLeft        = tex2D(_MainTex, input_texcoord.xy - float2(width.x, 0));
    10.  
    11.         // If one of the neighbouring pixels is transparent, render as outline.
    12.         if (pixelUp.a * pixelDown.a * pixelRight.a * pixelLeft.a == 0) {
    13.             return fixed4(1, 1, 1, 1) * outlineColor;
    14.         }
    15.     }
    16.     return (1, 1, 1, 0);
    17. }
    input_color is simply the color sampled from the main texture used mainly to skip if the pixel is transparent (this makes it render outline inside the geometry rather than outside as it replaces color from the main texture). If the pixel is not transparent then we check its surrounding pixels (in all four directions), if the product of all four neighboring pixels' alphas equals 0 then we render it as an outline. You can control the thickness of the outline by changing lineWidth.

    Hope this helps!
     

    Attached Files:

    Phil_42 likes this.
unityunity