Jan 292016
 

Water Simulation

In the beginning there was a line

I have always been fascinated by the concept of procedural terrain generation but I’ve not really had many reason to dig deeper into it professionally. A few months ago I decided that my Christmas 2015 personal project would be to experiment with terrain generation and dynamic water.

So where to do we start, first up I’m a firm believer in not re-inventing the wheel and terrain generation has been done already; for my experiments I found an excellent description of the fractal technique by Paul Martz located here: http://www.gameprogrammer.com/fractal.html

Since I’m using Unity I set about implementing the 2D generation first, using OpenGL calls in the render pipeline to draw the results.

Terrain2DGen_01

A single iteration of the generator produces what you might expect a line with a single kink in it.

 

Terrain2DGen_02

Two iterations of the generator produces something a little more interesting.

 

Terrain2DGen_04

and nine iterations produced something that was starting to look like the cross section through some rugged terrain.

Of course it was at this point that I cranked the iterations up to 100 and that was the last interaction my machine had with me as it fell into that wormhole that eventually required a reboot. Side note to self; even after all this time… You never learn. :)

Then there were three dimensions

Terrain2DGen_05

Calling it a day with my 2D experiments I moved onto 3D generation using the Diamond-Square algorithm detailed in the article.

In case you were wondering the green cube beneath the wireframe is actually where the procedural generation code lives, this gives me a fixed render point in space for the generated mesh (I like to think of it as the holographic projector).

Terrain2DGen_06

Once I was happy generating the terrain from a random seed value I made some changes to the code enabling me to specify the seed value. While experimenting you don’t want the terrain constantly changing between multiple runs of the simulation, I also added a texture of the height map data to the top left of the screen for easy reading and added some basic texturing using alternative colors for each triangle in the mesh.

To test the system I generated multiple terrains using the same seed value and changed only the iteration count, the theory being that I would end up with the same general shape just in higher and higher resolution.

Terrain2DGen_07

Two iterations.

 

Terrain2DGen_08

Three iterations.

 

Terrain2DGen_09

Four iterations

 

Terrain2DGen_10

And finally five iterations.

Given the towers in the middle of each edge I suspect my generation code might have a small bug somewhere. Normally I would dig deeper into the issue but I have limited time on this experiment and the terrain not being perfect isn’t a show stopper for me.

Just add water

Now we get to the fun bit of the project, adding a water simulation to the environment. My first approach involved calculating the inclination of each triangle, this would then be used to determine the flow of water over the triangles surface.

Terrain2DGen_11

In the above image the red marks are the triangle normals and the blue marks indicate the direction of water flow over the triangle surface. A good example of that can be seen in the middle where the six middle triangles all point towards the dip in the center.

It was only when I was creating a formula to calculate how the height of the water at the 3 points of a triangle change over time that I realized I have over complicated the process (not uncommon). What I really needed was simply a second height map for the water, except the water map would only represent the volume of water at each point, the actual height would need to be combined with the terrain height as well.

So first up, getting a second height map rendering over the top of the first one.

Terrain2DGen_12

Above we have two height maps being rendered, when the water level is at 0 I drop its vertex slightly so it appears below the terrain height (instead of z fighting with the terrain mesh). The reason you can see the blue blob is because I have added a small amount of water at one point on the water map (i.e. the center point of the blue).

This is the water simulation algorithm I ended up with;

   Create an empty water height map called Ocean

   for each point in the water map
   {
      Water level = terrain height map + water height map

      negative_Volume = 0
      Create an empty 3 x 3 grid called Puddle.
      Set middle coordinate of Puddle to be the current water level.
      Place surrounding height levels into the Puddle (terrain height + water height).
         If point is lower than middle
            Add difference to negative_volume. 

      Create a scalar by calculating 1.0f / negative_volume;
	
      Normalize Puddle values using the scalar.
      Negate the Puddle values; positive values indicate water should flow, negative are walls.

      If scalar is 0 then the water has nowhere to go
         Add the current water height value into the Ocean
         Continue to the next point.
	
      Calculate volume of water leaving this point for the elapsed time
         i.e. displacement_amount = Current Volume * delta time
	
      Subtract the volume from the current water height, add the remainder to the Ocean.
	
      For each positive value in the puddle.
      {
         Ignore if it is the center point.
         Add to the Ocean the displacement_amount * Puddle normalized value.
      }
   }

   Copy the Ocean into the water map.

The intended effect is that over time water is always trying to find the lowest point to flow to.

Terrain2DGen_13

Taking the algorithm and running it for several seconds the water bump we started with at the top of a hill runs off either side and pools where it can. Note that we handle the edge of the world as a wall and hence water doesn’t flow out of the system.

Technically it is doing everything correctly but at this close range it might not look correct to really see the system in action we need to go big. Returning to the high resolution terrain with lots of detail I implemented a function to add an amount of water to a specific location in the world when I press a key. It means that you can see a small pyramid appear as the water level is increased and it slowly sinks down as the water flows away.

Water Simulate from Michael Carr-Robb-John on Vimeo.

 

It was about this point that I ran out of the time I had given myself for the project, not that I’m saying I won’t be returning to this again in the future but its time to put it aside for now and write up some notes (like this post). Looking back I am very happy with the results, it’s certainly been a very fun little project to work on.

Notes for the future;

The current water algorithm is more of a displacement simulation, rather than a flow simulation, what I mean by that is water behaves differently depending upon it’s surroundings, a body of water moving down a river has a lot more speed and force than the rising levels in a lake. If the simulation was calculating forces when water moved that data could be applied to erode the terrain, which would be an additional bonus.

You have probably noticed that the water can at times look squarish and appears incorrect, with some rendering changes the popping effects and indeed the overall visuals of the water can be greatly improved making viewer believability more likely;

  • Alpha blending the water if any of the points are at 0. This will help to bring the water in slowly rather than popping it into view.
  • Adding translucency to see the terrain below.
  • By using the general direction of travel for the water it should be possible to animate movement on the waters surface.
  • Detecting areas where splash effects would occur, such as in a stream can add additional details to the simulation.

Water Simulation

 

  One Response to “Experiments with Terrain and Water”

  1. This website is pretty awesome, keep up the great work and write more posts.

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)