Video of result:
Copied and pasted from my website (it can't handle much traffic): http://vexal.us/Projects/TerrainGeneration/TerrainGeneration.html
The generation function now uses Simplex Noise instead of Perlin Noise.
Shown in the picture to the left is an example world definition file used to create the parameters for the Simplex Noise, and the parameters used for the first video above. Inside each { } is the definition for a single simplex noise function. The function runs for each x,z input coordinate, and outputs a 'y' height value. Functions nested inside functions imply that whether the nested function is applied at a particular coordinate is dependent on the output of the parent function at that coordinate. This nesting is how biomes are created. For more information on how dependencies and biome nesting works, see the next section of this page, Selective Application of Noise Function Application.
Functions inside [ ] are function systems. They signify that multiple simplex noise functions are applied at each input coordinate, with the output value being all functions combined. The amplitude and frequency of these function systems are multiplied or divided for each successive octave.
The parameter names mean:
- Frequency: The frequency of the simplex noise function. Higher values produce bumpier terrain. The input coordinates are multiplied by the frequency before computing the function output.
- Ampltitude: The peak value of the simplex noise output. The output height of the simplex noise function is multiplied by the amplitude.
- Contribute Value: Whether this function contributes to the output height value of the system -- non-contributing functions are used for dependencies.
- Octave Count: For function systems, the octave count is the number of successive simplex noise functions applied on top of each other at a particular input coordinate.
- Octave Start: The offset for the octave count.
- Persistence: In a function system, for each octave, the frequency of the function increases by a power of two, whereas the amplitude of the function is equal to pow(persistence, octaveIndex)
- Frequency Multiplier: Multiplied to the frequency of each function in a function system.
- Scale: The amplitude of a function system for each function is multiplied by the scale, and the frequency is divided by it.
- Falloff Power: The output of the simplex noise at a particular coordinate is divided by some scalar to this power.
- Dependency Type: For nested functions, dependency type determines how the child function is compared to the output of the parent function.
- Dependency Fail Action: For nested functions, dependency fail action determines how to compute values in the failure range of the parent function.
- Dependency Value: Dependency value 1 is the cutoff failure output for the child function to fail against the parent function's output. For interpolated functions, dependency value 2 is the interval outside the passing comparison of the parent function output for which the output of the child function goes to zero.
The screen shots of my terrain generation previously all demonstrated mountainous landscapes. If the goal is a huge mountain range, then this is perfect. However, if the goal is to make a useful game world, then having only mountain ranges can be problematic.
The first intuitive solution to this issue which comes to mind is to make the frequency of the mountains lower, so that the high mountain peaks are more spread out. This solves the issue of there being no flat land, but it leaves another problem: In order for the mountains to look good, additional high frequency, low amplitude height functions must be added on top of the low frequency, high amplitude mountain functions (to give the mountain a craggy, bumpy look, as opposed to a rolling, smooth look). This is fine for the mountains, but since the noise functions are applied uniformly across the entire map, then all the areas in between the mountain peaks will also become needlessly bumpy. It is not enough to simply lower the frequency of the tall mountain peaks.
The next solution is to selectively apply the high frequency height functions. One could check the height value generated by the low frequency height function at a particular coordinate, and only if the height value is above a certain threshold, then the height value from the high frequency function will be applied. This allows the areas in between mountain peaks to remain flat, and the high mountain peaks to remain craggy.
Other conditions can descriminate which functions to apply in which areas. For example, one could have a very low frequency height function which only returns 1, 2, or 3 at any coordinate. Coordinates where the function returns 1 could on top apply a set of noise functions producing a low frequency rolling hill; coordinates returning 2 could apply noise functions producing flat plains, etc.
Unfortunately, selective application of noise functions is not as straightforward as one wishes it be. As demonstrated by the following images:
The above images show that if a noise function is only applied to a specific area, there is a discontinuity at the coordinates at the edge of where the noise function is applied.
The solution to this issue is to interpolate the resulting height of the function at the coordinates where it is no longer applied so that the output of the function smoothly goes to zero.
In the image on the left, the terrain is flat except for two areas where several noise functions are applied to create the appearance of a crater or rock formation. The noise functions are set to apply at coordinates whose value of a separate random function is past a threshold. The amplitudes of the noise functions are interpolated towards zero at a specified rate for coordinates not meeting the criteria for applying the functions.
The image on the right shows bumpy noise functions that are only applied to areas where the height of the mountain noise function exceeds a specified threshold. The threshold for application is near the top of the peak, and the bumpiness of the mountains gradually decreases towards the bottom.
Other DetailsThe system I have set up is intended to be as generalized as possible. A function can be set to be dependent on the value of any number of other functions at that coordinate. The dependency conditions can be set as well as the dependency fail actions.
An example of using this system could be to have a low frequency function intended to determine what land type is at a coordinate (snow, plains, mountains, grassland, etc). A set of functions can be created intended to only be used for specific land types.