Is there a shader to only add an outline?

The toon shaders have outlines, but also change the shading of the object. I'd like to show an outline without affecting the rest of the shading. Also if possible, only show outline around the perimeter of the object and not on edges in the middle.

I have no idea how to program shaders, so if anyone knows how to do this or can point me in the direction of a shader that already exists, I would appreciate it greatly. Thanks!

Bumping an extremely old post here, but considering the fact that I came by it when trying to figure this out myself here’s for the others that might end up here:

(from the bottom of: http://wiki.unity3d.com/index.php/Silhouette-Outlined_Diffuse)

[31325-outlined-silhouette+only.zip|31325]

This will give you an outline only, based on an input color.
All you gotta do is add it as a separate material to your target object.

Old post, but also highest ranked on google for this topic. I just spent several hours writing a geometry shader that’s much improved compared to a toon or silhouette shader by taking every edge in the mesh and turning those edges into quads rendered facing the camera. This means that it works on cubes in a satisfying manner. Be aware that this is not a mobile-friendly shader, due to using the stencil buffer and rendering the object twice, and requires forward lighting. It also does not render the object itself, just the outline (due to my own needs) but should be easily modifiable to render the original mesh as well.

Note that because verts are supplied going clockwise around the surface normal, single sided quads will only render 3 edges (the other two are facing away from the camera). Similarly, at hard corners, the corners will be noticeably thinner than other areas, but this is only a problem at very high thickness values (greater than 10, which is pretty darn thick). This has to do with the quad’s corners not being fused with the neighboring triangle’s quads.

Both of these problems are technically solvable, but wasn’t worth the effort for my use, due to only being apparent in edge-cases I wasn’t going to be using. The former can be solved by rendering the front and back face of each quad (and correspondingly increasing maxvertexcount by double). The latter might be solved by using triangleadj in the geometry shader portion, instead of triangle, and calculating intersection points. See: Geometry-Shader Object - Win32 apps | Microsoft Learn

If there’s an issue with it rendering strangely, swap float4 parallel = end.pos-start.pos; for float4 parallel = start.pos-end.pos; which I had to do when I merged this shader into another for use as a replacement shader that handles particular special rendering effects. Why? No idea.

Shader "Stencil/Outline" 
{
	Properties 
	{
		_Color("Color", Color) = (1,0,0,1)
		_Thickness("Thickness", float) = 4
	}
	SubShader 
	{
	
	    Tags { "Queue"="Geometry" "IgnoreProjector"="True" "RenderType"="Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		Cull Back
		ZTest always
		Pass
		{
			Stencil {
	            Ref 1
	            Comp always
	            Pass replace
	        }
	        CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fog
			
			#include "UnityCG.cginc"
			
			struct v2g 
			{
    			float4  pos : SV_POSITION;
    			float2  uv : TEXCOORD0;
				float3 viewT : TANGENT;
				float3 normals : NORMAL;
			};
			
			struct g2f 
			{
    			float4  pos : SV_POSITION;
    			float2  uv : TEXCOORD0;
				float3  viewT : TANGENT;
				float3  normals : NORMAL;
			};

			v2g vert(appdata_base v)
			{
    			v2g OUT;
    			OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    			OUT.uv = v.texcoord; 
 				OUT.normals = v.normal;
				OUT.viewT = ObjSpaceViewDir(v.vertex);
				
    			return OUT;
			}
			
			half4 frag(g2f IN) : COLOR
			{
				//this renders nothing, if you want the base mesh and color
				//fill this in with a standard fragment shader calculation
				return 0;
			}
			ENDCG
		}
    	Pass 
    	{
			Stencil {
	            Ref 0
	            Comp equal
	        }
			CGPROGRAM
			#include "UnityCG.cginc"
			#pragma target 4.0
			#pragma vertex vert
			#pragma geometry geom
			#pragma fragment frag
			
			
			half4 _Color;
			float _Thickness;
		
			struct v2g 
			{
    			float4 pos : SV_POSITION;
    			float2 uv : TEXCOORD0;
				float3 viewT : TANGENT;
				float3 normals : NORMAL;
			};
			
			struct g2f 
			{
    			float4 pos : SV_POSITION;
    			float2 uv : TEXCOORD0;
				float3 viewT : TANGENT;
				float3 normals : NORMAL;
			};

			v2g vert(appdata_base v)
			{
    			v2g OUT;
    			OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    			
    			OUT.uv = v.texcoord;
 				OUT.normals = v.normal;
				OUT.viewT = ObjSpaceViewDir(v.vertex);
				
    			return OUT;
			}
			
			void geom2(v2g start, v2g end, inout TriangleStream<g2f> triStream)
			{
				float thisWidth = _Thickness/100;
				float4 parallel = end.pos-start.pos;
				normalize(parallel);
				parallel *= thisWidth;
				
				float4 perpendicular = float4(parallel.y,-parallel.x, 0, 0);
				perpendicular = normalize(perpendicular) * thisWidth;
				float4 v1 = start.pos-parallel;
				float4 v2 = end.pos+parallel;
				g2f OUT;
				OUT.pos = v1-perpendicular;
				OUT.uv = start.uv;
				OUT.viewT = start.viewT;
				OUT.normals = start.normals;
				triStream.Append(OUT);
				
				OUT.pos = v1+perpendicular;
				triStream.Append(OUT);
				
				OUT.pos = v2-perpendicular;
				OUT.uv = end.uv;
				OUT.viewT = end.viewT;
				OUT.normals = end.normals;
				triStream.Append(OUT);
				
				OUT.pos = v2+perpendicular;
				OUT.uv = end.uv;
				OUT.viewT = end.viewT;
				OUT.normals = end.normals;
				triStream.Append(OUT);
			}
			
			[maxvertexcount(12)]
			void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
			{
				geom2(IN[0],IN[1],triStream);
				geom2(IN[1],IN[2],triStream);
				geom2(IN[2],IN[0],triStream);
			}
			
			half4 frag(g2f IN) : COLOR
			{
				_Color.a = 1;
				return _Color;
			}
			
			ENDCG

    	}
	}
	FallBack "Diffuse"
}

Find and download the Strumpy Shader Editor in the Asset Store (it's free!) and you can probably get exactly what you want to.

http://wiki.unity3d.com/index.php/Shader_Archive

Try looking through these.

I recommend this post by Boolean:

http://forum.unity3d.com/threads/trying-to-make-a-silhouette-outline-shader.96005/#post-1408098

In my opinion, it’s better than the Silhouette Ouline Diffuse shader from the wiki, it hasn’t got the same overlap issues and the effect is also very cool.

Also take a look at my own modifications to this shader as detailed here:

For what it’s worth, I made some small changes to this outline shader to make it a little more efficient, which works fine in Deferred rendering mode in 5.6. I specifically modified the second pass to NOT render the object, so this will only render the outline, which is great for programmatic effects like selection using Graphics.DrawMesh(). Anyway, this shader doesn’t make nice looking cubes, so I won’t be using it, but figured I’d post a working version anyway, for posterity’s benefit.

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

 Shader "Custom/Outline"
 {
     Properties 
     {
         _Color("Color", Color) = (1,0,0,1)
         _Thickness("Thickness", float) = 4
     }
     SubShader 
     {
     
         Tags { "Queue"="Geometry" "IgnoreProjector"="True" "RenderType"="Opaque" }
         Blend SrcAlpha OneMinusSrcAlpha
         Cull Back
         ZTest always
         Pass
         {
             Stencil {
				 WriteMask 1
                 Ref 255
                 Comp always
                 Pass replace
             }
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #pragma multi_compile_fog
             
             #include "UnityCG.cginc"

             struct v2g_small
             {
                 float4  pos : SV_POSITION;
             };
             
             struct g2f_small
             {
                 float4  pos : SV_POSITION;
             };

             v2g_small vert(appdata_base v)
             {
                 v2g_small OUT;
                 OUT.pos = UnityObjectToClipPos(v.vertex);
                 return OUT;
             }
             
             half4 frag(g2f_small IN) : COLOR
             {
                 //this renders nothing, if you want the base mesh and color
                 //fill this in with a standard fragment shader calculation
                 return 0;
             }
             ENDCG
         }
         Pass 
         {
             Stencil {
				 ReadMask 1
                 Ref 0
                 Comp equal
             }
             CGPROGRAM
             #include "UnityCG.cginc"
             #pragma target 4.0
             #pragma vertex vert
             #pragma geometry geom
             #pragma fragment frag
             
             
             half4 _Color;
             float _Thickness;
         
             struct v2g 
             {
                 float4 pos : SV_POSITION;
                 float2 uv : TEXCOORD0;
                 float3 viewT : TANGENT;
                 float3 normals : NORMAL;
             };
             
             struct g2f 
             {
                 float4 pos : SV_POSITION;
                 float2 uv : TEXCOORD0;
                 float3 viewT : TANGENT;
                 float3 normals : NORMAL;
             };
 
             v2g vert(appdata_base v)
             {
                 v2g OUT;
                 OUT.pos = UnityObjectToClipPos(v.vertex);
                 
                 OUT.uv = v.texcoord;
                  OUT.normals = v.normal;
                 OUT.viewT = ObjSpaceViewDir(v.vertex);
                 
                 return OUT;
             }
             
             void geom2(v2g start, v2g end, inout TriangleStream<g2f> triStream)
             {
                 float thisWidth = _Thickness/100;
                 float4 parallel = end.pos-start.pos;
                 normalize(parallel);
                 parallel *= thisWidth;
                 
                 float4 perpendicular = float4(parallel.y,-parallel.x, 0, 0);
                 perpendicular = normalize(perpendicular) * thisWidth;
                 float4 v1 = start.pos-parallel;
                 float4 v2 = end.pos+parallel;
                 g2f OUT;
                 OUT.pos = v1-perpendicular;
                 OUT.uv = start.uv;
                 OUT.viewT = start.viewT;
                 OUT.normals = start.normals;
                 triStream.Append(OUT);
                 
                 OUT.pos = v1+perpendicular;
                 triStream.Append(OUT);
                 
                 OUT.pos = v2-perpendicular;
                 OUT.uv = end.uv;
                 OUT.viewT = end.viewT;
                 OUT.normals = end.normals;
                 triStream.Append(OUT);
                 
                 OUT.pos = v2+perpendicular;
                 OUT.uv = end.uv;
                 OUT.viewT = end.viewT;
                 OUT.normals = end.normals;
                 triStream.Append(OUT);
             }
             
             [maxvertexcount(12)]
             void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
             {
                 geom2(IN[0],IN[1],triStream);
                 geom2(IN[1],IN[2],triStream);
                 geom2(IN[2],IN[0],triStream);
             }
             
             half4 frag(g2f IN) : COLOR
             {
                 _Color.a = 1;
                 return _Color;
             }
             
             ENDCG
 
         }
     }
 }

Shader “Custom/OutlineOnly” {
Properties {
_OutlineColor (“Outline Color”, Color) = (0,0,0,1)
_Outline (“Outline width”, Range (0.0, 1)) = 0.02
}

    CGINCLUDE
    #include "UnityCG.cginc"
    
    struct appdata {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };
    
    struct v2f {
        float4 pos : POSITION;
        float4 color : COLOR;
    };
    
    uniform float _Outline;
    uniform float4 _OutlineColor;
    
    v2f vert(appdata v) {
        v2f o;
        v.vertex.xyz += normalize(v.normal) * _Outline;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.color = _OutlineColor;
        return o;
    }
    ENDCG
    
    SubShader {
        Tags { "Queue" = "Transparent" }
        
        Pass {
            Name "BASE"
            Cull Back
            Blend Zero One
        }
        Pass {
            Name "OUTLINE"
            Tags { "LightMode" = "Always" }
            Cull Front
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            half4 frag(v2f i) :COLOR {
                return i.color;
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

Just outline, tested in Unity 2021.2.1