# Day 88: Perlin noise

It has been 35 years since Ken Perlin has discovered a technique today called Perlin noise to generate a fixed gradient noise to achieve a better looking textures in the famous movie Tron.

How Perlin noise works?

Look at the top left square at the picture. There are *four corners*,* and *each has defined a fixed 2D vector representing gradient, `G¹, G², G³, G⁴`

. For any pixel `P`

*inside* we define four vectors representing the difference between the P and each corner, `V¹, V², V³, V⁴`

. Finally, we project gradients G onto vectors V and use bilinear interpolation to transform four numbers into one.

Each square on the picture contains 4-times more changes in gradient when compared to the previous square. As the number of changes increases, the output is more and more noisy.

When these 8 images with different frequencies are combined together using [for example] a weighted average, we get quite nice procedural textures.

Non-linear smoothing function and other tricks can be used to get more realistically looking textures. Fire, skies, fluids, landscapes, islands, mountains … Perlin noise and its successors like Simplex noise represent the very basic building block of procedurally generated textures.

https://github.com/coells/100days

https://notebooks.azure.com/coells/libraries/100days

#### algorithm

def generate_gradient(seed=None):

global gradient

seed and np.random.seed(seed)

gradient = np.random.rand(512, 512, 2) * 2 - 1

def perlin_noise(size_x, size_y, frequency):

global gradient

# linear space by frequency

x = np.tile(

np.linspace(0, frequency, size_x, endpoint=False),

size_y

)

y = np.repeat(

np.linspace(0, frequency, size_y, endpoint=False),

size_x

)

# gradient coordinates

x0 = x.astype(int)

y0 = y.astype(int)

# local coordinate

x -= x0

y -= y0

# gradient projections

g00 = gradient[x0, y0]

g10 = gradient[x0 + 1, y0]

g01 = gradient[x0, y0 + 1]

g11 = gradient[x0 + 1, y0 + 1]

# fade

t = (3 - 2 * x) * x * x

# linear interpolation

r = g00[:, 0] * x + g00[:, 1] * y

s = g10[:, 0] * (x - 1) + g10[:, 1] * y

g0 = r + t * (s - r)

# linear interpolation

r = g01[:, 0] * x + g01[:, 1] * (y - 1)

s = g11[:, 0] * (x - 1) + g11[:, 1] * (y - 1)

g1 = r + t * (s - r)

# fade

t = (3 - 2 * y) * y * y

# (bi)linear interpolation

g = g0 + t * (g1 - g0)

# reshape

return g.reshape(size_y, size_x)

def banded_perlin_noise(size_x, size_y, frequencies, amplitudes):

image = np.zeros((size_y, size_x))

for f, a in zip(frequencies, amplitudes):

image += perlin_noise(size_x, size_y, f) * a

image -= image.min()

image /= image.max()

return image

#### run

*For full code including plots check the **notebook**.*

>> generate_gradient(394)

>> perlin_noise(8, 8, 1)

array([[ 0. , -0.03, -0.1 , -0.18, -0.23, -0.23, -0.19, -0.11],

[-0. , -0.04, -0.12, -0.2 , -0.26, -0.27, -0.24, -0.16],

[-0.02, -0.07, -0.15, -0.23, -0.29, -0.31, -0.28, -0.22],

[-0.05, -0.1 , -0.19, -0.27, -0.32, -0.33, -0.31, -0.25],

[-0.06, -0.13, -0.21, -0.28, -0.33, -0.33, -0.31, -0.26],

[-0.07, -0.15, -0.23, -0.28, -0.31, -0.3 , -0.27, -0.23],

[-0.06, -0.15, -0.22, -0.26, -0.27, -0.25, -0.21, -0.16],

[-0.04, -0.13, -0.19, -0.22, -0.21, -0.18, -0.13, -0.08]])