Hi! I decided to write up what I was doing because: a. I like to write. b. Writing about I'm doing helps me to understand what I'm actually doing and c. Someone might benefit from it. So behold my random ramblings about my current project, a random (hopefully) believable terrain generator. You probably won't get any deep insight from me since I'm no expert (probably written less than 20k total lines of code in all my projects + uni assignments) and I don't promise to finish up this project sometime in the future but you might find the links/info useful
So, I set aside a renderer I was working on (Java + LWJGL) because I got interested in procedurally generated stuff in general. I managed to make a random map generator thanks to Perlin's noise reference implementation (well, not exactly that one, a faster one I found somewhere which wasn't as fast as I thought it was once I measured it ) and rendering it with LWJGL.
I took many shortcuts by using Java's ImageBuffer class which simplified storing the heightmap instead of doing everything by hand (tried once to figure out JDK's PNG decoder, never went there again). But while it was fun at first, the results were lackluster in the sense that it was just a massive blob of noise rather than actually believable terrain.
The next step was trying to spice it up a bit...
I started to research water erosion in terrain because it seemed the most sensible way to get a pretty terrain out of a random heightmap.
Found several papers, most of them revolving about placing water particles in a terrain, with mass and a direction, then simulating their movements through the terrain while calculating how much sediment they'were transporting/depositing. Here is a link to some slides filled with references Bedrich Benes - Hydraulic Erosion for Computer Graphics (this particular link sometimes works and sometimes it doesn't, here is the site where it comes from) And most of those papers cross reference each other anyway.
Seemed pretty fun but I never got around implementing any of those algorithms.
"Fractalish" Terrain Generation
After a while I found this paper Ridges and Rivers, A Bottom Up Approach (University of Paris site) which sounded not too complex and tackled the issue from another perspective. Instead of bringing random features to the "believable landscape" land, it tried to start with actually believable features (ridges and rivers) and then filling up the gaps with a fractal method (midpoint displacement, also known as the Diamond Square algorithm).
It was also interesting that according to the paper, the method could generate a 512x512 heightmap in 250ms, but made no mention about the hardware it was being used. After a while I found a cross-reference (that I can't find right now) mentioning that Belhadj and Audibert used a Pentium 4 (3,2Ghz I believe). Since P4s weren't Intel's best CPUs precisely, it sounded promising.
Rivers and Ridges, Bottom Up Approach
This method has 3 distinctive phases.
First one is generating a ridge network by just tracing ridge "particles" through a semi random path with their heights following a normal distribution. I'd say that the deviation for the normal distribution depends on the specific normal distribution function that you use (there are many). The (1/4 * heightmap_width) described in the paper looked too high in my case, I used 1/8 of the width instead.
Second one is generating a river network with particles starting at the top of the ridge lines and simulating their movement through the terrain. Movement follows what is described in this paper An Erosion Model Based on Velocity Fields, but I found it better explained in this one which is based on the same method Hydraulic Erosion of Soil and Terrain.
Third one is filling up the gaps of the terrain in two steps, one "inverse" diamond-square algorithm pass and then one regular diamond-square algorithm pass.
Starting With the Implementation, Ridge Time!
Implementing the ridge network generation wasn't as hard as I thought at first. I'll detail what isn't that obvious from what the paper described.
The ridge generation uses two functions for controlling its movement. The height is described by a normal distribution (which I know from Statistics course... which I failed ) and its lateral movement is described by a Fractional Brownian Motion.
I don't have the slightest idea what a FBM is but it kinda looked like I could make something similar by having a direction vector along which the Ridge Particle moves, and rotating it randomly in a defined angle range (ie, -30º to 30º) after a certain ammount of "steps" have been completed. Always rotating by the opposite direction of the previous rotation to (hopefully) avoid advancing in circles.
The particle would have an "advance()" method that would advance its Position by its Direction vector (normalized so it advances one length unit). A Ridge Network Generator would check the particle's length and modify its course after a certain amount of steps have been made. By "modifying its course" I mean a two dimensional direction vector, a 2x2 transformation (rotation) matrix and some matrix/vector multiplications.
Besides drawing the ridge lines on the heightmap, you also need to mind their collisions with other ridge lines. I made it so they would collide with each other if the ridge particles were different (ridges don't care to collide with themselves) as long their heights were different. So if two different ridge lines cross each other and they have the same height at that point, they continue with their paths normally.
Its important that the ridge particles don't check for collisions with themselves because I'm using floating point values for their movements, which don't map directly to a heightmap which is a regular grid of points. If somehow the ridge particle advances and it ends up in the same pixel/height vertex/etc it was before, it would collide with itself and stop immediately otherwise.
I'll details the problems I found and how I dealt with them in the next entry. This one is large enough as it is Bye!