S M T W T F S
1
2345678
9101112131415
16171819202122
23242526 27 2829
30

# Problems...

Hi! So I wrote a little bit more. And this time I have pictures! Shiny pictures! Have in mind that I might jump around a lot since its kinda hard to summarize everything and have in mind I haven't finished this project so every decision I made it might be subject to change in the future.

Also, sometimes the links look black on my end so it might be hard to distinguish them from normal text.

Problems...

One of the various things that either the paper doesn't describes properly or I just don't understand them (4 page long paper? It's a trap!) is the secondary ridge line generation.

After some steps (say, 20 advance() method calls), the ridge particles are supposed to spawn two more ridge lines with opposite directions, with half the deviation from the main one in perpendicular direction to the main ridge that spawns them.

Spawning them isn't difficult. If the main ridge direction is (X,Y), their two child ridge particles would have (-Y,X) direction and (Y,-X) direction. They would proceed just like the main ridge particles but with half (one quarter in my case) the deviation of its parent.

This is the heightmap with the main and secondary ridge lines:

The paper mentions "blending" the ridge lines to compute the river network. It doesn't says with what criteria, nor if it means just blending the ridge lines or blurring all the height values, to end up with an image like the paper shows. I choose to do a gaussian blur pass over the heights to blend them all together.

Blurry vision

Plain ridge lines don't look right and it isn't enough height information for the next steps, we need to spread them around:

I found a very nice library for image filters Jhlabs.com. It has all sort of filters, though we're interested in the blurring ones.

The Java library operates on top of BufferedImage objects. Which means first and foremost that they're very easy to use. The first blurring passes I did were made with that library by "transforming" the height array into a BufferedImage, blurring it, then bringing out the new height values again to a float array.

It worked pretty well at first but after I while I had some problems. Pixel range is quite... limited. I could go only from 0 to 255 on fixed integer steps. So no matter how detailed were my height calculations, after some integer conversions they looked quite bad.

Say that you want to have 1m resolution, so one pixel is one meter. Thats quite great, you could compute a heightmap with a 2km x 2km surface and have some fun with it in no time. But on the height side, you're limited to 0-255 values (255m tall mountain isn't the definition of scary precisely) if you decide to go for a grayscale heightmap using the previous library.

So in the end I had to knock off my own gaussian blur pass which would work with the float array directly. Turns out not only works quite well, but it also takes half the time to compute than doing a filter pass over the BufferedImage. While integer operations on pixel colors should be quite cheap, there is a lot of bitshifting around plus many integer to float conversions (and viceversa), thus the blurring pass over the float array was a better option.

Family, Ridges, and their limits

Something that isn't very clear (to me at least) is that if the ridge lines really should stop only when they reach "null" height (I guess the author meant 0.0f ?). See, if the ridges are supposed to reach 0.0f, which would be the lowest point of your heightmap, then the river lines wouldn't be able to carve their way around when you spawn them later. They'd carve their way around the mountain side, then reach 0.0f immediately after and stop since you can't carve a river line anymore.

If you start your heightmap with a middle value, say, your heights go from 0.0f to 1000.0f, and you set all the initial height values to 500.0f. As it is, the ridge lines would carve their way all down to 0.0f before stopping, that is, the mountains would go under your landscape. Which not only looks bad, but it produces plain pools of river particles later on, which isn't helpful. Though I don't discard the possibility of doing canyons this way sometime in the future, it should work with some tweaking I guess.

For now, and as you might see on the previous pictures, I decided to set up an initial height value for all the map, the ridge lines go from their highest points all down until they reach the initial height of the height map instead of the lowest point. This leaves a lot of terrain for the river particles to produce more interesting lines. I made a few helper classes to map back the height values to BufferedImages so I can visualize the results at each stage, only generating the terrain without doing this in the middle should be quite fast.

So for now the ridge particles stop on any of these 3 conditions:
• They reached the "bottom" of the heightmap, which is an arbitrary value, one quarter of the maximum height in my case.
• They collided with another ridge particle (first one to discover it has collided stops, the other keeps going). Collisions within the same particle aren't considered.
• They reached the limits of the heightmap.
The child ridge line generation follows the same rules. In some rare cases child ridge particles they might collide with their parent's right after their first advance() method invocation since the child particles actually aren't "child" particles. They're normal ridge particles, thus they don't know their parent. Tragic story isn't it?

Umm...

So that does it for now There may be some ridge particle stuff that I forgot, I'll add it up to the next journal entry if I remember something valuable. Bye!
Jun 01 2013 11:05 AM

Seems like a fairly convoluted algorithm to implement, given that the results aren't really all that fantastic. Have you thought about just doing a basic erosion simulation on a ridged fractal generated terrain? After the erosion pass, you can do a waterflow sim to trace the rivers and fill the lakes, and the results are pretty decent.

Jun 01 2013 01:10 PM

I think that you are on to the "null value". Not having read the paper, I would assume that it is the baseline for the mountains, not necessarily the lowest point possible. So your "null value" would be added to what ever exists at the current height level, this would give nice smooth hills from a procedurally generated fractal or perlin heightmap, with steeper slopes climbing up from there due to the ridge algorithm. would love to see a finished 3d shot.

Jun 01 2013 02:45 PM

Seems like a fairly convoluted algorithm to implement, given that the results aren't really all that fantastic. Have you thought about just doing a basic erosion simulation on a ridged fractal generated terrain? After the erosion pass, you can do a waterflow sim to trace the rivers and fill the lakes, and the results are pretty decent.

Hmm... I thought the results from the last page of the paper looked pretty good. Though indeed this part looks quite boring, its the midpoint displacement part that gives it the bling bling needed

I'll try with an erosion algorithm with the ridged terrain, have in mind that the river particle generation (the next stage) is based on an erosion algorithm ("Erosion Based On Velocity Fields" paper I linked in the first journal), except for the sedimentation.

If I finish up the river generation process, it shouldn't be too hard I think to adapt that stage to a complete erosion pass on the terrain. I'd need to spawn more particles and add sedimentation to them.

I think that you are on to the "null value". Not having read the paper, I would assume that it is the baseline for the mountains, not necessarily the lowest point possible. So your "null value" would be added to what ever exists at the current height level, this would give nice smooth hills from a procedurally generated fractal or perlin heightmap, with steeper slopes climbing up from there due to the ridge algorithm. would love to see a finished 3d shot.

Exactly. I'm actually trying to implement the midpoint displacement stage right now, even if I don't have the river generation finished up (as it is, it traces the river a little then carves a giant hole in the ground). With what I already have, I should be able to get some results at least

What is fun about that stage is that if you have the "inverse" midpoint displacement and the midpoint displacement implemented, you can experiment a lot with it, since you just have to fed it with some data, and the algorithm fills the rest for you. Anyway, I'll have to wrap this up before that

Jun 02 2013 05:47 AM

I took the liberty of rendering out the height map. Looks pretty good.

I had to convert it to a 16 bit image, blur it slightly and add a bit of high frequency noise to get rid of the stair cased look you get when working with 8 bit displacement maps (the down-sampled one you posted).

Jun 02 2013 09:22 PM

Sweet! I'd +1 you but it says there is an error registering my vote Yep, the 8 bit range is the primary reason why I opted out of ImageBuffers. Hopefully I'll have some format set up at the end. Or I could just convert the values to ints, store them on the 24 bits of an RGB value. Which also works as long as you don't operate on the image as a real image (ie, no per-color operations).

I also plan to do some more work with the ridges later, to make them more coherent, like make them follow (mostly) a single direction. As they are its just a bunch of chaotic mountains.

Though everything should be tweakable. Deviation factors, individual ridge particle heights, stepping distance for varying the direction, child particle spawing, etc. Everything can give you a different look. If I play my cards properly, knocking off a map editor shouldn't be to tricky, but I'll have to wait and see for that.

Jun 03 2013 09:54 PM

Looks pretty cool. I think it might be interesting to experiment with doing successive layers of the procedure, or going a level or two deeper into the simulation by spawning additional particles from the second-step particles. Real terrain exhibits fractal self-similarity; ie, mountain ridges often have ridges of their own which themselves can have even smaller ridges, something I don't really see being exhibited by a single pass of this algorithm as it stands. But perhaps multiple passes or deeper recursive splits might add the detail you would need. I especially like the potential application of extrapolating detail from data sets derived from real-world sources. Almost makes it a form of explicit sparse convolution noise in that regard.

Jun 09 2013 12:38 PM

Ah, that would be useful, the problem is that my code is a mess right now, I'd have to put it in shape for doing successive passes. I don't remember right now but there are a few places where it depends on some pre-conditions, for example, river generation can't happen if there are no ridges, since river particles are placed on top of the ridges.

I thought about spawning more ridges from the subridges, thing is, that information is only generated for the river network generation. Once the river particles are finished up, all the subridge information and blurred heights are erased and the map re initialized with the river and main ridge lines only.

Feeding it some real-world data sounds more involved. Though I guess its just a matter of thinking through it a bit. It would be very nice to have that feature.

I'll have to review all the logic once I'm finished.

Though I'm not sure if you know, did you see the paper or the previous journal entry? This is the first stage of the algorithm. Later there is a diamond-square pass that should add the missing detail that you're looking for. I'm making a journal entry about it right now

Note: GameDev.net moderates comments.