The basis of this effect is drawing a vertical stack of quads, each one showing a slice of the cloud. The cloud texture itself is just a world-mapped, top-down animated noise that is piped into opacity.
To animate the noise, I use the same trick that I use in countless other shaders to create a natural looking motion that appears free of obvious repetition, which is to blend the noise with either a copy of itself with a different UV scale, or a different noise texture, and pan these two noise textures against each other in opposite directions, and then blend them together through multiplication or some other desired blend mode.
This animated noise is useful is so many shader effects. For example, it's the basis of a nice fire effect, where instead of panning the two noise textures in opposite directions, you pan them both upwards but at different speeds and scales.
To pan a texture, you simple add Time to the UV coordinates in whichever axis you wish to pan. You just have to make sure you use a texture that is set to repeat wrapping and not clamped, and also one that tiles seamlessly with itself.
Remapping this noise texture, and then raising it to a power, will give you control over the opacity cut-off and also the softness of the noise.
The quad stack itself can be modelled in advance, or generated at runtime with this script, via Graphics.DrawMesh().
Generating it via script allows you to adjust the amount of layers for performance reasons, however it won't allow you to view the effect in Edit Mode, and I believe that it may not be appropriate for VR due to rendering only in one camera.
To make complete the 3 dimensional shape of the clouds, round them off towards the top and bottom of the quad stack by darkening the noise the further away it is from the middle of the stack.
To darken the opacity map further away from the middle of the quad stack, you can either bake in vertex colours and multiply by that, or calculate the y offset in shader.
This vertical falloff is also going to come in handy to give the clouds some basic ambient lighting, darkening them on the bottom and keeping them lighter on top. You can use this gradient to add in both the Ambient Sky Colour and Ambient Ground Colour, or set those colours yourself in shader. In real life, low clouds will often subtly pick up the colour of the ground on their bottom surface.
A subsurface scattering lighting pass is also a nice addition to the shader – since clouds let quite a lot of light through, when the sun is directly behind them you can often see it glowing through. The same Dot Product of the View Direction and negated Light Direction can be used here.
There's a lot of interesting tweaks you can make to the subsurface scattering contribution to create some nice effects. For example, if you constrict the final tapered noise slightly, and then subtract it from the SSS mask, it will only allow the light through around the outside of each cloud, and can create a pleasing rim-light effect.
Adding in directional light is a bit tricky, since these clouds are just 2d textures and don't have normals that are usually required for calculating light. The best way that I've found is to create a duplicate of the cloud noise, and offsetting it in the direction of the light, and then subtracting that offset noise from the original noise. This will leave a soft rim of noise facing towards the light direction, which we can use as a directional lighting mask.
To offset the noise texture, you need to transform the Light Direction into tangent space – this will give you a light direction vector that appropriately offsets the UVs of the texture. You may have to negate either the x or y axis, or both, depending on the orientation of the mesh. Offset this noise by an adjustable amount to get the best lighting effect, which will depend on the scale and detail of the noise texture.
You can use the fake lighting UV-offset (by tangent space light direction) trick with multiple lights. Multiply by Light Colour and Attenuation at the end, and pipe into a Custom Lighting input. It's a good way to fake lighting on smoke particles and other greyscale textures, especially if they're animated (via a flipbook, UV distortion, or noise overlays).
Depending on your game lighting and performance concerns, it's possible to not calculate directional lighting at all, and still get very convincing clouds with just an Ambient and SSS contribution. The SSS in particular gives a pseudo-directional look to the lighting anyway.
These clouds also can cast nice shadows on the underlying terrain, and with a volumetric lighting asset, can create very pretty sun rays. I'm a big fan of HX Volumetric Lighting, but there are also several free Volumetric Light assets available that are also very good.
Finally, I like to use a Time-of-Day script to rotate the main direction Sun light over time, and also set the light, ambient and fog colours by a sampled gradient.
You can set an adjustable gradient in the inspector with:
public Gradient lightColourByTime;
[Range(0f, 1f)]public float timeOfDay
and sample it with
Color lightColor = lightColourByTime.Evaluate(timeOfDay);
And set it to your light colour. You can do the same to your ambient colours via
If you've set your cloud shader up to use the Light Colour, and/or the global Ambient and Fog colours, you can get some very nice saturated sunrises and sunsets, and then fade to darker blues at night, depending on the art style of your game of course.
I hope you've gotten something out of the creation of these volumetric clouds. As always, if you have any questions, leave them in the comments, or hit me up on discord or twitter!