Quick intro
This guide will walk through a method for creating swim animations in a shader. This is primarily intended for those who are new to creating vertex displacements. For a quicker read, you can look here.
Pros of this approach:
This approach avoids the need to rig your characters. I personally find it faster to create smooth animations using this method than keyframing a rig. And this approach places all the work of animation on the player's GPU.
Cons of this approach:
With this approach, I've not found a good way of adjusting the animation speed during gameplay. Even when lerping between different inputs to the shader I would get stuttering. Perhaps lerping between different materials with different input values would work but I've not tried this and can't speak to its performance cost.
Note on setup:
I'll be using Unity 2022.2 and Shader Graph 14.06 but the principles should be the same no matter the platform you want to use.
My models are setup in Unity with the X Axis as right, Y Axis as up and Z Axis as forward. If X is forward in your models you'll need to account for this.
Step 1: Creating a wave-like motion
To access and modify the position of the mesh's vertices in Shader Graph we need a basic setup:
Add a Position node (set to Object)
Split out the X, Y and Z coordinates (aka R, G, B) of each of the vertices' positions
Make some modifications to those coordinates
Combine them back together into a Vector 3
Feed that Vector 3 into your shader's Position node
To create a wave-like motion in the mesh, we move each of the vertices along their X Axis (moving them left and right).
We will modify how much we move those vertices along the X Axis over time. And will modify how much we move them depending on where they are on the Z Axis of the mesh.
So the foundation for our wave movement is subtracting a value from the X Axis positions of each of the vertices. And changing how much we subtract from the X Axis, along the Z Axis.
We leave the vertices' position along the Z Axis and Y Axis unchanged.
Making the changes above to our Shader Graph should distort our mesh. But this distortion is static and in one direction. To modify this distortion over time:
Add a Time node (which will feed time in seconds into our modifications)
Multiply time by a float (Speed)
Add this to the Z Axis
Feed that into a Sine node (making the calculations so far dial between -1 and 1)
Multiply by a float (Strength)
And subtract this from the X Axis
Your model should now be doing something like this:
Step 2: Controlling Frequency
So far we have a wave-like motion moving along our Z Axis and we can control it's strength and speed. But we are not able to control how close or far apart these waves are.
To do that we will need to set up a proper wave. Catlike Coding has a great resource for reading about waves in more detail here.
Divide Pi by a float (Frequency)
Multiply that by 2
Multiply that by your modified Z Axis values before plugging them into the Sine node
With that in place we can now adjust how far apart the waves are with our Frequency input.
To get clearer visual feedback on how each of your input parameters (Speed, Strength, Frequency) are working:
Feed the output of your Z Axis modifications (after multiplying by Strength and before subtracting them from the X Axis) into a Clamp node
Feed that into the T value of a Lerp node
And Lerp between two colors (A and B) based on this.
Feed that into your Shader's Base Color.
This is how things should now look:
Step 3: Masking the vertex distortion
We can make some further adjustments to help our animation look a bit more realistic.
With sharks any many other sea creatures, their heads make much more subtle movements than the rest of their body. We can achieve this effect by applying a "mask" to our vertex distortion.
Add a float (Mask Position) to the Z Axis of your object
Smoothstep this between 0 and a float (Mask Blur)
Clamp the output of this between 0 and 1
Lerp between the Mesh's modified position and its original position using the above
Feed that lerp into the Shader's Position node
And for better visual feedback, feed the Mask into your Lerp between two colors
Our model should now look something like this:
And that is a basic swim animation setup in Shader Graph.
Variations
Using the same approach as the one above you can create many different animations.
Smaller fish:
Plenty of fish swim by moving their body side to side but without the wave-like movement of a shark. To quickly achieve an effect like this, we can simply stop adding in the Z Axis values in our Side to Side Movement and adjust the mask to get the desired effect.
Stingray:
This is takes a few extra steps to achieve.
1. Keep the same X Axis vertex displacement as used in the original shader with the mask along the Z Axis
2. Duplicate the X Axis displacement but subtract from the Y Axis rather than the X Axis to create a Y Axis wave.
3. Create a radial mask so that our Y Axis wave only affects the stingray's fins. (My preferred way of creating a radial mask is to Smoothstep the Length of the object position.)
4. Lerp between X Axis displacement and Y axis displacement with the Z Axis mask. And lerp between that and the original object position using a height mask (to prevent the eyes bobbing up and down ).
And now you have an animated stingray:
Thanks for following along! I hope this provided some inspiration for creating shader based animations. If you have questions or recommendations on how to improve the shader please feel free to comment below.
Comentários