Experimentation with drainage on proceduraly generated terrain

Hi folks,
It’s been a while since i was active on this forum. Nice to see the project flourishing.

A few times in the past when playing with procedurally generated terrain i got annoyed by my inability to create realistic rivers; Generating terrain on which every point above sea-level has at least one lower neighbor is quite tricky.
A few months ago i had a brainwave. I can generate maps where rivers correctly flow down hill in O(log n) time.
Babylon.js provided an easy tool to create a demo so here i am.

If anyone’s interested in the algorithm you can find it here:

If you just want to play with the demo:
https://mrdunk.github.io/flowing-terrain/implementations/ts/dist/flowing_terrain.html

It works fine for me on Firefox and Chrome.
I don’t have any other browsers to test on so let me know if you have issues.

dunk.

10 Likes

Neat :slight_smile:

I should probably put some screenshots too:

2 Likes

I added some trees and better camera control.
It’s really nice how i can draw so many trees without taxing the CPU much (Babylon’s thin instances) but shadows are expensive.
There’s a checkbox under Tools -> Environment -> Show tree shadows to switch shadows on/off.
If you are on a “proper” computer they make things far prettier but at the cost of FPS.
My laptop drops fro a full 60FPS without shadows to ~30-40FPS with them.
My phone gets really chunky with shadows enabled but still super smooth without.

Note that shadows are off by default to allow rendering on CPU bound platforms (Phones, etc.).

I intend to experiment with just making the land under trees a darker colour to get a similar but cheaper effect. Maybe even enable proper shadows for part of the map when zoomed in enough.
Also adding texture to the land is on my TODO list; Sandy beaches, rocky hill tops, etc.

This was supposed to just be a demo of my heights algorithm but it’s actually looking good enough to use for something…


An unrelated experiment i’m interested in trying it to pause rendering when nothing has changed; This demo really slows down other tasks on my laptop when the tab running this demo is focused, even when every frame is identical.

4 Likes

This is very cool. Also I like the camera interactions you have implemented here. They are very snappy.

This is really cool !!! adding @Evgeni_Popov who might be able to help with the shadow perf.

We would need a repro to be able on the shadow front. What could help is disabling shadows when the trees are further from a given distance by using LOD.

So Babylon’s Inspector can actually be launched via Tools -> Debug -> Launch Inspector.
Looking at a randomly generated landscape, Active Faces shows the following:

  • without trees or shaddows: 20197
  • with trees, without shadows: 578917
  • with trees and shadows: 1137637

So shaddows add one face per “real” face.
That makes sense.
If i’m going to use built in shadows, i think it’s just the cost of doing business; More stuff to draw == lower FPS.

An interesting observation: My CPU does take on a lot of load when rendering this scene; ~60% CPU time used by Chrome on the machine i’m sitting at just now.
But there is relatively little difference in CPU load with or without trees and shadows.
This implies i’m GPU bound rather than CPU bound here; The GPU is struggling to draw all the trees in a timely manner.
This also implies i’m using Thin Instances as i intended: Keeping the CPU out of the loop.

Out of interest, I’ll create a playground demonstrating this behaviour over the next few days.

1 Like

so this is interesting.
have a play with this:
https://playground.babylonjs.com/#UJ43PF#50

You have checkboxes to change the ShadowGenerator mapSize
and a slider to change the number of objects casting shadows.

The grid of shadow casting objects is 100 x 100.
It’s defined in the playground under gridSize so is easily changed but my GPU seems to have a hard limit on the number of shadows it can cope with so there’s not much point me increasing that number any more.

The number of divisions in the groundplane receiving the shadow doesn’t seem to make much difference to performance so i didn’t bother putting it behind a control.

you’ll probably need to load your CPU a bit to see anything interesting if you are testing on particularly capable hardware…
(my anti virus SW is running at the moment and that actually makes things more pronounced.

On my system, the ShadowGenerator.mapSize property makes the biggest difference.
The number of shadow casting objects does make some difference but it’s much less pronounced.

It’s hard to put hard numbers on this without a properly sandboxed system.
With other tasks competing for the CPU and/or GPU the results of this sort of testing are always going to be a bit vague.

I also see some interesting artifacts.
I presume this one is GPU dependent. Look at the attached image.
I presume the reason some of these objects are casting shadows and some aren’t is either hardware limitation of the GPU or a bug in the WebGL implementation i am using.
A limited counter size for tracking unique shadows maybe? It certainly seems to calculate the ones furthers from the light source first and gives up after ~5900 objects.

Anyway,
i’m not really looking for feedback here. Just found this interesting.
I’ve also get a lead to improve performance on my maps: Reduce ShadowGenerator.mapSize.
(Obvious in retrospect.)

So turning off usePoissonSampling and reducing mapSize to 512 gives me a big performance boost and i think the “square-ish” shadows kind of suit the low-polly look.

4 Likes

this is rad!

Your PG missing shadows is because the light is not correctly positionned.

Try this:

https://playground.babylonjs.com/#UJ43PF#52

Hu, interesting.
I was interpreting the DirectionalLight’s vector as being directional only.

So moving the light source further out changes the pattern but we still see strange artifacts with over ~6k meshes. I presume this is us hitting the limitations of the 4096 mapSize.
See https://playground.babylonjs.com/#UJ43PF#53 .
I’ve updated the playground to allow more meshes. (Now 200 x 200 grid.)


This isn’t a problem to me. I’m just playing with edge cases.

So i’m not convinced this playground is actually triggering the issue i see with my dynamic scenery though.
The workstation i’m currently sat at experiences a full 60FPS for all the different settings on this playground.
My dynamic scenery slows down a lot on the same machine.
I’ll keep digging.

The initial position is taken as the opposite of the direction for a direct light, that’s why updating the direction will also update the position. Note that you can still update the position afterwards by updating the position property.

The missing shadows/artefacts are because of the poisson sampling, try another sampling method or “None”. Also, here’s a PG that shows the frustum of the light and which helps understand what’s going on:

https://playground.babylonjs.com/#UJ43PF#55

Try to set the “Auto Calc Shadow ZBounds” property of the light in the inspector to see how the frustum tighten to fit the shadow caster objects. Having a right frustum also helps for shadow rendering (see Shadows | Babylon.js Documentation).

1 Like

For anyone who ends up on this post looking for the final results,
this playground incorporates @Evgeni_Popov 's last suggestions: turned off all sampling and set light.autoCalcShadowZBounds = true;.
I’ve also set the upper bound of the grid to 300x300 shadow casting objects.
https://playground.babylonjs.com/#UJ43PF#58

On my current laptop, i see no missing shadows up to around 40,000 polygons (1280000 faces).
After that the missing shadows line up along the direction of the camera’s direction vector. I suspect the exact number will depend on the exact size and spacing of your shadow casting meshes so test thoroughly.
In the Playground you can see the number of polygons displayed in the browser’s JS console.

Quite impressive numbers.
More than enough for my little project anyway.

1 Like

I’ve been playing with the SceneOptimizer.

It’s a bit fiddly but worth the effort.
Now when you load my scene it starts with low display settings and increases the settings until the scene drops below a set FPS.
This lets me have shadows on by default but still have the scene render-able by lower power devices.
You can play with the threshold in Tools -> Debug -> Maximum FPS menu.

Even if you don’t plan to go as far as i have and have the whole scene visibly change detail level, it’s still worth playing with this stuff.
eg:
Turning on BABYLON.MergeMeshesOptimization(0) made a noticeable difference to the work my GPU was doing which even if you are not maxing out your GPU yet, will still save battery and reduce fan noise.

Hrm. I wonder if some of these optimizations should be callable from outside the sceneOptimizer…?
BABYLON.MergeMeshesOptimization(0) would be a prime candidate.

1 Like

Next on my list is to re-visit my touch-input interface.
i remember having it working well
but now it’s hard to differentiate between double-drag and pinch events.
Maybe i forgot to scale movement according to the current frame rate?

Once i have that working again,
i need to submit some of my Mouse and Touch input ideas for inclusion in Babylon so everyone can benefit.
I’ve currently got code that lets you control /any/ camera movement axis from mouse or touch inputs instead of just the usual default pan up/down/left/right.

I’m still having fun with this.
I’ve been playing with shaders to add a bit of texture to the landscape.


Next on my TODO list is to get rivers looking a bit more organic.
It’s ironic that the thing that started this whole project (getting rivers to always flow down hill) currently looks the worst. Too many sharp corners.
It’s because they follow the mesh vertexes exactly and the size of the faces is actually quite large.
I’m going to have to cheat a bit and round the corners even if that means rivers don’t exactly follow the lowest path anymore.

Also i should add a sky since it’s easy and would make the screenshots look better.
And get the tree placement less random; More along river banks. Less at high altitude.

Then what?
Buildings, roads and fields maybe?
It would be a good canvas to build some sort of game on top of…

Anyway, the link still works if you want to create your very own island:

5 Likes

huh.
The land texture is currently broken on my Android phone (Pixel 3) in both Chrome and the built in Android browser.

The symptom is the land all appears black. (The sea and rivers are fine.)

It appears the shader program is quitting when i call sqrt(...).
I’ll fix that tonight. (Performing a square root here is far too expensive anyway.)
I’ll also dig around online a little and see if it’s a known issue.

Anyway, it works for me on a “propper” computer.
If if you are seeing this, sit tight and it should work later.

1 Like

This looks similar to what i’m seeing:

Anyway, i’ve merged a fix.
Works on Android again.
Please post here if look broken for you.