For more information, read our previous entries here.
As we continue developing our level, the need to improve our water shader has been lurking down the corner for a while and now, that we’re almost ready to finish our first level, we decided to come back and take a look on what we can change to make this element really shine.
As many of you might now, our game got a major makeover and so did the water, with the results being shown here.
Figure: Our water shader and new art (WIP).
As we cannot go into too much details about the implementation itself, we decided to share with you how the reactive water works.
Being inspired on games like Ori and the Blind Forest, we where simply facinated and decided to tackle the problem with our own solution, for this we used the typical “Spring net Approach”.
Hooke’s Law and why would you care about it:
Most of you would already be familiar with the famous Hooke’s Law used to calculate Spring forces when stretched or compressed, it reads like this:
F = -k * x
This handy little formula says the following: “If we compress or stretch a spring, it will respond with a force equal to the displacement times a spring constant pointing to the opposite direction”. This formula applies to any spring, and can solve complicated systems of interconnected springs, and this is where we need it.
Now imagine if you would, that our water surface, now is composed of a series of “molecules”, all of them connected with their neighbors with a spring, simulating water tension. For simplicity we will assume that the molecules itself cannot move from their horizontal position and can only go up and down (this is not true, but we don’t want to add an overly complex system).
Figure: Behold my paint skills.
When this bed of springs is excited in some way, the springs will move from their rest position:
Figure: Excitement on the water surface.
The springs bonding each “molecule” will stretch or compress to accommodate for this new position, creating an elastic force that will make the water react.
Figure: A single spring being stretched.
In the previous image, the dashed horizontal line is the normal length of the spring (that in which the force is 0). The solid red line is the new length of the spring. The vertical line is the vertical height of this particle. We can calculate the new “Delta X” on the spring length with our old friend Pitagoras.
Lo + dx = Sqrt(Lo*Lo + h*h)
Now, if we assume, that the original Length Lo is small enough, we can do a pretty rude simplification that will help us remove an square root, leaving
dx = h
We also know that each of our molecules are attached to 4 springs at most, and that F = m*a
Figure: Getting acceleration from the sum of forces.
Now that we have the acceleration, we can use a compute shader or material to calculate the current height displacement of each “texel/molecule”, for this we need to be able to translate acceleration into position, given that Acceleration is the rate of change of velocity and velocity is the rate of change of the position, we use the following formula (gotten by integrations)
x_f = x_i + v_f * t
We can create a variable for getting the delta time in between each frame, but for this example we will simplify t := 1. We can calculate v_f by adding the acceleration to the velocity on the last frame. All of this values are obtainable if we store the previous heights of 2 frames prior.
Figure: Resulting shader.
Here you can see the each section of the simulation, first we obtain the sum of all the height displacement (x1+…). Then we subtract 4*xo to get the sum of all the dx. If we multiply this by a handy little variable called SpringMassRelation, which is the relation of the k constant and the Mass of the water molecule, we can get the acceleration.
With this, we first obtain the previous velocity, by subtracting the previous heights and we add the half of the Acceleration (the 1/2 results by integrating the formula) (strictly speaking we should be multiplying by a DeltaTime here, but we can always add this later). With the resulting Current Velocity, we can just simply add the velocity to the previous height, to get the current height of our texel (remembering we’re missing an important time factor here).
All in all, this isn’t supposed to be an strict simulation, but one that gives us enough leeway to have simulating wakes and shallow water.
Here you can see the results:
You can always add some nice things like a waterfall hitting the water surface:
This is done simply by exciting the surface with 3 generators that randomly punch the water with fury and spite.
Hope this tutorial is good enough for you guys that wanted to know how the simulation took place, remember that the in-depth (huh), tutorial on the water reaction are on the previous dev-blog entry, this was done simply to explain a little of the physical simulation that we followed, with all its flaws and approximations.