I'd like to implement a procedural generation of a terrain.
After a thorough research I came up with a conclusion that it should be implemented with one of the gradient (coherent) noise generation algorithms, for instance Perlin Noise algorithm.
However, I don't want the generation to be completely random. I'd like to apply some constraints (like where should be a mountain range, or where should be a lowland etc.).
Question:
For example I have a curve which represents some landscape element. The curve is an array of points.
- How can I improve the Perlin Noise algorithm, so that along this
curve the noise will have a preferred range of values (or, for
simplicity, value 0)?
- Alternatively, if I have a texture which is an
output of Perlin Noise algorithm (2d array), how can I transform it
to the desired noise (influenced by the curve)?
How can I improve the Perlin Noise algorithm, so that along this curve the noise will have a preferred range of values (or, for simplicity, value 0)?
This can be achieved by transforming each pixel in the map through a simple p -> f(p)
mathematical function.
Consider a function, which maps the purple line into the blue curve - it emphasizes lower located heights as well as very high located heights, but makes the transition between them steeper (this example is just a cosine function, making the curve less smooth would make the transformation more prominent).
You could also only use bottom half of the curve, making peaks sharper and lower located areas flatter (thus more playable).
"sharpness" of the curve can be easily modulated with power (making the effect much more dramatic) or square root (decreasing the effect).
Implementation of this is actually extremely simple (especially if you use the cosine function) - just apply the function on each pixel in the map. If the function isn't so mathematically trivial, lookup tables work just fine (with cubic interpolation between the table values, linear interpolation creates artifacts).
(Copied from my aswer to another question)
If all you need is to transform the generated noise into a known value range (like -1.0 - 1.0), use this algorithm:
function scaleValuesTo(heightMap, newMin, newMax)
{
var min = min(heightMap);
var range = max(heightMap) - min;
var newRange = newMax - newMin;
for each coordinate x, y do:
hrightMap[x, y] = newMin + (heightMap[x, y] - min) * newRange / range;
end for
}
I'd like to apply some constraints (like where should be a mountain range, or where should be a lowland etc.).
This is a bit more complicated, because you need to specify where the mountains will be. The easiest solution is to create a separate height map (you can do this either procedurally or load it from a bit map, it doesn't matter) with the rough shape of the terrain desired and then add it together with your noise map. There are a few things to keep in mind:
1) Make sure there are no sharp edges on the template map (those would be very visible on the combined map). Box blur is usually enough for this purpose, although a more complex filter like Gaussian blur gives better results (in some occasions, box blur will preserve some sharp edges on the map).
2) You may want to give higher priority to the noise map in higher elevations and lower priority in lower elevations. This can be easily achieved by doing something like this: combinedMap[x, y] = templateMap[x, y] + (0.2 + 0.8 * templateMap[x, y]) * noiseMap[x, y]
.
You can see this approach on this diagram:
(I have made this for my own terrain generation project)