Silhouette Outline Shader: Problem with overlapping outlines

I have a problem with the popular Silhouette Outline Shader from the Unity wiki:

Specifically, the problem occurs when two objects that both have the outline shader are very close to each other, as depending on the camera angle (I think), one of the two object’s outlines is visible even though it should be hidden behind the object in front of the surface.

Note that the objects have no outline thickness, so it’s just the silhouette that shows up if the object is behind another object that does not have an outline shader.

I don’t know much about shaders, but maybe there is a simple fix for this kind of behavior? From my very limited knowledge of shader coding I think it might have something to do with Z-testing/-writing or culling. Still, it’s weird that this only occurs if the two objects are very close to each other.

1: The two objects next to each other, both are the same size and height

2: How it should always work; The red cube is in front of the yellow one and nothing shows through.

3: the red cube is a little behind the yellow one and overlaps it. Here, the yellow cube’s silhouette shows through even though it should be hidden.

4: Here, the red cube is a little bit nearer to the camera, and suddenly it’s the red cube’s silhouette that is showing through





I have come to a solution:

I have used an outline occlusion shader by Boolean, it shows only the outline if it’s behind objects.
Note that I have modified the original code as found here:

I have replaced the flat shading if the pixel is normally visible with standard diffuse shading.
Here is the shader code:


Shader "Outlined/NewOcclusionOutline" {
Properties {
    _Color ("Main Color", Color) = (.5,.5,.5,1)
    _RimCol ("Rim Colour" , Color) = (1,0,0,1)
    _RimPow ("Rim Power", Float) = 1.0
    _MainTex ("Base (RGB)", 2D) = "white" {}
SubShader {
    Pass {
            Name "Behind"
            Tags { "RenderType"="transparent" "Queue" = "Transparent" }
            Blend SrcAlpha OneMinusSrcAlpha
            ZTest Greater               // here the check is for the pixel being greater or closer to the camera, in which
            Cull Back                   // case the model is behind something, so this pass runs
            ZWrite Off
            LOD 200                    
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : TEXCOORD1;      // Normal needed for rim lighting
                float3 viewDir : TEXCOORD2;     // as is view direction.
            sampler2D _MainTex;
            float4 _RimCol;
            float _RimPow;
            float4 _MainTex_ST;
            v2f vert (appdata_tan v)
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normal = normalize(v.normal);
                o.viewDir = normalize(ObjSpaceViewDir(v.vertex));       //this could also be WorldSpaceViewDir, which would
                return o;                                               //return the World space view direction.
            half4 frag (v2f i) : COLOR
                half Rim = 1 - saturate(dot(normalize(i.viewDir), i.normal));       //Calculates where the model view falloff is       
                                                                                                                                   //for rim lighting.
                half4 RimOut = _RimCol * pow(Rim, _RimPow);
                return RimOut;

        Pass {
			Name "BASE"
			ZWrite On
			ZTest LEqual
			Blend SrcAlpha OneMinusSrcAlpha
			Material {
				Diffuse [_Color]
				Ambient [_Color]
			Lighting On
			SetTexture [_MainTex] {
				ConstantColor [_Color]
				Combine texture * constant
			SetTexture [_MainTex] {
				Combine previous * primary DOUBLE
	//FallBack "VertexLit"
	FallBack "Diffuse"