Cyanilux

Game Dev Blog & Tutorials

Cloud Shader Breakdown

URP
Shader Graph

Intro

This shader is applied to a flat subdivided plane where vertices are offset vertically based on layered noise to create a cloud effect. Each layer of noise is offset by time at a different rate to simulate the clouds changing shape over time. It also uses the depth buffer to fade the alpha where there are intersections with other scene objects, which helps give the clouds a much softer appearance.

Notes

Breakdown

To begin we’ll set up the noise for our clouds. We’ll be using a mixture of Gradient Noise and Simple Noise nodes to do this – although you may also want to think about replacing some layers of noise with a seamless texture as a cheaper alternative.

In order to ensure we can use multiple plane objects and have no seams between them, we can set the UVs of the noise to be based on the Position node set to World space. That gives us a Vector3 but the UVs are a Vector2 so we also need to Split it and take the X/R and Z/B coordinates to make our noise be based horizontally along the plane.

We can use a Time node to offset this position so the noise moves slowly over Time, with a Multiply node to control the speed at which the noise scrolls at. We’ll also add an overall speed multiplier as a Vector1 property so we can control the cloud scroll speed from the inspector.

We’ll use parts of this noise setup 3 times, one for the Gradient Noise and two Simple Noise nodes, all moving at slightly different rates as shown in the image below.

I’ve used a mixture of Add and Multiply to combine these layers of noise. We could take an average of all three (by adding them together and dividing by 3), but having a multiply in there made the layered noise output a bit more interesting, in my opinion at least.

We can now use our layered noise output to offset the vertex position. In order to ensure this is only a vertical offset, we’ll first put it into the Y input on a Vector3 node, with the other two inputs set to 0. We can then Multiply this by a value to control the scaling – I’m using the object’s scale for this, so that we can easily change it by adjusting the Y scale of the plane rather than having a separate property – But if you want to be able to apply this to meshes other than a plane, you might want to use a property instead.

Also note that we are offsetting the World space vertex position here, but the Master node wants a Object space position, so we need to use a Transform node to convert it (from World, to Object).

Next we’ll handle the cloud colour as it’s currently hard to see anything we’ve done with the shader being a solid colour. I’m using the same output from our layered noise – giving the higher parts of the clouds a different colour to the lower parts. We can do this by putting the layered noise into the T input on a Lerp node, and set the A and B inputs to two Color properties. This creates a linear interpolation between the two colours, creating a gradient. At T=0 it outputs A, and at T=1 it outputs B.

So we can control how much cloud cover there is, we can add a Smoothstep based on the layered noise output and use this as the Alpha input on the Master node. The Smoothstep is similar to a Step node, but provides a smoother transition where we specify two Edge inputs. If the In input is less than the first Edge it outputs 0 and if it is larger than the second Edge it outputs 1. Anything inbetween is an interpolation between 0 and 1, but it is not linear – it instead smoothly eases in and out.

I’m using a property to control the two Edge inputs. It outputs 1 if the Cloud Cover property is 0, as all noise values will be larger than 0, but as it gets larger the Smoothstep outputs 0 for the lower noise values, leaving only the higher parts behind. Also the falloff (which is controlled by Edge2) also increases, so the clouds also get softer, and we have an Additional Falloff property to control this further if required.

Finally, I wanted to add a depth intersection effect, so that objects fade into the clouds rather than having a harsh transition. We subtract the Object Depth from the Scene Depth, which we can then multiply by a value to control how it fades, aka the Density of the clouds. In order to obtain the Object Depth, we use the W/A component from the Screen Position node set to Raw mode. We can then Multiply this with the previous alpha affect to combine them.

This is a common technique to have effects where there are intersections with scene objects, such as with forcefields or shoreline effects on water shaders. Note however that this will only work in a Perspective camera projection. Also, on the (Lightweight / Universal) Render Pipeline Asset, the Depth Texture option needs to be enabled for the Scene Depth node to work.

If you aren’t going to have any objects intersecting with the clouds you’ll likely want to leave this part out for better performance.

Note : Also make sure the Alpha Clip Threshold on the Master node to 0 for URP. In older versions of LWRP the value would do nothing unless a node was connected but this has been changed. You may want to use an additional property to control this threshold in order to correctly render shadows if required, but this will create a hard-cutoff. If you don’t want to affect the normal visuals, you can create a Boolean Keyword with the reference of SHADERPASS_SHADOWCASTER to output different values when that is true, or use a Custom Function containing something like :

1
2
3
4
5
#ifdef SHADERPASS_SHADOWCASTER
	Out = 0.5; // or maybe pass a Vector1 property in
#else
	Out = 0;
#endif

Fixing Issues

That’s the Shader Graph complete – But before we finish the post we’ve got to tackle the issues I mentioned in the notes at the start of the post.

In order to fix this, we need to locate “ZWRITE” at the top of our shader and set this to “On” instead of “Off”.

We can fix this either by updating to URP v7.1.1 or higher (may also need to update Unity version), or editing the generated code like so :



Thanks for reading! If you have any comments, questions or suggestions you can drop me a tweet or join my discord. If this post helped, consider sharing a link with others!

~ Cyan


License / Usage Cookies & Privacy