Help with shadows in shader that moves vertices

The situation is this : I’m rendering some low-poly clouds in my game, and I got everything working, but I was doing all the work on the CPU. I’m trying to move it all to the GPU and I’ve got everything working EXCEPT the shadow rendering. You can see the CPU-driven version at x.com.

The way I was doing it was with a single cloud mesh that I replaced all the vertex data every frame. That mesh was then attached to two meshrenderers, one with a small custom shader that coloured the clouds (and cast no shadows), and a second one that was set to ShadowOnly and used the Standard shader with Rendering Mode set to Fade and Albedo set to (1,1,1,0.7). This way, I got solid clouds, but the shadows themselves were not as solid as the shadows cast by other objects.

The process I’m using now is to set up the mesh once, with each cloud ‘cell’ being full size. Then I’ve still got the two meshrenderers with shaders. This time the ‘cloud’ shader does the work in the vert program to position the vertices of each cloud cell in order to give the effect I want. The ‘cloudshadow’ shader is doing the same vertex repositioning.

The two problems I have are (1) the shadows are dark - I’m not setting things right to have the Fade + Alpha = 0.7 semi-dark shadows and (2) for cloud cells that are smaller than full size, the shadows are positioning/moving quite differently to the cell itself. You can see an example of the bug below - the problem is where the shadows zip across the world when I make their associated cells shrink in place:

The ‘cloud’ shader code is:

Shader "Clouds"
{
    Properties
    {
    }
    SubShader
    {
        Tags { "RenderType"="Opaque"}
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard vertex:vert fullforwardshadows addshadow

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

        struct Input
        {
            fixed4 color : COLOR;
        };

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)
     
        void vert (inout appdata_full v)
        {
            // BLAH BLAH BLAH ALL CULMINATING IN:
            v.vertex = ......;

            UNITY_TRANSFER_DEPTH(v);
        }

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            o.Albedo = fixed3(1,1,1);
        }
        ENDCG
    }
    FallBack "Standard"
}

And the ‘cloudshadow’ shader is:

Shader "CloudShadows"
{
   Properties
    {
    }
    SubShader
    {
        Tags { "Queue"="Transparent" "RenderType"="Transparent"}
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard vertex:vert fullforwardshadows addshadow

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

        struct Input
        {
            fixed4 color : COLOR;
        };

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void vert (inout appdata_full v)
        {
            // BLAH BLAH BLAH ALL CULMINATING IN:
            v.vertex = ......;
        }

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            o.Albedo = fixed3(1,1,1);
            o.Alpha =  0.7;
        }
        ENDCG
    }
    FallBack "Standard"
}

Note that I’ve removed the specifics of the vertex transformation just for simplicity - I’d hope the details of it aren’t needed, but I can supply them if necessary.

A few more notes:

  1. At the moment the ‘cloud’ renderer is set to Shadows : Off and the ‘cloudshadow’ renderer to ShadowOnly. If I disable the cloudshadow objects and set the ‘cloud’ renderer to Shadows : On I get the same effect with the shadows moving wrong, so it’s not unique to the cloudshadow shader

  2. I use a similar process of positioning the vertices for the ground tiles too, and the shadows seem to behave themselves for that. the cloud shader is based on the ground shader, so I’m not sure what the issue is :frowning:

Obviously I’m a bit clueless about all this. Any help would be greatly appreciated!

Surface shaders don’t have built in support for semi-transparent shadows. You can hack it by using an alpha tested shader, but it’ll be much simpler to write a custom shadow caster pass like shown here:

You don’t need everything from that shader, just the main bits of using the
_DitherMaskLOD texture and VPOS. I think you can have a shader file with only the shadow caster pass and it’ll work fine. You could also put those lines in a Surface Shader and see if that works. I seem to remember some bug with Surface Shaders and generated shadow passes using VPOS though…

As for the shadows moving, presumably your shader is scaling the v.vertex.xyz. If the mesh is being batched, then the mesh’s 0,0,0 is at the center of the world, and it is going to be scaled down to that point rather than to its game object pivot. For what you have already, it might be fixed by enabling instancing on the material. With a custom vertex fragment shadow caster shader you’d have to do the work to make that shader instanced. Catlike coding has a tutorial on that too. Alternatively you can try adding the DisableBatching tag.

Thanks for that. the shadow dithering tutorials look like just what I need.

Unfortunately the game object pivot is already at the 0,0,0 for the clouds, and enabling instancing/disabling batching doesn’t appear to do anything :frowning: I’ve had to revert to my old code for now