http://answers.unity3d.com/questions/141229/making-a-silhouette-outline-shader.html
This shader uses a pass to create a slightly bigger mesh behind the original one.
This is a good solution (at least in Unity), but only for convex/non transparent object. The fragments of the outline will indeed appear behind the mesh:
We can remove the fragments behind the mesh modifying the depth buffer with a duplicated object.
The original object writes to the z-buffer, so the duplicated object (i.e. the one that act as an outline) will be partially culled by the original one.
In order to obtain this, we can use these shaders:
Transparent shader for the original object:
Shader "Outline/Transparent" {
Properties {
_color ("Color", Color) = (1,1,1,0.5)
}
SubShader {
Tags {"Queue" = "Geometry+1" }
Pass {
Blend SrcAlpha OneMinusSrcAlpha
Lighting On
ZWrite On
Material {
Diffuse [_color]
}
}
}
}
Outline shader for the outline, it will be applied to the duplicated object (Note: this is a mod of the shader quoted at the begin)
Shader "Outline/Outline" {
Properties {
_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_Outline ("Outline width", Range (.002, 0.03)) = .005
}
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) {
// just make a copy of incoming vertex data but scaled according to normal direction
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
float2 offset = TransformViewToProjection(norm.xy);
o.pos.xy += offset * o.pos.z * _Outline;
o.color = _OutlineColor;
return o;
}
ENDCG
SubShader {
Tags {"Queue" = "Overlay"}
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ZTest Less
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
Offset 15,15
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 frag(v2f i) :COLOR { return i.color; }
ENDCG
}
}
SubShader {
Tags {"Queue" = "Overlay" }
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma exclude_renderers gles xbox360 ps3
ENDCG
SetTexture [_MainTex] { combine primary }
}
}
Fallback "Diffuse"
}
The result is pretty good:

Finally, here it is a Unity script that automatically creates the outline effect when applied to an object:
using UnityEngine;
using System.Collections;
public class Outliner : MonoBehaviour {
public Color meshColor = new Color(1f,1f,1f,0.5f);
public Color outlineColor = new Color(1f,1f,0f,1f);
// Use this for initialization
public void Start () {
// Set the transparent material to this object
MeshRenderer meshRenderer = GetComponent();
Material[] materials = meshRenderer.materials;
int materialsNum = materials.Length;
for(int i = 0; i < materialsNum; i++) {
materials[i].shader = Shader.Find("Outline/Transparent");
materials[i].SetColor("_color", meshColor);
}
// Create copy of this object, this will have the shader that makes the real outline
GameObject outlineObj = new GameObject();
outlineObj.transform.position = transform.position;
outlineObj.transform.rotation = transform.rotation;
outlineObj.AddComponent();
outlineObj.AddComponent();
Mesh mesh;
mesh = (Mesh) Instantiate(GetComponent().mesh);
outlineObj.GetComponent().mesh = mesh;
outlineObj.transform.parent = this.transform;
materials = new Material[materialsNum];
for(int i = 0; i < materialsNum; i++) {
materials[i] = new Material(Shader.Find("Outline/Outline"));
materials[i].SetColor("_OutlineColor", outlineColor);
}
outlineObj.GetComponent().materials = materials;
}
}
