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
}
}
}
Answer by glennMediaMonks · Oct 21, 2019 at 09:56 AM
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
}
}
}
Answer by Namey5 · Apr 05, 2017 at 01:51 AM
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
}
}
}
This does fix this example! It still runs the 3 calls in the wrong order, but then displays the blue on top due to the Offset. Very useful to know.
Unfortunately my actual shader uses the stencil buffer and really needs to run #0 then #1 then #2 to work. It'd odd that #0 always get called first, but subsequent passes end up in reverse order part of the time. $$anonymous$$y actual shader uses 5 passes which get rendered as: 0-4-3-2-1.
That is strange. I've never worked with more than two passes, so I'm not sure if this is intentional or some kind of bug. I suppose the obvious fix would be to just re-order the passes in that order, but I'll try and look around to see why this is the case.
Your answer
![](https://koobas.hobune.stream/wayback/20220612114716im_/https://answers.unity.com/themes/thub/images/avi.jpg)