I understand what Sycle did but I can’t use the custom tag on Unity 4. And I can’t just put the pass after the surface shader because it will consider the object mesh itself in the ztest greater. Anyone knows how to get it work?
Nevermind, I got it.
Here follows a sample (based on what Sycle and Farfarer wrote) that illustrate how to use multi pass with 3 different ways to write shaders in unity. Just uncomment the pass blocks to see the effect.
Shader "Character Indicator"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_Indicator ("Indicator Color", Color) = (1,1,1,1)
_Cutoff ("Alpha cutoff", Range (0,1)) = 0.0
}
SubShader
{
Tags { "Queue" = "Geometry+1" "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf BlinnPhong alphatest:_Cutoff
uniform float4 _Color;
uniform float4 _Indicator;
uniform sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
float3 viewDir;
};
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D ( _MainTex, IN.uv_MainTex).rgb * _Color;
}
ENDCG
// Pass: Surface SHADER
// ZWrite Off
// ZTest Greater
// Blend DstColor Zero
//
// CGPROGRAM
// #pragma surface surf BlinnPhong
// uniform float4 _Color;
// uniform float4 _Indicator;
// uniform sampler2D _MainTex;
//
// struct Input
// {
// float2 uv_MainTex;
// float3 viewDir;
// };
//
// void surf (Input IN, inout SurfaceOutput o)
// {
// //o.Albedo =_Indicator;
// o.Albedo = tex2D ( _MainTex, IN.uv_MainTex).rgb * _Indicator;
// }
// ENDCG
// Pass: CG SHADER
// Pass
// {
// Tags { "LightMode" = "Always" }
// AlphaTest Greater [_Cutoff]
// ZWrite Off
// ZTest Greater
//
// CGPROGRAM
// #pragma vertex vert
// #pragma fragment frag
// #pragma fragmentoption ARB_precision_hint_fastest
// #include "UnityCG.cginc"
//
// sampler2D _MainTex;
// float4 _MainTex_ST;
// uniform float4 _Indicator;
//
// struct v2f
// {
// float4 pos : POSITION;
// float2 uv : TEXCOORD1;
// };
//
// v2f vert (appdata_full v)
// {
// v2f o;
// o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
// o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
// return o;
// }
//
// half4 frag( v2f i ) : COLOR
// {
// half4 texcol = tex2D (_MainTex, i.uv);
// return texcol * _Indicator;
// }
// ENDCG
// }
// Pass: Fixed pipeline
// Pass
// {
// Tags { "LightMode" = "Always" }
// AlphaTest Greater [_Cutoff]
// ZWrite Off
// ZTest Greater
//
// SetTexture [_MainTex]
// {
// constantColor [_Indicator]
// //combine constant, texture
// combine constant* texture
// }
// }
}
Fallback " Glossy", 0
}
Charles, your single post has helped my understanding more than the last few hours of research. Thanks a million for taking the time to post this code and the accompanying images. ![]()
This is an older post but is exactly what I am looking for however I cant seem to get it working.
The double surface trick works but not the CG or fixed function pass and what I need to do is run a surface shader for non occluded parts then follow up with a CG rendering only the occluded parts …
I tryed using the code you have posted above and commented each 1 by 1 again the surface surface works as expect but not surface + CG or surface + fixed
PS: I have worked around this by just using surface shaders as opposed to a vert/frag CG; also I note you need to switch the order
below is an example
SubShader
{
Tags { "Queue" = "AlphaTest" "RenderType" = "Opaque" }
ZWrite Off
ZTest Greater
Blend One One
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
//sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimWidth;
void surf (Input IN, inout SurfaceOutput o) {
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimWidth);
}
ENDCG
ZWrite On
ZTest LEqual
Blend Off
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
This does the following

Thank you lodendsg! Given my beginner’s understanding, I was pulling my hair to realize, why I cannot combine a CG pass with a depth offset parameter, no mater what combinations of code I tried. While the same time, due to the 3.4 version of Unity that I currently use, it doesn’t seem to work with the structure the older posts had recommended. It is your example that showed me the correct way, although I’m not sure that I fully understand, how the order of code blocks is significant for a proper function…![]()
Can i ask is this possible using an Indie version of Unity?
im pretty new to shaders but can understnad Surface Shaders but I cant any of these shaders to work?
Also are these Multi pass more expensive because they use surface shaders?
Here’s what I have so far:
Shader "Custom/MultiPass Surface Shader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
_BumpMap ("Bump (RGB) Gloss (A)", 2D) = "bump" {}
_RimColor ("Rim Color", Range (0, 1)) = 0.5
_RimWidth ("Rim Width", Range (0, 1)) = 0.5
}
SubShader
{
Tags { "Queue" = "AlphaTest" "RenderType" = "Opaque" }
ZWrite Off
ZTest Greater
Blend One One
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
//sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimWidth;
void surf (Input IN, inout SurfaceOutput o) {
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow (rim, _RimWidth);
}
ENDCG
ZWrite On
ZTest LEqual
Blend Off
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
Fallback "Diffuse"
}
and here is the error I keep getting?
“Shader error in ‘Custom/MultiPass Surface Shader’: Parse error: syntax error at line 107”
Any help?
Thanks.
hey farfarer, i just tested your hair shader. i just noticed that the aniso color(white) is just too strong when not lighted. the darker the area the stronger the aniso is. is there a way to control the intensity of aniso?
hey, i’m folowing this thread with great interest and it helped me to successfully achieve some effects.
i have a question : is there a way to keep a variable over the passes ?
As far as I know - it is not possible. You can pass data from vertex program to fragment program or from script to shader but not from pass to pass.
mmm too bad, may be a setPixel/getPixel to store a value ? or is ot too slow ?
thanks a lot
SetPixel/GetPixel is really slow but if it’s not a problem for you then why not. In general- reading data from GPU is tricky. I believe that you can do that with Compute Shaders but they are probably not what you are looking for. Also, if you need a specific variable to be passed for both passes you may want to create C# script, compute whatever you need to and pass it to the shader/material.
i’ll follow your advice and avoid set/get pixel then
thanks a lot
Old post, but look into Stencil shaders where you can easily assign a 0-255 integer value to the pixels of each pass and then use those values for basic calculations in other passes. ![]()
You can write your image effect or compute buffer - store your values in custom render target and assign them for your passes (blur effect is done this way). Compute buffers are also an option since they can return calculation values or textures back to the CPU.
This whole shader thingy can get so complicated in minutes!
I just started learning but after every line of code i feel like i “need more”…
Hi!
Sorry to bump this up, but I have a question that may help a lot people that comes and see this, which is a rather famous post in the forum.
I’m trying to do a multipass shader, but I need the _MainTex of the first pass, since I’m making one operation and then, once the maintex is done, do another one, but this doesn’t work because I get the old maintex again.
How can I achieve that?
Thanks!
You mean, you manipulate the fragments of the _MainTex in the first pass and want to access the manipulated _MainTex in the second pass?
Won’t work. Since you are not manipulating any of the input texture data. You are manipulating the output to the framebuffer in a shader. The input textures are mere variables that help shaping the output.
Passes only blend what’s already in the framebuffer with the output they themself generate in a specified manner.
And that’s the answer to your challenge. You can either do it with clever pass blending. Or if you absolutely need to manipulate texture data for usage in subsequent passes because there is no blend operation that suits your requirements you probably need to do this with rendertextures and Graphics.Blit()
This solution worked for me!
This doesn’t seem to work for some reason when mixing surface shader and fragment shaders.
I have two surface shaders passes, and those work fine, but the fragment pass after them fails to do anything, even if I turn off ZTest and ZWrite. I’ve tried making it even wildly adjust the vertices to grow the mesh in that final pass, but nothing happens… It’s like Unity is throwing away that fragment pass because of the surface shader passes being there. Doesn’t matter if I put the fragment pass before them or after.
Shader "Invertex/PassTest"
{
Properties
{
_Color ("Front Tint", Color) = (1,1,1,1)
_BackColor ("Rear Tiny", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Geometry" }
LOD 200
//Front regular pass
ZWrite true
Cull back
ZTest true
CGPROGRAM
#pragma surface surf Standard vertex:vert addshadow
#pragma target 3.0
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
fixed4 _Color;
struct Input
{
float2 uv_MainTex;
};
void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
}
void surf(Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
//Rear pass.
ZTest on
ZWrite on
Cull Front
CGPROGRAM
#pragma surface surf Standard vertex:vert
#pragma target 3.0
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
fixed4 _BackColor;
struct Input
{
float2 uv_MainTex;
};
void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex) * _BackColor;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
Pass
{
ZWrite true
ZTest false
Cull off
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;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
v.vertex += float4(4, 5, 3, 0);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col + float4(3,3,5,2);
}
ENDCG
}
}
FallBack "Diffuse"
}
Unity 2017.3.1.
Hey all, sorry to dig up an old thread but it’s been really helpful so far in helping me get to where I want.
The problem I’m running into is that I want to have a two-pass Surface shader, where the first pass only responds to Directional lights, and the second pass responds only to Spot/Point lights.
So far, I’ve tried two solutions that I thought would work. The first is using Pass Tags to switch between ForwardBase (Directional only) and ForwardAdd (Spot and Point) like so:
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
...
ENDCG
Tags {"LightMode"="ForwardAdd"}
ZWrite Off
ZTest Greater
Blend DstColor Zero
CGPROGRAM
...
ENDCG
This just makes everything black.
The other thing I’ve tried is using Compile Directives to turn off Forward Add for the first pass, like so:
CGPROGRAM
#pragma surface surf Standard fullforwardshadows noforwardadd
...
ENDCG
ZWrite Off
ZTest Greater
Blend DstColor Zero
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
...
ENDCG
This time it renders, but now it doesn’t respond to Spot or Point Lights for either pass.
Is there any way to have the Compile Directive work for just the one pass? Or to make the second pass a Forward Add pass?

