Simplest possible fragment shader that has shadows

I’m trying to write a custom shader that has shadows. I’ve tried everything and I can’t seem to get it to work.
Here is the code that I have. I just want the simplest possible non-surface shader that accepts shadows. I know I can just use a surface shader but I need to do some more advanced operations after I get shadows working.

Shader "Custom/Test"
{
	SubShader
	{
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdadd 
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"
	
			struct v2f
			{
				float4 pos : SV_POSITION;
				LIGHTING_COORDS(0,1)
			};

			v2f vert (appdata_full v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				TRANSFER_VERTEX_TO_FRAGMENT(o);
				return o;
			}

			float4 frag (v2f i) : COLOR
			{
				return LIGHT_ATTENUATION(i);
			}
			ENDCG
		}
	}
	FallBack "Diffuse"
}

This is what it looks like in a simple room filled with pillars:

Note that there are two issues:

  • There are no shadows between the pillars.
  • The light is in the top right area of the room, even though the light is on the left. The lighting stays the same even if I move the light around. This is probably a separate issue from the shadows though.

Thanks for your help.

#pragma multi_compile_forwardadd

should be

#pragma multi_compile_fwdbase

When I do that, it return 1.0 for every value of atten (everything is white).

Well, by default Unity will give you one shadow in forward rendering mode.

This gets given to the fwd_base pass and is the shadow cast from the most prominent directional light affecting that object.

If you’re not using directional lights, you won’t get shadows. Also the attenuation for a directional light will always be 1 if that light has no shadows (directional lights never attenuate).

Try setting the FallBack to “VertexLit” (technically Diffuse should do that anyway, but it’s worth cutting out the middle man). VertexLit contains passes that give the shadow caster/reciever information to the lighting system, so it has to be in the fallback for shadows to be calculated at all. You could make your own fallback with just those in… but vertexlit’s an easy and known-to-work alternative.

Oh, damn, I forgot.

Vertex lit REQUIRES that you have a property of _Color in your properties (just copy/paste it from the other Unity shaders if you like). Otherwise you don’t get no shadows at all :stuck_out_tongue:

If you’re using cutout/transparent vertex lit it will also require _MainTex in there so that it can derive the alpha from that with which to cast shadows.

Oh oops. I’m using deferred rendering; I guess I should have mentioned that.
I put in “#pragma multi_compile_fwdadd” because without it, it would comment out my shader and complain about it being in Unity 2.x style and needing to change it to a surface shader. Is there a deferred version of that pragma command that would let me use shadows?

I believe that even if you’re running deferred, it’ll just render that shader in forward rendering if it doesn’t have a deferred path.

I’m not sure what the deferred pipeline is for custom shaders (or if it’s even accessible that way without using surface shaders) sorry :confused:

You could try making a really simple deferred surface shader and open up the compiled version to have a look.

Hmmm… well, maybe I could render my shader with the advanced stuff without shadows, and then render another pass with just shadows, and blend them together?

Do you think that’s a viable option? If so, I’m having trouble combining them: how do I get a surface shader into a Pass? I need my second Pass to blend with my first one, but I can’t seem to get shadows working in anything other than a surface shader.

The deferred pipeline’s pretty locked down… I’m not sure how easy it is to work around it (I’ve not tried before).

It’s probably possible to render it once and get just the lighting out and then render again with a replacement shader or something to get the albedo.

What is it you’re trying to do, exactly? There may be an easier way.

Dang. Basically, I’m just trying to add shadows to my scene over my own shader.

Here’s what I have now - it’s a shader that lights each tile equally. (By the way, what’s the term for a non-surface shader? Do I just call them fragment shaders? (That’s what this is))

I’d like to apply some simple shadows on top of that. If I replace that shader with just a super-simple surface shader (just setting albedo to 1), I get this:

I’d like to blend those two images together to create something like this:


(note that this doesn’t actually exist in game, I just blended the images together in Photoshop to make this one)
That’s the end result I’m trying to achieve (well, except for the little dark bump edges on the tiles, I just want to blend the shadows. But I guess that’s a different issue). Is there a way to achieve this blending in Unity?

1 Like

I’m trying to follow this advice here: http://forum.unity3d.com/threads/96393-Achieving-a-multi-pass-effect-with-a-Surface-Shader

It says to just put the surface shader after the pass - however, they don’t seem to be blending. If I put “Blend One Zero” before the second pass, it just gives me a gray color, whereas I would expect it to be the appearance of the first pass.

Sorry, have to bump this thread.

I’m still on this issue. I have a fragment shader that I need to get some hard shadows from point lights in. It sounds like a simple task but I’ve spent weeks unsuccessfully trying to get it to work. Is it possible at all to get a fragment shader with shadows? Or at least fake it, through multiple passes and blending?

The trick is to use #pragma multi_compile_fwdadd_fullshadows (thanks aras!). This gives you all combinations of spot/point-keywords with shadows as well as without. The functional code then becomes (tested unity4.0):

Shader "Custom/Test Shadowed" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1) //note: required but not used
	}
	SubShader {
		Tags { "RenderType"="Opaque" "Queue"="Geometry" }
		Pass {
			Tags { "LightMode" = "ForwardAdd" }
			Blend One One
			Fog { Color(0,0,0,0) }

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdadd_fullshadows
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"

			struct v2f {
				float4 pos : SV_POSITION;
				LIGHTING_COORDS(0,1)
			};

			v2f vert (appdata_full v) {
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				TRANSFER_VERTEX_TO_FRAGMENT(o);
				return o;
			}

			float4 frag (v2f i) : COLOR {
				return LIGHT_ATTENUATION(i);
			}
			ENDCG
		} //Pass
	} //SubShader
	FallBack "Diffuse" //note: for passes: ForwardBase, ShadowCaster, ShadowCollector
}

Damn! holy smokes! The above code doesn’t work at all! I just transcribed all my shader into it and guess what- if you comment out the “Fallback diffuse” line, it doesn’t display anything at all. fortunately it is also amusing, as this was unexpected.

Trying to do this in Unity 5 with no luck.
Using forward rendering, 1 directional light casting hard shadows and this test code
I tried the above code directly unmodified but then the object completely vanishes.
I saw on another thread that SHADOW_COORDS and TRANSFER_SHADOW is now the way to go so I knocked up the example below, but no joy.

Shader "Custom/TestShadowed" {
    Properties{
        _Color("Main Color", Color) = (1,1,1,1) //note: required but not used
    }
    SubShader{
        Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
        Pass{
            Tags{ "LightMode" = "ForwardBase" }
            Blend One One
            Fog{ Color(0,0,0,0) }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma fullforwardshadows
            #pragma noforwardadd
            #pragma multi_compile_fwdbase
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"

            struct v2f {
                float4 pos : SV_POSITION;
                SHADOW_COORDS(0)
            };

            v2f vert(appdata_full v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                TRANSFER_SHADOW(o);
                return o;
            }

            float4 frag(v2f i) : COLOR{
                return SHADOW_ATTENUATION(i);
            }
            ENDCG
        } //Pass
    } //SubShader
        FallBack "Diffuse" //note: for passes: ForwardBase, ShadowCaster, ShadowCollector
}

All I get is white, no shadow receiving at all. It cast shadows but won’t receive them.

Any ideas what is the way to do this in Unity 5.x

Yeah that seems to match what I have already. I noticed when I just put this shader into it’s own file it does in fact work as expected so there’s something else going on. The technique appears to be correct.

For anyone else who comes upon this thread at a later date, I figured it out.

@monark almost had it right. That will cause it to receive shadows but not to cast them. To also cast shadows, after the first pass, add the following line:

UsePass “Legacy Shaders/VertexLit/SHADOWCASTER”

That will allow it to both cast and receive shadows.