- Home /
How can I get Transparency into the Unity Toon Lighted Outline Shader?
I have been trying to figure out how to work shaders and I'm stumped. Does anyone have a modified version of the Basic Toon Lighted Outline shader that I can fade out over time? I would even take a cutout shader.
Bonus points if you can tell me how to modify it myself, I do want to learn this but I've spent all day trying and failing on it.
Answer by CHPedersen · Jun 30, 2014 at 10:29 AM
This is, thankfully, pretty easy. But it's going to involve a lot of code to explain what's going on. :) Ready? Brace yourself, it's mostly copy-pasted.
First off, let's start from the basic toon outline shader's source code, which looks like this:
Shader "Toon/Basic Outline" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_Outline ("Outline width", Range (.002, 0.03)) = .005
_MainTex ("Base (RGB)", 2D) = "white" { }
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { Texgen CubeNormal }
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : POSITION;
float4 color : COLOR;
};
uniform float _Outline;
uniform float4 _OutlineColor;
v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
float2 offset = TransformViewToProjection(norm.xy);
o.pos.xy += offset * o.pos.z * _Outline;
o.color = _OutlineColor;
return o;
}
ENDCG
SubShader {
Tags { "RenderType"="Opaque" }
UsePass "Toon/Basic/BASE"
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 frag(v2f i) :COLOR { return i.color; }
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" }
UsePass "Toon/Basic/BASE"
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma exclude_renderers shaderonly
ENDCG
SetTexture [_MainTex] { combine primary }
}
}
Fallback "Toon/Basic"
}
Now, you just want to make this transparent. There are several things you'll want to change, but they're all simple, and more a matter of configuring the shader than it's a matter of doing any kind of new calculation, actually.
The first thing is to observe that the shader contains two sub-shaders. It will choose between them at runtime based on the machine it's running on, so you'll need to configure both. Secondly, notice that both subshaders have a Tags block, which contains a RenderType. Leave the RenderType, it's only relevant for replacement shading. But you do need to add "Queue"="Transparent" to the tags in order to tell Unity to move the shader to the render order's Transparent queue. So both Tags blocks should be changed to:
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
Now, the next crucial thing to observe is that the Toon/Basic Outline shader uses a pass from the regular basic toon shader. That's what this line means:
UsePass "Toon/Basic/BASE"
That pass is defined in the regular Toon Basic shader, and the source code of that shader looks like this:
Shader "Toon/Basic" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { Texgen CubeNormal }
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
Name "BASE"
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
samplerCUBE _ToonShade;
float4 _MainTex_ST;
float4 _Color;
struct appdata {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : POSITION;
float2 texcoord : TEXCOORD0;
float3 cubenormal : TEXCOORD1;
};
v2f vert (appdata v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
o.cubenormal = mul (UNITY_MATRIX_MV, float4(v.normal,0));
return o;
}
float4 frag (v2f i) : COLOR
{
float4 col = _Color * tex2D(_MainTex, i.texcoord);
float4 cube = texCUBE(_ToonShade, i.cubenormal);
return float4(2.0f * cube.rgb * col.rgb, col.a);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
Name "BASE"
Cull Off
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * constant
}
SetTexture [_ToonShade] {
combine texture * previous DOUBLE, previous
}
}
}
Fallback "VertexLit"
}
It's defining the name of its pass using this line of code:
Name "BASE"
That's what enables its pass to be callable from within the outline version. So, this means we have to also modify the basic toon shader to be transparent, such that the pass used in the outline version will draw something transparent. So, add "Queue"="Transparent" to the basic toon shader, too. The basic toon shader does not have blending enabled, either, so you have to tell it to alphablend with the background. This line of code does that:
Blend SrcAlpha OneMinusSrcAlpha
That's it. In order to preserve the original Basic Toon shader and the original Basic Toon Outline, I created two new shaders by copy-pasting these two shaders' code in, and then did the modifications there, including slight renaming. As a result, this is the modified basic one:
Shader "Toon/Basic-Alpha" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { Texgen CubeNormal }
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
Pass {
Name "BASE"
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
sampler2D _MainTex;
samplerCUBE _ToonShade;
float4 _MainTex_ST;
float4 _Color;
struct appdata {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : POSITION;
float2 texcoord : TEXCOORD0;
float3 cubenormal : TEXCOORD1;
};
v2f vert (appdata v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
o.cubenormal = mul (UNITY_MATRIX_MV, float4(v.normal,0));
return o;
}
float4 frag (v2f i) : COLOR
{
float4 col = _Color * tex2D(_MainTex, i.texcoord);
float4 cube = texCUBE(_ToonShade, i.cubenormal);
return float4(2.0f * cube.rgb * col.rgb, col.a);
}
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Transparent"}
Pass {
Name "BASE"
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * constant
}
SetTexture [_ToonShade] {
combine texture * previous DOUBLE, previous
}
}
}
Fallback "VertexLit"
}
And this is the modified outline one (notice the name changes):
Shader "Toon/Basic Outline-Alpha" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_Outline ("Outline width", Range (.002, 0.03)) = .005
_MainTex ("Base (RGB)", 2D) = "white" { }
_ToonShade ("ToonShader Cubemap(RGB)", CUBE) = "" { Texgen CubeNormal }
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : POSITION;
float4 color : COLOR;
};
uniform float _Outline;
uniform float4 _OutlineColor;
v2f vert(appdata v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
float2 offset = TransformViewToProjection(norm.xy);
o.pos.xy += offset * o.pos.z * _Outline;
o.color = _OutlineColor;
return o;
}
ENDCG
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
UsePass "Toon/Basic-Alpha/BASE"
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 frag(v2f i) :COLOR { return i.color; }
ENDCG
}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Transparent"}
UsePass "Toon/Basic-Alpha/BASE"
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma exclude_renderers shaderonly
ENDCG
SetTexture [_MainTex] { combine primary }
}
}
Fallback "Toon/Basic"
}
Together, they render something that looks like this:
That should accomplish what you want. Notice that the alpha is defined by the alpha channel of the colors it's already using. So, since there's one color for the main color and another for the outline color, it's possible for those two to have two different levels of transparency.
It just occurred to me that changing the Color$$anonymous$$ask to RGBA shouldn't be necessary anyway, because the blending operation I used doesn't use the destination alpha, so there's no reason to write it to the buffer. I've updated my reply to reflect that. :)
Very nicely done! I've been stumped on this for a while now. Thank you for taking the time to explain it as well. Now I need to figure out how to get it to be lit. Shouldn't be to hard hopefully. I'm a complete noob at this stuff.
That's going to be slightly more involved, because the pass that draws the toony part consists of an old school pair of vertex/fragment shaders, so it isn't just a matter of declaring lighting function like one would with a surface shader, or writing "Lighting On" or whatever they do with that high level shaderlab code.
But you can find plenty of examples on shaders in general here, and on Surface Shaders in particular here.
If you feel this answer helped you, please consider marking it correct. :)
I know this is from a good while ago, but I just wanted to let you know this helped me out A LOT. Thank you for the detailed description.
Your answer
Follow this Question
Related Questions
Multiply Shader with Alpha 0 Answers
Shader Help? Need an Alpha Test, then a "set pixel" sort of command 1 Answer
How to adjust alpha color of particle system main module during runtime (shuriken)? 1 Answer
Shadows with Alpha, fade out shadow. 2 Answers
How to move alpha mask along the floor plane as the user (camera) moves 1 Answer