Search Unity

Silhouette style outline shader

Discussion in 'Shaders' started by GarrettShields, Nov 21, 2016.

  1. GarrettShields

    GarrettShields

    Joined:
    Feb 1, 2016
    Posts:
    32
    I've tried using a few different outline shaders / methods but I can't find what I'm looking for.

    I'm trying to achieve an outline that is only on the border of a mesh, never in the middle of it.

    This is exactly what I am trying to achieve


    This is the result I get with the outline built into shader forge


    I'm open to ideas / theory on how to accomplish this as I'm not really sure where to start.
     
  2. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,684
    I posted a solution that won't work. lol

    I would be interested in the solution to this as well, simply to know it.
     
    GarrettShields likes this.
  3. J_P_

    J_P_

    Joined:
    Jan 9, 2010
    Posts:
    1,027
    Render the outlines before the model
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    To expand on this, render the outlines as a "vertex normal push" style outline mesh (like used in the Standard Assets Toon Outline shader), but with zwrite turned off.

    This will cause issues with rendering against a skybox, so to use this method you need to be not using normal Unity skyboxes (which render after opaque objects into any areas where nothing has written to the z buffer). The alternative is to render the outline mesh, then render the normal mesh, but only to depth and with the fragment shader writing depth to "clear" the zbuffer inside where the outline mesh already rendered, then render the "real" mesh.

    The other way to do this is with a two step post process that renders your highlight objects to an offscreen texture as flat colors, then applying a shader to extract an outline from that. There are a couple of assets on the store which do this.
     
  5. GarrettShields

    GarrettShields

    Joined:
    Feb 1, 2016
    Posts:
    32
    Thank you both for the tips! I got it working perfectly.
     
    hopeful likes this.
  6. robin911

    robin911

    Joined:
    Nov 21, 2016
    Posts:
    6
    I am working on a shader just like this. But the targets are combined with several parts. So the outline mesh for one part will be rendered on the normal mesh for other parts. Do you have any solution for this problem?
     
  7. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I do it by setting the stencil buffer during the main pass and then rejecting based on the stencil buffer in the outline pass. That way I can still use the standard Unity skyboxes. It also doesn't require any extra RenderTexture.

    Edit: I also often partly show the outline through other objects, so I can't really rely on draw order in these cases anyway.
     
  8. robin911

    robin911

    Joined:
    Nov 21, 2016
    Posts:
    6
    Can you show me part of your shader code? I'm not sure how to manipulate the stencil buffer.
     
    hopeful likes this.
  9. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    973
    I have tried to create a Standard/Outline shader.
    I replaced the single UsePass from the Standard Assets Toon Outline shader with passes from Standard shader.
    But I can't see the outline.
    What can be wrong with the shader code below?
    Code (CSharp):
    1. Shader "Standard/Outline" {
    2.     Properties {
    3.         _Color("Color", Color) = (1,1,1,1)
    4.         _MainTex("Albedo", 2D) = "white" {}
    5.      
    6.         _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
    7.  
    8.         _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
    9.         [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
    10.         _MetallicGlossMap("Metallic", 2D) = "white" {}
    11.  
    12.         _BumpScale("Scale", Float) = 1.0
    13.         _BumpMap("Normal Map", 2D) = "bump" {}
    14.  
    15.         _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
    16.         _ParallaxMap ("Height Map", 2D) = "black" {}
    17.  
    18.         _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
    19.         _OcclusionMap("Occlusion", 2D) = "white" {}
    20.  
    21.         _EmissionColor("Color", Color) = (0,0,0)
    22.         _EmissionMap("Emission", 2D) = "white" {}
    23.      
    24.         _DetailMask("Detail Mask", 2D) = "white" {}
    25.  
    26.         _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
    27.         _DetailNormalMapScale("Scale", Float) = 1.0
    28.         _DetailNormalMap("Normal Map", 2D) = "bump" {}
    29.  
    30.         [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
    31.         _OutlineColor ("Outline Color", Color) = (0,0,0,1)
    32.         _Outline ("Outline width", Range (.002, 0.3)) = .005
    33.         //_MainTex ("Base (RGB)", 2D) = "white" { }
    34.         _ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
    35.     }
    36.  
    37.     CGINCLUDE
    38.     #include "UnityCG.cginc"
    39.  
    40.     struct appdata {
    41.         float4 vertex : POSITION;
    42.         float3 normal : NORMAL;
    43.     };
    44.  
    45.     struct v2f {
    46.         float4 pos : SV_POSITION;
    47.         UNITY_FOG_COORDS(0)
    48.         fixed4 color : COLOR;
    49.     };
    50.  
    51.     uniform float _Outline;
    52.     uniform float4 _OutlineColor;
    53.  
    54.     v2f vert(appdata v) {
    55.         v2f o;
    56.         o.pos = UnityObjectToClipPos(v.vertex);
    57.  
    58.         float3 norm   = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
    59.         float2 offset = TransformViewToProjection(norm.xy);
    60.  
    61.         #ifdef UNITY_Z_0_FAR_FROM_CLIPSPACE //to handle recent standard asset package on older version of unity (before 5.5)
    62.             o.pos.xy += offset * UNITY_Z_0_FAR_FROM_CLIPSPACE(o.pos.z) * _Outline;
    63.         #else
    64.             o.pos.xy += offset * o.pos.z * _Outline;
    65.         #endif
    66.         o.color = _OutlineColor;
    67.         UNITY_TRANSFER_FOG(o,o.pos);
    68.         return o;
    69.     }
    70.     ENDCG
    71.  
    72.     SubShader {
    73.         Tags { "RenderType"="Opaque" }
    74.         UsePass "Standard/FORWARD"
    75.         UsePass "Standard/FORWARD_DELTA"
    76.         UsePass "Standard/ShadowCaster"
    77.         UsePass "Standard/META"
    78.         Pass {
    79.             Name "OUTLINE"
    80.             Tags { "LightMode" = "Always" }
    81.             Cull Front
    82.             ZWrite Off
    83.             ColorMask RGB
    84.             Blend SrcAlpha OneMinusSrcAlpha
    85.  
    86.             CGPROGRAM
    87.             #pragma vertex vert
    88.             #pragma fragment frag
    89.             #pragma multi_compile_fog
    90.             fixed4 frag(v2f i) : SV_Target
    91.             {
    92.                 UNITY_APPLY_FOG(i.fogCoord, i.color);
    93.                 return i.color;
    94.             }
    95.             ENDCG
    96.         }
    97.     }
    98.  
    99.     Fallback "Toon/Basic"
    100. }
    code:
     
  10. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Inside the pass block you can do something like this to mark the object itself in the standard pass:
    Code (csharp):
    1.  
    2. Stencil {
    3.    Ref 1
    4.    WriteMask 1
    5.    Comp Always
    6.    Pass Replace
    7. }
    8.  
    Then in the outline part without backface culling, you do:
    Code (csharp):
    1.  
    2. Stencil {
    3.    Ref 0
    4.    ReadMask 1
    5.    Comp Equal
    6. }
    7.  
     
  11. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    973
    If the gameobject renderer consist of a single submesh you could just put two materials on it. The first one (its shader) could just use "OUTLINE" pass from the Standard Assets Toon Outline shader, and the second one could be just any material. You could just set the render queue of the outline shader to Geometry-1.