Fixed angle shadow - irrespective of scene lights

I’m working on an RTS game, and I want the flying units to have a blob shadow for orientation, that does not come from a specific light on the scene, but it’s like a projector shadow. It needs to always be displayed directly under the unit on the y-axis. Blob shadows are more expensive on my rendering mechanism than real shadows, so I’d rather use that.

Is there a way to write a shadow pass shader that always projects the shadow at a specific angle, rather than taking the lights in the scene into account?

My project is on the built-in pipeline.

I can think of a few ways to go about this.

Option 1: Fake shadow maps.
This could be as “simple” as rendering all flying objects using a solid black shader to a camera that’s looking down from above and then projecting that onto the ground, either using a custom ground shader or using a single projector (which you exclude the flying objects from receiving).

Option 2: Override the directional light direction.
The short version of this option would be to move the directional light in the scene to be pointed straight down, and use a custom shader on all objects that uses a light direction that comes from a separate global shader value you set. This does mean all shadows are now coming from above, but it would fix the issue for planes.

Option 3: Inject shadows into the screen space shadow pass.
If your target is desktop or consoles, Unity’s directional light shadows are rendered using a separate screen space pass that renders the shadows onto a depth texture. The in-camera shaders then sample this screen space shadow texture to get their shadows. You can use command buffers to render other things to that screen space texture, like using deferred decal style shaders to project blob shadows for the flying units for a significantly reduced overhead compared to real projectors. You could also render a depth texture from a top down camera to use as a “real” shadow map that includes only the flying objects.

Many thanks, Bgolus! Your answers in this forum have helped me before. I’m targeting mobile, for now, android/ OpenGLE3 (no Vulkan and no URP as my benchmarking show that it’s not performant in many android devices).

I realized that I didn’t get how the lighting in the scene works - specifically that only the direction of the light matters not the distance. I’m going to have baked lighting only, minus these directional shadows that are going to be real-time.

I’m thinking of either using the real shadow of the flying objects with one directional light from above (and lightmaps with different light sources for the object itself) or only rendering the shadow of a disc mesh attached to the object.

For the latter, I found the following transparent shader. I am a bit worried about the transparency though.

Is there a better way to only render the shadow of an object?

Shader "ShaddowOnly"
{

    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader
    {

        Tags
        {
            "Queue"="Transparent+1" "IgnoreProjector"="True" "RenderType"="Transparent"
        }

        Lighting Off
        blend One One

        Pass
        {

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag : Alpha
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 pos : SV_POSITION;
            };

            struct appdata
            {
                float4 vertex : POSITION;
            };


            float4 _GrassTex_ST;
            uniform float3x3 _Object2WorldR;

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : COLOR
            {
                fixed4 color;
                color.rgb = fixed3(0, 0, 0);
                color.a = 0;
                return color;
            }
            ENDCG

        }
    }

    Fallback "VertexLit"
}

If you want a shadow only object, there’s a setting on the mesh renderer for that.

Thanks! I’m using DrawMeshInstanced, but it has a similar option.