I really enjoy writing shaders that breathe life into the game world, especially vertex shaders. A vertex shader’s main function is to transform a vertex’s 3d position into a 2d position on the screen for rendering and coloring, but you can manipulate that position in interesting ways to do fun things.
For cloth, hair, vegetation, or anything that needs to be affected by wind, vertex shader is the perfect tool. And the code is super small - for my waving flag, the vertex shader is just 3 lines.
//x is length, y is depth, z is Y-height
//horizontal wave - more wavy towards the end
v.vertex.y += _WavingAmount * pow(v.vertex.x, 2) * pow(v.vertex.z, 3) * cnoise(float2(_Time.z * 0.7, v.vertex.z * 2)) * 1 * sin(v.vertex.x * 4 + _Time.z * 2);
v.vertex.z += pow(v.vertex.x * 0.4, 3) * 0.6 * cnoise(float2(_Time.z * 0.7, v.vertex.z * 2)) + (pow(v.vertex.x,2) * -0.15);
//more horizontal wave
v.vertex.y += pow((1 - abs(v.vertex.x + 1)), 0.5) * cnoise(float2(_Time.z * 0.5, v.vertex.z)) * _WavingAmount;
Essentially it’s a bunch of sine and noise functions stacked on top of each other to create a natural waving pattern. And a cool thing is that all this can be multiplied by a global float value. Meaning, one number can control the waving amount of ALL objects that are programmed to wave in the wind. Also all these things run on the GPU, freeing precious CPU resource for important things like AI and physics.
The main reason why I find shader development so fun is that everything can be parameterized and previewed instantly. It’s super easy to expose the waving amount and the sine wave octave values to the material, and turn them into a slider. Then you can go out into the scene and play with those values until you get one that looks just right.
Vertex shaders aren’t good for every kind of animation though.. For my main character’s pony tail, I use the built in cloth component in Unity (which runs on CPU), because it needs collision support. It’s possible to write a vertex shader that reacts to colliders, but it requires some set up and steps that I haven’t investigated yet. On the other hand, the ghost mode long hair runs on the GPU, because it doesn’t collide with anything.
One interesting issue I was running into with vertex shaders is that it doesn’t change depth and normals textures that are used for some post processing effects (ex: ambient occlusion), even when you add ‘addshadow’ tag in surface shaders… Turns out, there’s a built-in hidden shader in Unity that takes care of depth normals. I had to modify that shader and add custom render types with the same vertex displacement functions to make sure that the vertices used for depth normals were waving in the same way as the vertices in the normal shader.
Anyway, graphics programming is fun and super useful. Everyone should learn it!