Since I figured out how to do the vertex offset in the last shader I made, I thought it could be fun to mask out some of the vertices based on a given vector (i.e a snowfall direction) and then calculate the dot product of that with with the normal direction.

I wanted to add a Snow Amount that I could adjust with a slider, another slider that adjusts the power exponent (like I used in the previous Fresnel shader too) and a vector that could be input for the snow direction.

Perhaps in the future I could get the direction of a game object in the scene and use that as the vector for the snow direction. It would allow for a much easier adjustment of the values, and work across multiple materials using the same shader.

Shader "Ryans/SnowTop"
        _MainTex ("Texture", 2D) = "white" {}
        _SnowAmount ("Snow Amount", Range (0.0,0.25)) = 0
        _SnowPowerExponent ("Snow Power Exponent", Range (0.01, 5)) = 1
        _SnowDirection ("Snow Direction", Vector) = (0,1,0,0)

Our struct vertexOutput needs of course to contain the information of the calculated snowmask from the vertex shader, so that we can use it for the vertex offset and the colouring later in the fragment shader. For that we add the code float snowmask : TEXCOORD1;.

We also need to declare our variables from our properties so that we can use them too.

            struct vertexOutput
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
                float snowmask : TEXCOORD1;

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _SnowAmount;
            float3 _SnowDirection;
            float _SnowPowerExponent;

In the vertex shader we need to get our _SnowDirection variable and use it as one of the arguments in the dot, the other will be the vertex normal, but we need to make sure it’s in world space, with UnityObjectToWorldNormal(v.normal)

Saturation is pretty much free on modern hardware so it’s okay that we need to use it a couple of times just to clamp the values, before moving on to our next calculation.

For the vertices, we need to make sure to only move the ones effected by the mask, and to use mul(unity_WorldToObject, v) to transform an upwards world vector into a object space one. This will make sure that it doesn’t matter about the objects rotation.

            // Vertex Shader
            vertexOutput vert (vertexInput v)
                vertexOutput o;
                // Creation of the snowmask float
                o.snowmask = saturate(dot(_SnowDirection, UnityObjectToWorldNormal(v.normal)));
                // Adjust the mid levels of the snow mask
                o.snowmask = saturate(pow(o.snowmask, _SnowPowerExponent));
                // Multiply the snowmask by the _SnowAmount parameter
                o.snowmask *= _SnowAmount;

                // Move the vertices affected by the snow mask, by an amount in world space
                o.vertex = UnityObjectToClipPos(v.vertex + (o.snowmask * (mul(unity_WorldToObject, float4(0,1,0,0)))));

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;

Inside the fragment shader, all we need to do is colour the “snow” that we added in the vertex shader. We can do this simply by changing the colour of the pixels affected by the mask, with a lerp. This interpolates between 2 values based on an alpha, ours would be the texture colour, the snow colour, and the snow mask as alpha.

smoothstep is good for altering gradients, so that’s basically all I did!

And in the end we get this! The snow also goes upwards no matter what the rotation of the mesh is.