Silhouette style outline shader

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.

I posted a solution that won’t work. lol

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

1 Like

Render the outlines before the model

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.

Thank you both for the tips! I got it working perfectly.

1 Like

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?

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.

Can you show me part of your shader code? I’m not sure how to manipulate the stencil buffer.

1 Like

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?

Shader "Standard/Outline" {
    Properties {
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo", 2D) = "white" {}
      
        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

        _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
        [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        _MetallicGlossMap("Metallic", 2D) = "white" {}

        _BumpScale("Scale", Float) = 1.0
        _BumpMap("Normal Map", 2D) = "bump" {}

        _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
        _ParallaxMap ("Height Map", 2D) = "black" {}

        _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}

        _EmissionColor("Color", Color) = (0,0,0)
        _EmissionMap("Emission", 2D) = "white" {}
      
        _DetailMask("Detail Mask", 2D) = "white" {}

        _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
        _DetailNormalMapScale("Scale", Float) = 1.0
        _DetailNormalMap("Normal Map", 2D) = "bump" {}

        [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
        _OutlineColor ("Outline Color", Color) = (0,0,0,1)
        _Outline ("Outline width", Range (.002, 0.3)) = .005
        //_MainTex ("Base (RGB)", 2D) = "white" { }
        _ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { }
    }
  
    CGINCLUDE
    #include "UnityCG.cginc"
  
    struct appdata {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };

    struct v2f {
        float4 pos : SV_POSITION;
        UNITY_FOG_COORDS(0)
        fixed4 color : COLOR;
    };
  
    uniform float _Outline;
    uniform float4 _OutlineColor;
  
    v2f vert(appdata v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);

        float3 norm   = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
        float2 offset = TransformViewToProjection(norm.xy);

        #ifdef UNITY_Z_0_FAR_FROM_CLIPSPACE //to handle recent standard asset package on older version of unity (before 5.5)
            o.pos.xy += offset * UNITY_Z_0_FAR_FROM_CLIPSPACE(o.pos.z) * _Outline;
        #else
            o.pos.xy += offset * o.pos.z * _Outline;
        #endif
        o.color = _OutlineColor;
        UNITY_TRANSFER_FOG(o,o.pos);
        return o;
    }
    ENDCG

    SubShader {
        Tags { "RenderType"="Opaque" }
        UsePass "Standard/FORWARD"
        UsePass "Standard/FORWARD_DELTA"
        UsePass "Standard/ShadowCaster"
        UsePass "Standard/META"
        Pass {
            Name "OUTLINE"
            Tags { "LightMode" = "Always" }
            Cull Front
            ZWrite Off
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_APPLY_FOG(i.fogCoord, i.color);
                return i.color;
            }
            ENDCG
        }
    }
  
    Fallback "Toon/Basic"
}

code:

Inside the pass block you can do something like this to mark the object itself in the standard pass:

Stencil {
   Ref 1
   WriteMask 1
   Comp Always
   Pass Replace
}

Then in the outline part without backface culling, you do:

Stencil {
   Ref 0
   ReadMask 1
   Comp Equal
}

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.