27 October 2024

How to implement a tree moved by wind in Godot

Cherry tree
In previous articles, I explained how to use shaders to make the water in a 2D scene more realistic. Shaders can achieve much more than that. Another common use is to make vegetation move as if swayed by the wind. This adds life to the scene, making its elements appear more organic.

To explain how to achieve this, I’ll evolve the scene from the previous articles. As before, I recommend you download the repository at the specific commit for this article, to ensure that the code you study is exactly the same version that I’ll be showing here.

In the previous shaders, we manipulated the colors to display from the fragment() method, but before this, Godot shaders execute another method called vertex(). While fragment() is used to manipulate the color of the pixels on the rendered surface, vertex() is used to deform the surface by manipulating its vertices. This is highly effective in 3D, as it allows for dynamic mesh deformation, simulating effects like ocean movement or the indentation that occurs when making a groove in the snow. In 2D, it is also useful, though somewhat limited since any sprite has only four vertices that we can manipulate:

  • Top left: VERTEX(0,0)
  • Top right: VERTEX(Width,0)
  • Bottom left: VERTEX(0,Height)
  • Bottom right: VERTEX(Width,Height)

The VERTEX concept is equivalent to UV, which we used in the fragment() method. If fragment() executes for every pixel on the rendered surface, collecting its relative position in the UV variable, the vertex() method executes for every vertex on the surface, gathering its position in the VERTEX variable. The key difference is that UV coordinates are normalized between 0 and 1, whereas VERTEX coordinates are not normalized and vary between the width and height in pixels of the sprite where we apply the shader. If you still don’t recognize what UV coordinates are, I recommend reviewing the article where I explained it.

It doesn’t matter if a sprite has an irregular shape; in shader terms, it is rectangular, with the four vertices in each corner, just filled with transparent color (alpha=0) where there is no drawing (if the sprite image has an alpha channel).

Although a 2D sprite lacks the vertex resolution of a 3D mesh, by manipulating the position of its vertices, we can stretch and compress the sprite. This is very effective for simulating vegetation that is compressed, stretched, and ultimately moved by the wind.

Looking at the code in the repository’s example, you’ll see that I’ve applied a shader to the tree, located in Shaders/Tree.gdshader. Being a 2D shader, it is of type canvas_item, like those in the previous articles.

Shader configuration
Shader configuration

The shader exports the following variables for editing through the inspector:

Shader variables exported to inspector.
Shader variables exported to inspector.
  1. wind_strength: This variable models the wind's strength. The greater it is, the more the tree canopy will sway.
  2. horizontal_offset: This creates an asymmetrical sway. The closer this value is to 1.0, the more the canopy tends to sway to the right; conversely, the closer it is to -1.0, the more it will lean left. If the value is zero, the sway will be symmetrical.
  3. maximum_oscillation_amplitude: This defines the maximum displacement (in pixels) for the tree canopy. It’s the maximum displacement reached when wind strength is 1.0.
  4. time: This variable isn’t intended for user manipulation but for an external script. It synchronizes the canopy's oscillation with the particle emitter that releases flowers. The particle emitter will be covered in a later article, so please ignore it for now. If there were no particle emitter, I could have used the TIME variable, which is internal to the shader and provides a counter with the shader’s active time.

Let’s now examine the vertex() method:

vertex() method
vertex() method

In line 11, the code limits the manipulation to the top vertices. Why limit ourselves to the top vertices? Because vegetation is firmly anchored to the ground by its roots, so only the top sways in the wind. Therefore, we only modify the top vertices, leaving the bottom ones in their default position. In theory, I should have been able to filter using VERTEX.y instead of UV.y, but when I tried, I didn’t get the desired effect since the entire image swayed, not just the top vertices.

Also, keep in mind that the comparison in line 11 cannot be for exact equality because floating-point numbers are not precise. Hence, the comparison is not "equal to zero" but "very close to zero."

The oscillation is generated in line 14 using a sinusoid with a maximum amplitude set by wind_strength. Remember that a sinusoid produces values between -1 and +1, so multiplying it by its amplitude means its resultant oscillation will range between -wind_strength and +wind_strength. Since wind_strength is limited between 0 and 1, the oscillation will not exceed -1 and +1 at the maximum value of wind_strength.

Note that inside the sinusoid there are two terms. One is time, allowing the sinusoid to change as time progresses. The other is VERTEX.x, which varies for each top vertex, allowing their oscillations to be unsynchronized, creating a stretching and compressing effect. If we were rendering vegetation without foliage, such as grass, and didn’t want this stretching and compressing effect, we could remove the VERTEX.x term, making the two vertices move in sync.

In line 15, we multiply by the maximum displacement to achieve the desired oscillation amplitude.

In line 16, we add the displacement to the vertex's horizontal position.

Finally, in line 17, we register the vertex's new position.

By moving only the two top vertices and leaving the bottom ones fixed, the effect is as if pinning the two bottom corners of a flexible rectangle in place and moving the two top corners.

Surface deformation
Surface deformation achieved with the shader.

When you overlay a tree image onto a surface deformed in this way, you get the effect we aimed to create:

Movement of the tree
Movement of the tree