Buoyancy Update
watch?v=hMZDzfgf-n8
Implementing buoyancy for large objects was trickier than I expected, for two reasons:
First, the Gerstner equations tell us that a point A of the nominal surface shifts to the location B of the deformed surface. However, given the coordinates x and y of a point B = (x, y, z) it is not a trivial task to calculate z. It quickly became apparent that an iterative algorithm is needed to solve the problem. The algorithm tries to find a good approximation of the point A that is shifted to point B and consequently allows us to calculate z. The algorithm is implemented in blueprint and just one iteration is enough for good locking results. Even disabling the algorithm results in only a small error of the calculated wave height that may be acceptable in some cases (very large, slow moving of far away objects).
The second problem is CPU performance. Because of the large number of Gerstner waves I’m summing up in Blueprint, CPU performance can become a problem. Having a lot of floating objects, each of them evaluating the wave height at many points at 60 fps is not a good idea. Luckily, there is a lot that can be done to optimize CPU performance:
- Use the simple buoyancy shown in my previous video if you can get away with it. It only calculates the water displacement at one point. It works well for small, lightweight objects (floating leaf, particles etc.)
- It is not necessary to update the buoyancy force at each tick. A few times a second is more than enough. With this measure, the performance can be improved a lot. (Already implemented, see below)
- Since the motion of the water is deterministic, the motion of a passive floating object could be calculated beforehand and played back as an animation. This way, the amount of floating objects would not be limited by an expensive buoyancy calculation on the CPU.
- One should only calculate buoyancy for objects that are visiible. For far away objects, the simple buoyancy mode should be used.
- Implement the buoyancy calculations in C++ instead of blueprint (it’s on my to do list)
- I also noticed that CPU performance is a lot better in standalone mode than in play in editor.
So, how exactly does it work? Similar to handkors approach, I calculate the wave height at multiple points relative to the floating object. I think they way how I apply forces on the object is a bit different. I’m using a physics simulation with the following forces acting on the object:
- gravity (UE4 physics)
- intertia (UE4 physics)
- buoyancy
- damping (linear and angular, UE4 physics)
- horizontal drag force
Each object has an array of points defined in local object space where the wave height is evaluated. This array of point locations is generated automatically, taking into account the dimensions of the object. You can set the number of points in X and Y direction on the properties of the buoyancy component. (did I mention that buoyancy is implemented in a component that you can simply add to any static mesh?)
If a point is below the water level, an upwards buoyancy force is applied at the location depending on the depth.
For performance reasons, not every point is evalueted at each tick. In the video above, each box has 9 points, and at each tick the buoyancy force of only 3 of them is updated. As you can see, the buoyancy looks fine.
If you look carefully at the video above, you can see that the boxes are also drifting horizontally. They are moving slower than the horizontal movement of the water, as if the water was gently pushing the boxes as it’s flowing around them. To achieve this effect, I calculate the relative horizontal speed of the floating object and the water surface, and apply a drag force depending on the speed difference.
Compare that to simple buoancy mode, where the floating object is locked to a spot on the water surface.
The floating boxes are not stuck on the water surface. As you can see in the video, one of the boxes slides into the water. The objects can be lifted from the water or submerged completely if you apply additional forces.
Starting at 30 seconds into the video, I display debug spheres at the locations where the wave height is evaluated and the buoyancy forces are applied. You can see some of the spheres flickering or disappearing for a fraction of a second. At these moments, the center of the sphere is above the water level, and thus no buoyancy force is applied.
These are the setting currently available on the buoyancy component: