Following one man's task of building a virtual world from the comfort of his pajamas. Discusses Procedural Terrain, Vegetation and Architecture generation. Also OpenCL, Voxels and Computer Graphics in general.

Tuesday, January 28, 2014

Leveling Lakes

This post will advance the water saga.

You may want to check out the previous post about water. It ended up producing a 2D map of booleans, where true meant there was water in that location of the map. That was great, but we still had a nasty problem ahead. We needed to level the lakes.

It will be easier if we look at some pictures. Imagine this is the tile we need to compute. Initially it is just the terrain height and the sea map (in this case using a sea-level value):

And here you can see the lake and river location phase from the earlier post in pretty pink:

As you can see in the zoomed portion, these lakes often span over very irregular terrain. We knew the shoreline points for the same lake were at similar height. Figuring out the water level from A to B was the problem. We know A-B should be as flat as possible and the same applies to A and any other point in the shoreline. That's a lot of potential points.

If we had all the time in the world, we could try discovering individual lakes in one phase and then do some sort of iterative leveling by relaxation and or filtering. We had only a fraction of a second for this so we knew it had to be hacked.

We tried many different things. (When I say "we", it is not modesty plural. Michael Coates who recently joined Voxel Farm got to implement these desperate things we tried.) Most had to do with rasterization and interpolation, first from shore to shore, then we got looking into water height derivatives. Nothing really worked.

As a last recourse attempt, we tried an approach that involved a Quadtree. The idea was to encode the water bodies in a Quadtree and apply the same leveling algorithms we knew would be too slow if we were operating in individual water samples.

The shorelines produced a lot of small quads, but as we moved inside the lake we started getting larger quads.

We could see that the larger a patch, the more "detached" it was from the shore. That we liked. We could raise or sink those points much more to achieve the level we needed.

Once the water height of the larger patches is decided, we rasterize them into the water heightmap. This is a simple, fast bi-linear interpolation over the quad. Then we do the same for the smaller quads. There is a reason why you need to process all quads of a given size before processing the next level of smaller patches: You need to make sure the small quads match the interpolation values along the edges they share with a larger quad.

This overall process is quite fast. Here is a rendering of the new found water volumes. It is a bit exaggerated, but hopefully you get the idea:

We still need to work out on some of the heuristics, but it looks we are pretty much on our way.

If there is any lesson to be drawn from this, it would be when in despair, apply a Quadtree and everything will be alright.

19 comments:

There we go! Quadtrees are great for this kind of optimization, although would it be possible to further limit the number of squares by allowing them to be larger polys closer to the edge? I might be failing to see why everything needs to be kept as a square.

Also, I still see rivers being biased towards the cardinal directions. Is this a product of the water algorithms or is it something in the terrain itself?

I too see several rivers following the main axis lines. I think it is because the terrain also has features aligning with those lines. And then we have a Manhattan distance somewhere in the river pathfinding.

So will these liquids be simply stuck in space, or is it somehow going to be affected by voxels you dig out and place. Or am I missing something? I don't think we can have such huge bodies of water fully simulated in our current gen of hardware.

This is a great question, if the generation process is efficient enough to be generated in real time, does it have the complexity to say... have a dam along the edge of a lake and then have that damn destroyed?

We are not there yet, but this is a possibility. In general I think we need to simulate this at different voxel resolutions. One thing is the water coming out of the tap, another thing is a damn. We will probably need two different systems just because of how different in scope waterfronts can be.

Leveling algorithms... does this mean you plan to account for different balances of rainfall and evaporation in different climates? Could this system produce a dry lake with no water or one that is always overflowing?

I read that, but what it describes how lakes are found and where they will overflow, but it doesn't cover changes and eventual balancing of the level due to rates of flowing, raining, evaporation and draining. Without this sort of balancing, I think you're limited to varying the number of sources. That’s not ideal for varying climates or weather. The blog says: “We could raise or sink those points much more to achieve the level we needed.” So they determine the level first. Could that target level then be shallower at a higher temperature or overflow more with heavy rainfall? Could it vary based on inflow or drainage?

Looks great! I went a bit insane dealing with water, erosion, and so forth but ended up settling on using satellite data as you suggested in one of your articles. One great reference point when working with these kinds of things is to look at a drainage basin, like this one: http://imgur.com/Evg8Ayr Basically, a basin forms from mountain tops and flows down into valleys, often in a tree-like pattern (in fact, explicitly generating a tree can produce better results than attempting to run an erosion algorithm).