-
Notifications
You must be signed in to change notification settings - Fork 0
/
perlin2D.py
108 lines (94 loc) · 3.76 KB
/
perlin2D.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""
A fast and simple perlin noise generator using numpy.
Source: https://github.com/pvigier/perlin-numpy
"""
import numpy as np
def interpolant(t):
return t*t*t*(t*(t*6 - 15) + 10)
def generate_perlin_noise_2d(
shape, res, tileable=(False, False), seed=0, interpolant=interpolant
):
"""Generate a 2D numpy array of perlin noise.
Args:
shape: The shape of the generated array (tuple of two ints).
This must be a multple of res.
res: The number of periods of noise to generate along each
axis (tuple of two ints). Note shape must be a multiple of
res.
tileable: If the noise should be tileable along each axis
(tuple of two bools). Defaults to (False, False).
seed: Seed for random generator.
interpolant: The interpolation function, defaults to
t*t*t*(t*(t*6 - 15) + 10).
Returns:
A numpy array of shape shape with the generated noise.
Raises:
ValueError: If shape is not a multiple of res.
"""
delta = (res[0] / shape[0], res[1] / shape[1])
d = (shape[0] // res[0], shape[1] // res[1])
grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]]\
.transpose(1, 2, 0) % 1
# Gradients
np.random.seed(seed)
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))
if tileable[0]:
gradients[-1,:] = gradients[0,:]
if tileable[1]:
gradients[:,-1] = gradients[:,0]
gradients = gradients.repeat(d[0], 0).repeat(d[1], 1)
g00 = gradients[ :-d[0], :-d[1]]
g10 = gradients[d[0]: , :-d[1]]
g01 = gradients[ :-d[0],d[1]: ]
g11 = gradients[d[0]: ,d[1]: ]
# Ramps
n00 = np.sum(np.dstack((grid[:,:,0] , grid[:,:,1] )) * g00, 2)
n10 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1] )) * g10, 2)
n01 = np.sum(np.dstack((grid[:,:,0] , grid[:,:,1]-1)) * g01, 2)
n11 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]-1)) * g11, 2)
# Interpolation
t = interpolant(grid)
n0 = n00*(1-t[:,:,0]) + t[:,:,0]*n10
n1 = n01*(1-t[:,:,0]) + t[:,:,0]*n11
return np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)
def generate_fractal_noise_2d(
shape, res, octaves=1, persistence=0.5,
lacunarity=2, tileable=(False, False),
seed=0,
interpolant=interpolant
):
"""Generate a 2D numpy array of fractal noise.
Args:
shape: The shape of the generated array (tuple of two ints).
This must be a multiple of lacunarity**(octaves-1)*res.
res: The number of periods of noise to generate along each
axis (tuple of two ints). Note shape must be a multiple of
(lacunarity**(octaves-1)*res).
octaves: The number of octaves in the noise. Defaults to 1.
persistence: The scaling factor between two octaves.
lacunarity: The frequency factor between two octaves.
tileable: If the noise should be tileable along each axis
(tuple of two bools). Defaults to (False, False).
seed: Seed for random generator.
interpolant: The, interpolation function, defaults to
t*t*t*(t*(t*6 - 15) + 10).
Returns:
A numpy array of fractal noise and of shape shape generated by
combining several octaves of perlin noise.
Raises:
ValueError: If shape is not a multiple of
(lacunarity**(octaves-1)*res).
"""
noise = np.zeros(shape)
frequency = 1
amplitude = 1
seed = seed
for _ in range(octaves):
noise += amplitude * generate_perlin_noise_2d(
shape, (frequency*res[0], frequency*res[1]), tileable, seed, interpolant
)
frequency *= lacunarity
amplitude *= persistence
seed += 1
return noise