2D Rivers/Lakes in infinite terrain

2D Rivers/Lakes in infinite terrain

I've been looking into generating lakes and rivers for a 2D infinite tilemap but I'm stuck with how to handle them for an infinite map.
I can use noise/heightmaps for determining the lakes and the starting points for rivers but for an infinite map I can't use the  "flow downhill" or "carve out" technique because the player could come across the river at any point.
If I have a river that's a max of 200 tiles long, I'd have to calculate noise/map values for 33x33 chunks (my chunks are 6x6 tiles) around the player which feels excessive and very inefficient.
I have not found a way to make noise alone work for this, rivers would always be too circular if I find a range narrow enough and they'd encircle lakes, not extend outward.
Calculating voronoi cells and putting rivers at certain edges, with some extra noise to make the paths more natural, might work.
I just can't seem to find any good examples where they aren't working with known-dimension maps.


Answer 1:

Take a look at this resource: http://martindevans.me/game-development/2015/12/11/Procedural-Generation-For-Dummies-Roads/

In short, I think you could treat a river in a similar manner as a road in that example. Let’s say you are using simple perlin-noise as a heightfield. You can then sample that heightfield at any point to generate an uphill/downhill vector for that location in space using a formula like the one listed below, and you can then use that vector to trace out the route of a river. E.G. start at a random seed position, sample the vector field, step in the direction of the vector, iterate like this until the route has traveled a certain number of steps, or doubles back on itself, or goes uphill, or collides with another route, etc. As will all things procedural, it will take a lot of fiddling to achieve a believable result.

heightfield-to-vectorfield pseudocode:

float v = SampleHeightField(position);
float x = SampleHeightField(new Vector2(position.x + .5f, position.y));
float y = SampleHeightField(new Vector2(position.x, position.y + .5f));

Vector2 fieldVector = new Vector2(v-x,v-y);

Answer 2:

I had this problem, my solution was to replace causation with correlation.
Basically all algorithm try to cause a river, ie you have a mountain and you try to find a plausible river path, which is hard. But observation show that river correlate to a the slope of a mountain, ie it flows down.

SO why not generating the river first THEN generating the mountain that generate it? Basically the river define the slope of the mountain. It gives you much more power because you can now control events along the river path, it become trivial to add lake, bridge, waterfall, town, just like you generate barrier, lighting, etc … along a road.

Basically all you need now, is the starting point (highest point) and the ending point (lowest point), which you can use to join river together too, and if you are smart you can also create infinite river. You can just recursively break the river in smaller intermediate segment or events, and just interpolate between them. Adjusting the width of influence to shape terrain around it.