- Home /
Change blend mode from outside shader?
I've been doing something which requires quite a lot of rendering transparent objects to texture.
These objects are used directly in the scene with standard alpha blending ("Blend SrcAlpha OneMinusSrcAlpha"). In order to get the correct alpha when rendering to texture, I need to use pre-multiplied alpha ("Blend One OneMinusSrcAlpha").
Is there any way to change the blend mode from a script, so I don't have to duplicate every single shader just to change one line?
Answer by Philipp · Feb 14, 2013 at 11:14 AM
I'm 99% certain this isn't possible. However your various shader versions can use #include or UsePass to minimize code duplication.
That's a shame. I hadn't considered #includes / UsePass though. I'll test a few things, then comment with what I think works best.
I couldn't really get UsePass to work properly, so I ended up using a #include with the main body of the shader in it and one #define. Something like this:
Shader "Foo/Foo"
{
Properties
{
// ...
}
SubShader
{
Tags
{
// ...
}
// The one line difference...
Blend SrcAlpha One$$anonymous$$inusSrcAlpha
AlphaTest Greater .01
Color$$anonymous$$ask RGBA
// ...
Pass
{
CGPROGRA$$anonymous$$
// pragmas have to be before the include
#pragma vertex vert
#pragma fragment frag
// one shader defines this, the other doesn't.
#define FOO
#include "Foo.cginc"
ENDCG
}
}
}
It's still way more code duplication than should be necessary, but it's neater than copying the entire file.
Answer by Juho_Oravainen · Jun 11, 2014 at 12:46 PM
Setting blend modes by scripting has been possible since 4.3.
On shaderlab add Float properties for the modes:
Properties {
MySrcMode ("SrcMode", Float) = 0
MyDstMode ("DstMode", Float) = 0
}
Pass {
Blend [MySrcMode] [MyDstMode]
}
On script side update the values with Material.SetInt() and BlendMode enums:
material.SetInt ("MySrcMode", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt ("MyDstMode", (int)UnityEngine.Rendering.BlendMode.Zero);
Edit: Added typecasts as suggested by badweasel
This works. The only thing is that you have to typecast the blend modes as they are enums...
material.SetInt ("$$anonymous$$ySrc$$anonymous$$ode", (int)UnityEngine.Rendering.Blend$$anonymous$$ode.SrcAlpha);
material.SetInt ("$$anonymous$$yDst$$anonymous$$ode", (int)UnityEngine.Rendering.Blend$$anonymous$$ode.Zero);
Damn, i wish i saw this a few months back. I had been maintaining two shaders which differed only in blend mode.
Any idea about the performance implications of using this on mobile? Assu$$anonymous$$g they don't change at runtime, just so we have less shaders to deal with.
Answer by PicklesIIDX · Feb 16, 2018 at 05:55 PM
The standard shader has properties called _DstBlend and _SrcBlend which you can modify to get what you want. It's basically exactly like @Juho_Oraveinen answer, but it's built-in.
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);