Search Unity

Different Outline Shader

Discussion in 'Shaders' started by Hylus, Sep 5, 2019.

  1. Hylus

    Hylus

    Joined:
    Jul 4, 2017
    Posts:
    7
    Hello. I have a problem with outline shader in my dynamically generated mesh. I tried using probably all available outline shader but the result was not be rewarding.
    For example i tried using:
    https://github.com/westmark/unity-mesh-outline
    https://roystan.net/articles/outline-shader.html
    https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/
    https://www.febucci.com/2019/06/sprite-outline-shader/
    etc.

    I attached image to show 'my little progress'
    The problem is when two or more shapes are covered (these shapes touch each other by maybe one pixel - ss number two), then outline between these shapes are not rendering.

    I useVector grahpics package:
    https://forum.unity.com/threads/vector-graphics-preview-package.529845/
    but this asset don't provide any normal information along with the SVG generated meshes so maybe this is a reason of my problem.

    When I'm using this simple shader my outline behave weirdly.

    Code (CSharp):
    1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    2. Shader "Custom/Outline"
    3. {
    4.     Properties //variables
    5.     {
    6.         _MainTex("Main Texture (RGB)",2D) = "white" {} // allows for a texture property
    7.         _Color("Color", Color) = (1,1,1,1) // allows for a color property
    8.         _OutlineTex("Outline Texture", 2D) = "white" {}
    9.         _OutlineColor("Outline Color", Color) = (1,1,1,1)
    10.         _OutlineWidth("Outline Width", Range(1.0,10.0)) = 1.1
    11.     }
    12.         Subshader
    13.         {
    14.             Tags
    15.             {
    16.                 "Queue" = "Transparent"
    17.             //    "DisableBatching" = "True"
    18.             }
    19.             Pass
    20.             {
    21.                 Name "OUTLINE"
    22.            
    23.                 ZWrite Off
    24.            
    25.                 CGPROGRAM // allows talk between two languages: shader lab and nvidia C for graphics
    26.                 // Function Defines - defines the name for the vertex and fragment functions
    27.                 // Define for the building function.
    28.                 // Has information of the shape and tells program how to build it
    29.                 #pragma vertex vert
    30.                 // Define for coloring function
    31.                 #pragma fragment frag
    32.                 // Includes
    33.                 #include "UnityCG.cginc" // build in shader functions.
    34.                 // Structures - can get data like - vertices's, normal, color, uv
    35.                 //appdata basically says how the vertex function has going to get inforamtions
    36.                 struct appdata // vertex information
    37.                 {
    38.                     float4 vertex : POSITION;
    39.                     float2 uv : TEXCOORD0;
    40.                 };
    41.         // how fragments get its data
    42.         struct v2f // fragment inforamtion
    43.         {
    44.             //without SV_ this shader doesnt work on playstation and some other platforms
    45.             float4 pos : SV_POSITION;
    46.             float2 uv: TEXCOORD0;
    47.         };
    48.         // Imports - Re-import property from shader lab to nvidia cg
    49.         // properties have to same the same name like in shader lab part
    50.         float4 _OutlineColor;
    51.         sampler2D _OutlineTex;
    52.         float _OutlineWidth;
    53.         // Vertex function - builds the object
    54.         //vertex to fragment
    55.         //put struct appdata (above) into IN and convert it into a v2f
    56.         v2f vert(appdata IN)
    57.         {
    58.             //get all vertexes of the shape and it's going to
    59.             //times it by the outline width
    60.             // and so its gonna make it either the same size if its 1 or more than one
    61.             // SO its gonna get the shape and then its goona increase it a bit bigger than the actual shape size
    62.             IN.vertex.xyz *= _OutlineWidth;
    63.             v2f OUT;
    64.             // its gonna take the object form the object space and
    65.             // put it into camera clip space so it's gonna allow it to appear
    66.             // on the screen
    67.             //OUT.pos = UnityObjectToClipPos(IN.vertex);
    68.             OUT.pos = UnityObjectToClipPos(IN.vertex);
    69.         //    OUT.pos -=  float4(1, 0, 0,0);
    70.             // swapping the uvs to our uvs
    71.             OUT.uv = IN.uv;
    72.             return OUT;
    73.         }
    74.         // fragment function - color it in
    75.         fixed4 frag(v2f IN) : SV_Target
    76.         {
    77.             //gets the teture and wraps it around the uvs of the objects
    78.             float4 texColor = tex2D(_OutlineTex, IN.uv);
    79.             return texColor * _OutlineColor;
    80.         }
    81.         ENDCG
    82.     }
    83.     Pass
    84.     {
    85.         Name "OBJECT"
    86.         CGPROGRAM // allows talk between two languages: shader lab and nvidia C for graphics
    87.         // Function Defines - defines the name for the vertex and fragment functions
    88.         // Define for the building function.
    89.         // Has information of the shape and tells program how to build it
    90.         #pragma vertex vert
    91.         // Define for coloring function
    92.         #pragma fragment frag
    93.         // Includes
    94.         #include "UnityCG.cginc" // build in shader functions.
    95.         // Structures - can get data like - vertices's, normal, color, uv
    96.         //appdata basically says how the vertex function has going to get inforamtions
    97.         struct appdata // vertex information
    98.         {
    99.             float4 vertex : POSITION;
    100.             float2 uv : TEXCOORD0;
    101.         };
    102.         // how fragments get its data
    103.         struct v2f // fragment inforamtion
    104.         {
    105.             //without SV_ this shader doesnt work on playstation and some other platforms
    106.             float4 pos : SV_POSITION;
    107.             float2 uv: TEXCOORD0;
    108.         };
    109.         // Imports - Re-import property from shader lab to nvidia cg
    110.         // properties have to same the same name like in shader lab part
    111.         float4 _Color;
    112.         sampler2D _MainTex;
    113.         // Vertex function - builds the object
    114.         //vertex to fragment
    115.         //put struct appdata (above) into IN and convert it into a v2f
    116.         v2f vert(appdata IN)
    117.         {
    118.             v2f OUT;
    119.             // its gonna take the object form the object space and
    120.             // put it into camera clip space so it's gonna allow it to appear
    121.             // on the screen
    122.             OUT.pos = UnityObjectToClipPos(IN.vertex);
    123.             // swapping the uvs to our uvs
    124.             OUT.uv = IN.uv;
    125.             return OUT;
    126.         }
    127.         // fragment function - color it in
    128.         fixed4 frag(v2f IN) : SV_Target
    129.         {
    130.             //gets the teture and wraps it around the uvs of the objects
    131.             float4 texColor = tex2D(_MainTex, IN.uv);
    132.             return texColor * _Color;
    133.         }
    134.         ENDCG
    135.     }
    136.         }
    137. }
    I'm little confused and don't know what to do. Does it caused by normals or something else? Or what should i change in my shader to works fine?


    The problem occured in procedural mesh generated:
    You can use this to check:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PolygonTester : MonoBehaviour
    4. {
    5.     void Start()
    6.     {
    7.         // Create Vector2 vertices
    8.         Vector2[] vertices2D = new Vector2[] {
    9.             new Vector2(0,0),
    10.             new Vector2(0,50),
    11.             new Vector2(50,50),
    12.             new Vector2(50,100),
    13.             new Vector2(0,100),
    14.             new Vector2(0,150),
    15.             new Vector2(150,150),
    16.             new Vector2(150,100),
    17.             new Vector2(100,100),
    18.             new Vector2(100,50),
    19.             new Vector2(150,50),
    20.             new Vector2(150,0),
    21.         };
    22.  
    23.  
    24.  
    25.         // Use the triangulator to get indices for creating triangles
    26.         Triangulator2 tr = new Triangulator2(vertices2D);
    27.         int[] indices = tr.Triangulate();
    28.  
    29.         // Create the Vector3 vertices
    30.         Vector3[] vertices = new Vector3[vertices2D.Length];
    31.         for (int i = 0; i < vertices.Length; i++)
    32.         {
    33.             vertices[i] = new Vector3(vertices2D[i].x, vertices2D[i].y, 0);
    34.         }
    35.  
    36.         // Create the mesh
    37.         Mesh msh = new Mesh();
    38.         msh.vertices = vertices;
    39.         msh.triangles = indices;
    40.  
    41.         Vector3[] normalsToChange = msh.normals;
    42.  
    43.  
    44.  
    45.         msh.RecalculateNormals();
    46.         msh.RecalculateBounds();
    47.  
    48.         // Set up game object with mesh;
    49.         gameObject.AddComponent(typeof(MeshRenderer));
    50.         MeshFilter filter = gameObject.AddComponent(typeof(MeshFilter)) as MeshFilter;
    51.         filter.mesh = msh;
    52.     }
    53. }
    and this:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. public class Triangulator2
    5. {
    6.     private List<Vector2> m_points = new List<Vector2>();
    7.  
    8.     public Triangulator2(Vector2[] points)
    9.     {
    10.         m_points = new List<Vector2>(points);
    11.     }
    12.  
    13.     public int[] Triangulate()
    14.     {
    15.         List<int> indices = new List<int>();
    16.  
    17.         int n = m_points.Count;
    18.         if (n < 3)
    19.             return indices.ToArray();
    20.  
    21.         int[] V = new int[n];
    22.         if (Area() > 0)
    23.         {
    24.             for (int v = 0; v < n; v++)
    25.                 V[v] = v;
    26.         }
    27.         else
    28.         {
    29.             for (int v = 0; v < n; v++)
    30.                 V[v] = (n - 1) - v;
    31.         }
    32.  
    33.         int nv = n;
    34.         int count = 2 * nv;
    35.         for (int v = nv - 1; nv > 2;)
    36.         {
    37.             if ((count--) <= 0)
    38.                 return indices.ToArray();
    39.  
    40.             int u = v;
    41.             if (nv <= u)
    42.                 u = 0;
    43.             v = u + 1;
    44.             if (nv <= v)
    45.                 v = 0;
    46.             int w = v + 1;
    47.             if (nv <= w)
    48.                 w = 0;
    49.  
    50.             if (Snip(u, v, w, nv, V))
    51.             {
    52.                 int a, b, c, s, t;
    53.                 a = V[u];
    54.                 b = V[v];
    55.                 c = V[w];
    56.                 indices.Add(a);
    57.                 indices.Add(b);
    58.                 indices.Add(c);
    59.                 for (s = v, t = v + 1; t < nv; s++, t++)
    60.                     V[s] = V[t];
    61.                 nv--;
    62.                 count = 2 * nv;
    63.             }
    64.         }
    65.  
    66.         indices.Reverse();
    67.         return indices.ToArray();
    68.     }
    69.  
    70.     private float Area()
    71.     {
    72.         int n = m_points.Count;
    73.         float A = 0.0f;
    74.         for (int p = n - 1, q = 0; q < n; p = q++)
    75.         {
    76.             Vector2 pval = m_points[p];
    77.             Vector2 qval = m_points[q];
    78.             A += pval.x * qval.y - qval.x * pval.y;
    79.         }
    80.         return (A * 0.5f);
    81.     }
    82.  
    83.     private bool Snip(int u, int v, int w, int n, int[] V)
    84.     {
    85.         int p;
    86.         Vector2 A = m_points[V[u]];
    87.         Vector2 B = m_points[V[v]];
    88.         Vector2 C = m_points[V[w]];
    89.         if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))))
    90.             return false;
    91.         for (p = 0; p < n; p++)
    92.         {
    93.             if ((p == u) || (p == v) || (p == w))
    94.                 continue;
    95.             Vector2 P = m_points[V[p]];
    96.             if (InsideTriangle(A, B, C, P))
    97.                 return false;
    98.         }
    99.         return true;
    100.     }
    101.  
    102.     private bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
    103.     {
    104.         float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
    105.         float cCROSSap, bCROSScp, aCROSSbp;
    106.  
    107.         ax = C.x - B.x; ay = C.y - B.y;
    108.         bx = A.x - C.x; by = A.y - C.y;
    109.         cx = B.x - A.x; cy = B.y - A.y;
    110.         apx = P.x - A.x; apy = P.y - A.y;
    111.         bpx = P.x - B.x; bpy = P.y - B.y;
    112.         cpx = P.x - C.x; cpy = P.y - C.y;
    113.  
    114.         aCROSSbp = ax * bpy - ay * bpx;
    115.         cCROSSap = cx * apy - cy * apx;
    116.         bCROSScp = bx * cpy - by * cpx;
    117.  
    118.         return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
    119.     }
    120. }
     

    Attached Files:

    Last edited: Sep 6, 2019
  2. Hylus

    Hylus

    Joined:
    Jul 4, 2017
    Posts:
    7
    Well, I visualize my normals and they look fine.

    To visualize normlas i use this script:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TangentSpaceVisualizer : MonoBehaviour
    6. {
    7.     public float offset = 0.01f;
    8.     public float scale = 0.1f;
    9.  
    10.  
    11.     void OnDrawGizmos()
    12.     {
    13.         MeshFilter filter = GetComponent<MeshFilter>();
    14.         if (filter)
    15.         {
    16.             Mesh mesh = filter.sharedMesh;
    17.             if (mesh)
    18.             {
    19.                 ShowTangentSpace(mesh);
    20.             }
    21.         }
    22.     }
    23.  
    24.     void ShowTangentSpace(Mesh mesh)
    25.     {
    26.         Vector3[] vertices = mesh.vertices;
    27.         Vector3[] normals = mesh.normals;
    28.         for (int i = 0; i < vertices.Length; i++)
    29.         {
    30.             ShowTangentSpace(
    31.                              transform.TransformPoint(vertices[i]),
    32.                              transform.TransformDirection(normals[i])
    33.                             );
    34.         }
    35.     }
    36.  
    37.     void ShowTangentSpace(Vector3 vertex, Vector3 normal)
    38.     {
    39.         vertex += normal * offset;
    40.         Gizmos.color = Color.green;
    41.         Gizmos.DrawLine(vertex, vertex + normal * scale);
    42.     }
    43. }
    44.  
     

    Attached Files:

    Last edited: Sep 6, 2019
  3. Hylus

    Hylus

    Joined:
    Jul 4, 2017
    Posts:
    7