Create Random 3D Terrain by Using Perline Noise
This article shows the mothod to create various mountains by using Perline noise. Other article introduces Perline noise in detail. This article will mainly introduce implementation part.
Algorithm
1. Create random gradient vectors
Each grid point has a gradient vector, which has a random angle.
dirs = [
(math.cos(a * 2.0 * math.pi / 256), math.sin(a * 2.0 * math.pi / 256))
for a in range(256)
]
2. Caculate distance vector
The vector from each grid point to the target point is called a distance vector.
3. Calculate the dot product of the gradient vector and distance vector
A grid point that is close to a certain point has a stronger influence. To do this smoothly, use the following fade function.
def calculate_weighted_dot_product(gridX, gridY, x, y, period, dirs, perm):
distX, distY = abs(x - gridX), abs(y - gridY)
# The closer the distance between a point and the grid,
# the greater the weight of that grid.
weightX = 1 - 6 * distX**5 + 15 * distX**4 - 10 * distX**3
weightY = 1 - 6 * distY**5 + 15 * distY**4 - 10 * distY**3
hashed = perm[perm[int(gridX) % period] + int(gridY) % period]
dot = (x - gridX) * dirs[hashed][0] + (y - gridY) * dirs[hashed][1]
return weightX * weightY * dot
4. Merge results in multiple octaves
By merging results in octaves with different frequencies and amplitudes, you can get more natural and detailed results.
def calculate_octave_perline_noise(x, y, period, octs, dirs, perm):
noise = 0
# Add noise functions
for octave in range(octs):
amplitude = 0.5**octave
frequency = 2**octave
noise += amplitude * create_noise(
x * frequency, y * frequency, period * frequency, dirs, perm
)
return noise
Implementation
The code is here. You can get a 2D random image by running following command.
python3 create_perline_noise.py --size 128 --octs 5 --set_seed
You can get a 3D random mountain by running following command.
python create_3d_terrain.py --size 128 --octs 5 --set_seed
If you want a rough mountain, you should change the number of octs from 5 to 1.
If you want to output a different mountain each time it is executed, do not specify the set_seed
argument.
python create_3d_terrain.py --size 128 --octs 5