Shader passes being run in wrong order?

I have a shader containing 3 passes that must be executed in an exact order. In my example, pass #0 sets the color to red, pass #1 sets it to green, pass #3 sets it to blue. If they run in the right order, the object is blue. (My actual shader uses the stencil buffer which is why the render order matters).

Depending on the camera angle and other objects in the scene, the order that the 3 passes are rendered changes. I can verify this via the Frame Debug tool which shows it alternates between rendering pass #0, then 1, then 2 (blue - correct) or rendering pass #0, then 3, then 2 (green - broken). The result is that the object flickers between blue/green as the camera moves. It’s affected by adding or removing other objects behind the main one, and if I clear everything else from the scene the object is always blue (correct).

Is this a bug in Unity 5.5.2f1?

Is there a way to force shader passes to always run in the order they are written in the .shader file?

Shader "WallThrough/WallThroughEverything" {
	SubShader{

		// PASS #0 RED
		Pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			struct v2f {
				float4  pos : SV_POSITION;
				float2  uv : TEXCOORD0;
			};

			struct VertexIn
			{
				float4 vertex  : POSITION;
			};

			v2f vert(VertexIn v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				return o;
			}

			half4 frag(v2f i) : COLOR	
			{
				return float4(1,0,0,1);
			}
			ENDCG
		}

		// PASS #1 GREEN
		Pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			struct v2f {
				float4  pos : SV_POSITION;
				float2  uv : TEXCOORD0;
			};

			struct VertexIn
			{
				float4 vertex  : POSITION;
			};

			v2f vert(VertexIn v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				return o;
			}

			half4 frag(v2f i) : COLOR
			{
				return float4(0,1,0,1);
			}
			ENDCG
		}

		// PASS #2 BLUE
		Pass{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			struct v2f {
				float4  pos : SV_POSITION;
				float2  uv : TEXCOORD0;
			};

			struct VertexIn
			{
				float4 vertex  : POSITION;
			};

			v2f vert(VertexIn v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				return o;
			}

			half4 frag(v2f i) : COLOR
			{
				return float4(0,0,1,1);
			}
			ENDCG
		}
	}
}

Try using offset to order each pass individually, ignoring actual position. The passes are all being drawn in the same position, and the camera doesn’t know how to handle this. The following should work;

 Shader "WallThrough/WallThroughEverything" {
     SubShader{
 
         // PASS #0 RED
         Pass{
             Offset 1, 1

             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
 
             struct v2f {
                 float4  pos : SV_POSITION;
                 float2  uv : TEXCOORD0;
             };
 
             struct VertexIn
             {
                 float4 vertex  : POSITION;
             };
 
             v2f vert(VertexIn v)
             {
                 v2f o;
                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                 return o;
             }
 
             half4 frag(v2f i) : COLOR    
             {
                 return float4(1,0,0,1);
             }
             ENDCG
         }
 
         // PASS #1 GREEN
         Pass{
             Offset 0, 0

             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
 
             struct v2f {
                 float4  pos : SV_POSITION;
                 float2  uv : TEXCOORD0;
             };
 
             struct VertexIn
             {
                 float4 vertex  : POSITION;
             };
 
             v2f vert(VertexIn v)
             {
                 v2f o;
                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                 return o;
             }
 
             half4 frag(v2f i) : COLOR
             {
                 return float4(0,1,0,1);
             }
             ENDCG
         }
 
         // PASS #2 BLUE
         Pass{
             Offset -1, -1

             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
 
             struct v2f {
                 float4  pos : SV_POSITION;
                 float2  uv : TEXCOORD0;
             };
 
             struct VertexIn
             {
                 float4 vertex  : POSITION;
             };
 
             v2f vert(VertexIn v)
             {
                 v2f o;
                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                 return o;
             }
 
             half4 frag(v2f i) : COLOR
             {
                 return float4(0,0,1,1);
             }
             ENDCG
         }
     }
 }

@Namey5 @sarahnorthway

Here is my take: I had a similar issue with passes being rendered in an inconsistent order. I will explain below what I saw, how to change it, and share my shader.

my shader works as follows:
pass 1. additively add 0.5 blue
pass 2. multiply all color channels with 0.5
pass 3. additively add 0.5 red

put a material with this shader on 2 quads in an empty scene, with the camera rendering just black.
depending on where you put the quads and which renderQueue you use you can get 3 results. See image.

the top 2 results are from a renderqueue on 2500 or below. Unity seems to switch between different modes of optimizing on how to render the objects / how to sort the passes.

Version 1. the bright red and quarter blue indicates the order is 1,1,2,2,3,3
Version 2. the 3/4 red indicates and quarter blue indicates 1,1,2,3,2,3

the bottom result is from a RenderQueue on 2501 or higher.
Version 3. the 3/4 red ad 1/3 blue indicates 1,2,3,1,2,3 (which is prob what most people expect)

It seems that 2500 and below queues are seen as Opaque objects, for these Objects Unity can assume that the pass order shouldn’t matter in total (per object the order always stays 1,2,3) and thus can try to optimize the process of how the passes are sorted for the GPU vs in what order to render the objects (closest to camera first) this change in pass order can be seen by moving the camera or the object, where you will sometimes see Version 1 or 2.

2501 and above are transparent and unity enforces the pass order, as otherwise you will get these types of visual changes.

Shader "Unlit/PassTest2"
{
    Properties
    {
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

		
		Pass
		{
			ZWrite on // off
			ZTest Always// LEqual
			cull off
			Blend One One
 

			CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };


            struct v2f
            {
                float4 vertex : SV_POSITION;

            };

            v2f vert (appdata v)
            {
                v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return float4(0,0,0.5,1);
            }
            ENDCG
		}
		

		
        Pass
        {
			ZWrite on // off
			ZTest Always// LEqual
			cull off
			Blend DstColor Zero

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;

            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;

            };



            v2f vert (appdata v)
            {
                v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
            }



            fixed4 frag (v2f i) : SV_Target
            {
				return float4(0.5,0.5,0.5,1);
            }
            ENDCG
        }
		


		
		Pass
		{
			ZWrite on // off
			ZTest Always// LEqual
			cull off
			Blend One One
 

			CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };


            struct v2f
            {
                float4 vertex : SV_POSITION;

            };

            v2f vert (appdata v)
            {
                v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return float4(0.5,0,0,1);
            }
            ENDCG

		}
    }
}