clip() in surface shader doesn't clip ligthing.

I’m trying to make a simple cutout surface shader in forward rendering (VR) and even tho the pixels are clipping properly, I am left with a shadow pass, regardless whether receiving shadows is on or off for the given mesh (if I disable cast shadows, I get similar shadow-z-issue result but there are no shadowed spots)
I’m using 5.5.1f

Here’s the shader and a screenshot:

Shader "Custom/Playershader" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "white" {}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0
	}
	SubShader {
		Cull Off
		Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True"  "RenderType"="TransparentCutout" }
		LOD 200
		
		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
		#pragma surface surf Standard fullforwardshadows

		// Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 3.0

		sampler2D _MainTex;
		sampler2D _NoiseTex;

		struct Input {
			float2 uv_MainTex;
			float3 worldPos;
		};

		half _Glossiness;
		half _Metallic;
		fixed4 _Color;

		void surf (Input IN, inout SurfaceOutputStandard o) {
			// Albedo comes from a texture tinted by color
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			fixed4 noise = tex2D(_NoiseTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
			// Metallic and smoothness come from slider variables
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			//o.Alpha = c.a;
			float3 cam = _WorldSpaceCameraPos - IN.worldPos;
			//cam.y *= 10.0;
			float threshold = 4.2f + noise.r*.515;
			
			o.Albedo *= length(cam) - threshold;
			clip(length(cam) - threshold);
			//o.Alpha = .5f;// distance(_WorldSpaceCameraPos, IN.worldPos);

		}
		ENDCG
	}
	FallBack "Diffuse"
}

Try to change the Fallback to

Fallback "Legacy Shaders/Transparent/Cutout/VertexLit"

Otherwise you might need to write your own shadow caster pass. This is the one from the fallback shader as a reference:

// Pass to render object as a shadow caster
    Pass {
        Name "Caster"
        Tags { "LightMode" = "ShadowCaster" }
       
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancing // allow instanced shadow pass for most of the shaders
#include "UnityCG.cginc"

struct v2f {
    V2F_SHADOW_CASTER;
    float2  uv : TEXCOORD1;
    UNITY_VERTEX_OUTPUT_STEREO
};

uniform float4 _MainTex_ST;

v2f vert( appdata_base v )
{
    v2f o;
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    return o;
}

uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;

float4 frag( v2f i ) : SV_Target
{
    fixed4 texcol = tex2D( _MainTex, i.uv );
    clip( texcol.a*_Color.a - _Cutoff );
   
    SHADOW_CASTER_FRAGMENT(i)
}
ENDCG

    }

Try it again with render queue changed from AlphaTest to Transparent in your shader.

Or just add addshadow to the #pragma surface line and let the surface shader generate the appropriate shadow pass for you.

3 Likes

Great thanks, I had a similiar case with my shaders and that really helped.

For simple alpha test where the alpha of the _MainTex is the only factor, @Johannski 's Fallback example should work, but for anything more complex than that you likely need a custom shadowcaster pass (which controls both the shadow casting and directional light shadow receiving). Luckily that addshadow can do that for you, as it’ll add a shadowcaster pass that uses the same surf function as the rest of the shader.

However, it is important to be mindful of that fact that the shadowcaster is used for both shadow casting and shadow receiving as sometimes what you want for those two cases are not the same, and there’s not a really good way to differentiate between the two.