- Home /
Receiving Shadows On a Tranparent Shader
In Unity 2020.1.3f1
I'm currently working on a small shader which takes the alpha component of a mesh's vertex colours and uses it for transparency, the result is a fade out along the edge of the shape:
Fairly simple stuff, and works generally okay. The only noticable bug is that it doesn't receive shadows.
Through a bit of reading, I know that it's possible to receive shadows on transparent shaders such as these and have seen working visual examples, but there's a lot of dubious information on the exact reason and resolution to this problem.
The shader is as follows:
Shader "Custom/StandardVertexAlpha"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Texture", 2D) = "white" {}
_Emission("Emission", Color) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "AlphaTest"}
Cull Off
Lighting Off
CGPROGRAM
#pragma surface surf Lambert alpha:blend
struct Input
{
float2 uv_MainTex;
half4 color : COLOR;
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _Emission;
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb * IN.color.rgb;
o.Alpha = IN.color.a;
o.Emission = _Emission;
}
float ShadowAttenuation(int index, float3 worldPos)
{
return 1.0;
}
float CascadedShadowAttenuation(float3 worldPos)
{
return 1.0;
}
ENDCG
}
Fallback "Diffuse"
}
The two methods "ShadowAttenuation" and "CascadedShadowAttenuation" are taken from an old Unity tutorial, but don't seem to function anymore.
Answer by Namey5 · Sep 13, 2021 at 10:38 AM
First, 2 reasons those functions don't work:
Like any method in a script, a function needs to be called in order to do anything - in this case, neither function is called so of course they aren't going to do anything.
Both of those shadowing functions just return '1' no matter what, so even if they were called they wouldn't do anything. My guess is that they were copied from an incomplete tutorial.
That aside, in general there is nothing stopping transparent objects from receiving shadows - the reason they don't in Unity is down to implementation. Unity renders directional light shadows into a screenspace texture via the depth buffer before rendering takes place - this is then composited back onto the scene when needed. Transparent objects don't write to depth, and as such any projected shadows would show those of the objects behind rather than any cast on the transparent surface itself.
However, in your case the transparent surface in question lies directly on the surface of an opaque object - meaning we can abuse this and just pretend that the path is opaque and let it draw shadows.
Shader "Custom/StandardVertexAlpha"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Texture", 2D) = "white" {}
_Emission("Emission", Color) = (0,0,0,0)
}
SubShader
{
// Use a high render queue to make sure we draw after everything (however anything in the transparent queue won't get shadow info, so 2500 should work fine)
Tags { "RenderType" = "Transparent" "Queue" = "AlphaTest+50"}
Cull Off
CGPROGRAM
// Use the decal blend function to keep the shadow rendering code - any normal blend will strip shadows
#pragma surface surf Standard fullforwardshadows decal:blend
struct Input
{
float2 uv_MainTex;
half4 color : COLOR;
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _Emission;
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb * IN.color.rgb;
o.Alpha = IN.color.a;
o.Emission = _Emission;
}
ENDCG
}
Fallback "Diffuse"
}
This will have issues when the object lies in front of other transparent objects and the skybox, so make sure the geometry is tight to the terrain. It also probably won't work in Deferred rendering.
Fantastic answer, thanks most kindly for the explanation and example shader setup!