Tutorial: Wind-blown leaves shader


leaves

In this post, we’ll write a fragment shader to animate leaves in Unity. I’m assuming familiarity with light vector math.

First, get your base textures. The tree in the gif is actually a tilemap composed of leaf tiles like these.

leaf leaf leaf

Then load them into the world. You can use a default SpriteRenderer if your leaves are one image, and tilemaps will be fine too.

leaves

leaves

Copy the Unity Sprites/Default shader and rename it to Custom/Windleaves, then save it as WindLeaves.shader. Then make a material to to apply the shader to.

leaves

Putting the material on your sprite or tilemap should result in no change. Now let’s add the normal map distortion to the shader.

What’s a normal map

img

Normal maps are images, mostly used in 3D graphics to make flat textures look like they stand out. Each pixel’s red, green, and blue values are numbers that make the X, Y and Z components of a 3D vector. I’m using the below image for mine.

img

Code time

img

Add fields in the shader headers for the normal map properties. We’ll probably be tweaking these values in-game until they look good.

img

The new field for the normal map is exposed in the material, so we can add it.

img

Now that we’ve got the normal map exposed up top, we need to read it in the shader. When we add them here above the frag function, Unity will recognize that those names correspond to the top-level properties we just defined and pass them through, as long as the data types align.

  • sampler2D: a 2D image
  • float: number
  • float4: a 4-dimensional vector

Next step: Add the distortion from the normal map inside the shader code.

img

The shader takes the 2D vector, IN.texcoord, and looks up that point on the main texture, the leaves. We’re altering that vector based on the normal map, so we change which point on the leaf texture the current coordinates point to.

Let’s see what that looks like in-engine.

img

Hmm. Not great. Let’s move sliders until it looks less insane.

img

img

That’s a bit better. Notice how you can still see the grid pattern in the leaves, though. That’s because I’m using a tilemap here and the coordinate lookup for the distortion happens in texture space instead of world space.

Fortunately, changing the lookup coordinates of the distortion map to use world space isn’t too painful.

img

Add another float3 to the v2f struct to represent the pixel’s world position, and then a line in the vertex shader to translate its local position into world space.

unity_objectToWorld is a builtin 4×4 matrix.

img

Then, change distortion_coordinates to pick up the world space value insteaad of the normal texture coordinate value.

img img

It’s a very subtle effect. Increasing the distortion value (compare with the first gif) highlights it.

We can get more fine-grained control over the normal map distortion by making sure the distortion lookup operates at the same scale as the leaf texture. Coordinates aren’t 1-256 for a 256 pixel wide texture; they’re always decimals from 0-1.

img

Adding a float4 called [TEXTURE_NAME]_TexelSize will make Unity automatically populate that value. Declare it along with everything else so we can use it on line 103.

img img

Now we’ve got much more delicate control over the distortion value, and it looks a bit more realistic.

What else can we do?

The leaves still look a bit like a sail rippling in the wind. If only there were a way to make the separate colored clusters move individually…

One way to do this is to change the strength of the distortion based on each pixel’s green value.

img

Do a pre-lookup on the first line and grab its green value, then combine it with the strengthening on line 105.

img

Looks much better.

Thanks for reading. You can find the source code for the shader here.

Get Vapor Trails

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.