Notifications
Article
Deconstructing Unity trees for Wind Zone dynamics
Updated 4 months ago
213
4
What's going on inside Unity Trees and Wind Zones??
The Unity Editor comes with a great tool to design trees along with some other vegetation. This tool, however, is still too much obscure about its implementation, especially when we ask the question: How is it that these trees react with the wind?. If you, like me, have ever tried to look for specifics about the Wind Zone implementation, then you have witnessed the information out there is almost non-existent. In this article I'll try to shed some light on this implementation as I had to dig on Unity's built-in shaders and deconstruct the meshes generated by the tree creator. Hopefully this would help others and the community to have their custom projects to be reactive to Wind Zones as well.
At first, the reason I had to know about Wind Zone specifics was the idea of a game, that eventually grew to an Asset Store extension to generate procedural trees. After much googling and asking on forums I reached the conclusion I need to inspect the tree meshes myself and try to replicate whatever the tree creator tool what baking in them. To begin with, every mesh generated by the tree creator tool in Unity has two submeshes: the bark mesh and the leaves mesh. This is due to each one of these elements having different dynamics required to react to Wind Zones.
The mesh has some information baked on a per-vertex basis, using the color and UV2 channels mostly to get the nature shaders apply the vertex displacement effect perceived as wind. Apparently the wind effect is mostly based on the CryENGINE 2 implementation, described by these Nvidia GPU articles for branches and for leaves by Tiago Sousa. There is much alikeness on the vertex animation code between these two implementations. However, Unity's does not use textures maps for the wind weights, as this information comes from the vertex directly. On the Wind Zone side, its sole purpose seems to be that of a sine wave provider so that other shaders might use it to apply the vertex displacement (available at a float4 _Wind variable).

Vertex data on the color and UV2 channels

Let us begin with how the wind is applied to the branches. The U value of the UV2 channel dictates how much a vertex should bend to the wind direction. This value is zero at the base of the tree and increases as it goes through the branches hierarchy (in a more exponential than in a linear fashion). In the vertex displacement shader function this value is called primary factor.
The secondary factor required for wind is on the V value of the UV2 channel. This value controls the swaying of the branches parallel to the wind direction. Much alike the primary factor, its value increases a it traverses the branches, but notice how the main tree or trunk is spared from its influence (first two trees at the left were created using the tool from Unity and the two at the right using Broccoli, not sparing the main trunk).
If you notice, on the last animation all the branches influenced by the secondary factor swing along in coordination. The red value of the color channel is used to make a group of branches (coming from the main trunk) to swing more independently from one another, to let each group have its own phase; this value is called branch phase in the vertex displacement shader function.
Now, combining all these values from the color and the UV2 channels the nature shader applies a more wind like effect to the branches. Notice how after the red color value has been added each group of branches swings independently.
The leaves mesh uses pretty much the same principle about the color and UV2 channels. Each leaf mesh "inherits" the same vertex values from the branch position is placed at (although a small multiplying factor is applied at the red value from the color channel). The green value channel is used on the leaves to make them wiggle with the wind turbulence, this is called edge flutter factor in the shader function.

The nature shaders

The shaders applied to the bark and leaves submeshes in the final product of the tree creation tool are available for us to download and inspect at the Unity Download Archive. These are the paths where you can find the bark and the leaves shaders respectively: 1. DefaultResourcesExtra/Nature/TreeCreator/TreeCreatorLeavesFastOptimized.shader 2. DefaultResourcesExtra/Nature/TreeCreator/TreeCreatorBarkOptimized.shader The shaders use a couple of vertex functions: TreeVertBark and TreeVertLeaf ( CGIncludes/UnityBuiltin3xTreeLibrary.cginc). Both of these functions will call the AnimateVertex (CGIncludes/TerrainEngine.cginc) function to apply the vertex displacement.
void TreeVertBark (inout appdata_full v) { v.vertex.xyz *= _TreeInstanceScale.xyz; v.vertex = AnimateVertex(v.vertex, v.normal, float4(v.color.xy, v.texcoord1.xy)); v.vertex = Squash(v.vertex); v.color.rgb = _TreeInstanceColor.rgb * _Color.rgb; v.normal = normalize(v.normal); v.tangent.xyz = normalize(v.tangent.xyz); } void TreeVertLeaf (inout appdata_full v) { ExpandBillboard (UNITY_MATRIX_IT_MV, v.vertex, v.normal, v.tangent); v.vertex.xyz *= _TreeInstanceScale.xyz; v.vertex = AnimateVertex (v.vertex,v.normal, float4(v.color.xy, v.texcoord1.xy)); v.vertex = Squash(v.vertex); v.color.rgb = _TreeInstanceColor.rgb * _Color.rgb; v.normal = normalize(v.normal); v.tangent.xyz = normalize(v.tangent.xyz); }
Here, on the AnimateVertex function, we can see how the shader makes use of the values baked at the tree mesh vertices (passed as the animParams parameter) and on the bending section of the function uses the _Wind directional value to effect the displacement.
// Detail bending inline float4 AnimateVertex(float4 pos, float3 normal, float4 animParams) { // animParams stored in color // animParams.x = branch phase // animParams.y = edge flutter factor // animParams.z = primary factor // animParams.w = secondary factor float fDetailAmp = 0.1f; float fBranchAmp = 0.3f; // Phases (object, vertex, branch) float fObjPhase = dot(unity_ObjectToWorld._14_24_34, 1); float fBranchPhase = fObjPhase + animParams.x; float fVtxPhase = dot(pos.xyz, animParams.y + fBranchPhase); // x is used for edges; y is used for branches float2 vWavesIn = _Time.yy + float2(fVtxPhase, fBranchPhase ); // 1.975, 0.793, 0.375, 0.193 are good frequencies float4 vWaves = (frac( vWavesIn.xxyy * float4(1.975, 0.793, 0.375, 0.193) ) * 2.0 - 1.0); vWaves = SmoothTriangleWave( vWaves ); float2 vWavesSum = vWaves.xz + vWaves.yw; // Edge (xz) and branch bending (y) float3 bend = animParams.y * fDetailAmp * normal.xyz; bend.y = animParams.w * fBranchAmp; pos.xyz += ((vWavesSum.xyx * bend) + (_Wind.xyz * vWavesSum.y * animParams.w)) * _Wind.w; // Primary bending // Displace position pos.xyz += animParams.z * _Wind.xyz; return pos; }

The Wind Zone Component

As you can see, the _Wind variable is required to produce the displacement effect, as you can have all the wind related data baked on the mesh and not get any wind effect if this variable is not properly set. Unity's tree creator tool adds a tree component to all the GameObjects it produces. This component is in change of updating the _Wind value on the shader/material using a MaterialPropertyBlock. I could not find how the final value for this variable is produced, but I made an approximation using the CryENGINE 2 reference and just playing with the Wind Zone to see what kind of value it produced. Here is the function I used at the Broccoli Tree Creator extension to set the _Wind variable:
public void UpdateWind (bool setToZero = false) { wind = Vector4.zero; float factor = 1f; float freq = 1f; if (!setToZero) { Vector3 windZoneDirection = Vector3.zero; Vector4 windZoneFactor = Vector4.zero; WindZone[] windZones = FindObjectsOfType<WindZone> (); for (int i = 0; i < windZones.Length; i++) { if (windZones [i].gameObject.activeSelf && windZones[i].mode == WindZoneMode.Directional) { windZoneDirection = windZones [i].transform.forward; factor = windZones [i].windMain * (windZones [i].windPulseMagnitude + 1f); // Since the frequency solving function is obscured we use this one instead. freq = (float)(Mathf.Cos (windZones [i].windPulseFrequency * Mathf.PI) * Mathf.Cos (windZones [i].windPulseFrequency * 3 * Mathf.PI) * Mathf.Cos (windZones [i].windPulseFrequency * 5 * Mathf.PI) + Mathf.Sin (windZones [i].windPulseFrequency * 25 * Mathf.PI) * 0.1f); factor *= freq; windZoneFactor = new Vector4 (windZoneDirection.x * factor, windZoneDirection.y * factor, windZoneDirection.z * factor, windZones [i].windTurbulence * (windZones[i].windPulseMagnitude + 1f)); wind += windZoneFactor; } } } if (_propBlock != null) { _propBlock.SetVector ("_Wind", wind); _renderer.SetPropertyBlock (_propBlock); } }
The _Wind variable is a float4, containing on the xyz the direction of the wind multiplied by a factor and a pulse/turbulence value on the w.

Conclusions

There is plenty of room for improvement! I mean, we can see more natural wind like effects on trees from SpeedTree (CryENGINE 2 implementation has some too), then we wonder: why can't the tree creator tool get the same treatment?. Well, actually, Unity seems to be making some changes on how users can make use of wind effects in Unity 2018.2: this Unity blog post introduces a vertex position node used by its Shader Graph tool to build a wind shader, let us hope it spreads to the tree creator tool in the future. As for me, digging on this implementation helped me learned tons of stuff about meshes and shaders, and led me to seek a more robust and natural wind shader to make available in the future for my Broccoli Tree Creator tool. Thanks and I hope you find this post useful!

Valdemar Sánchez
Developer - Programmer
1
Comments
Valdemar Sánchez
4 months ago
Developer - Programmer
zy689896968This is really cool. If you create a tree with only one shader, it's perfect. I don't know if it can be achieved?
2. You'll probably need to define your own TreeVertLeaf and TreeVertBark on the custom shader to make it work on both submeshes when declaring how vertices would be processed.
0
Valdemar Sánchez
4 months ago
Developer - Programmer
zy689896968This is really cool. If you create a tree with only one shader, it's perfect. I don't know if it can be achieved?
Yes, it can be done with one shader. Some things to consider are: 1. By using only one shader, it would have to support alpha cutout for the leaves despite the bark doesn't need it.
0
z
zy689896968
4 months ago
zy689896968This is really cool. If you create a tree with only one shader, it's perfect. I don't know if it can be achieved?
one material
0
z
zy689896968
4 months ago
This is really cool. If you create a tree with only one shader, it's perfect. I don't know if it can be achieved?
0