Perlin Noise Experiments (2D)

Import the necessary libraries

In [1]:
from random import random, randrange
from math import floor, sqrt
from matplotlib import cm, pyplot as plt 
from mpl_toolkits.mplot3d.axes3d import *
import numpy as np

Define the global constants

In [2]:
TABLE_SIZE = 255

Initialize the random gradient vectors

In [3]:
gradients = []
permutations = []
for i in range( TABLE_SIZE ):
    x = random() * 2 - 1
    y = random() * 2 - 1
    l = sqrt( x * x + y * y )
    x /= l
    y /= l
    gradients.append( x )
    gradients.append( y )
    permutations.append( i )

Create the uniformly distributed permutation table

In [4]:
for i in range( TABLE_SIZE ):
    t = permutations[ i ]
    idx = randrange( TABLE_SIZE )
    permutations[ i ] = permutations[ idx ]
    permutations[ idx ] = t

print( permutations )

for i in range( TABLE_SIZE ):
    permutations.append( permutations[ i ] )
[237, 25, 45, 207, 4, 177, 197, 46, 109, 12, 227, 94, 134, 226, 31, 231, 127, 15, 21, 202, 138, 173, 117, 61, 38, 159, 213, 0, 17, 194, 32, 157, 142, 49, 246, 51, 92, 69, 248, 43, 36, 247, 57, 215, 16, 216, 42, 208, 178, 82, 243, 54, 228, 99, 152, 64, 86, 108, 103, 79, 68, 34, 175, 39, 163, 22, 233, 97, 128, 137, 143, 112, 214, 191, 170, 222, 72, 67, 167, 78, 74, 110, 98, 131, 48, 123, 168, 66, 212, 225, 132, 102, 93, 235, 145, 52, 164, 126, 24, 119, 29, 18, 206, 184, 85, 165, 162, 55, 150, 224, 135, 204, 229, 218, 5, 20, 176, 151, 209, 158, 60, 180, 136, 28, 13, 198, 148, 129, 221, 146, 84, 76, 181, 217, 30, 147, 232, 160, 75, 113, 121, 91, 153, 245, 189, 190, 23, 155, 53, 203, 100, 195, 242, 236, 192, 252, 47, 234, 196, 166, 130, 14, 114, 179, 141, 244, 220, 253, 107, 188, 33, 83, 81, 154, 249, 87, 183, 58, 77, 199, 254, 187, 205, 133, 80, 172, 239, 41, 211, 37, 116, 161, 144, 139, 210, 19, 90, 125, 2, 149, 88, 71, 193, 174, 40, 6, 182, 219, 201, 1, 26, 10, 186, 185, 56, 35, 50, 27, 240, 59, 95, 3, 104, 44, 106, 62, 111, 223, 124, 73, 122, 9, 238, 251, 241, 120, 250, 96, 8, 63, 7, 89, 156, 171, 200, 65, 70, 118, 230, 140, 101, 11, 169, 115, 105]

Define the hash function

In [5]:
def hash_pos( x, y ):
    return permutations[ ( permutations[ x % TABLE_SIZE ] + y ) % TABLE_SIZE ]

Define smoothing function

In [6]:
def smooth_step( x ):
    return ( x ** 3 ) * ( x * (x * 6 - 15 ) + 10 )

Define linear interpolation function

In [7]:
def linterpolate( a, b, weight ):
    return a + weight * ( b - a )

Define function for calculating Perlin noise at a point

In [8]:
def perlin_at( x, y ):
    lx = floor( x )
    ty = floor( y )
    rx = ( lx + 1 )
    by = ( ty + 1 )
    xd = x - floor( x )
    yd = y - floor( y )
    xweight = smooth_step( xd )
    yweight = smooth_step( yd )
    tl_x = gradients[ 2 * hash_pos( lx, ty ) ]
    tl_y = gradients[ 2 * hash_pos( lx, ty ) + 1 ]
    tr_x = gradients[ 2 * hash_pos( rx, ty ) ]
    tr_y = gradients[ 2 * hash_pos( rx, ty ) + 1 ]
    bl_x = gradients[ 2 * hash_pos( lx, by ) ]
    bl_y = gradients[ 2 * hash_pos( lx, by ) + 1 ]
    br_x = gradients[ 2 * hash_pos( rx, by ) ]
    br_y = gradients[ 2 * hash_pos( rx, by ) + 1 ]
    ld = xd
    rd = xd - 1
    td = yd
    bd = yd - 1
    t = linterpolate( tl_x * ld + tl_y * td, tr_x * rd + tr_y * td, xweight )
    b = linterpolate( bl_x * ld + bl_y * bd, br_x * rd + br_y * bd, xweight )
    return linterpolate( t, b, yweight )

Visualize in 3D (Hills)

In [9]:
STEP = 0.10
RANGE = 20
HEIGHT = 0.15
STEPS = floor( RANGE / STEP )
z = []
xi = np.linspace( 0, RANGE, STEPS )
yi = np.linspace( 0, RANGE, STEPS )
for i in xi:
    for j in xi:
        z.append( perlin_at( i, j ) )
z = np.array( z )
x, y = np.meshgrid( xi, yi )
z = z.reshape( x.shape )
fig = plt.figure()
a = Axes3D( fig )
a.plot_surface( x, y, z, rstride = 1, cstride = 1, cmap = cm.jet, linewidth = 1, antialiased = True )
a.get_proj = lambda: np.dot(Axes3D.get_proj(a), np.diag([1, 1, HEIGHT, 1]))
plt.show()

Visualize in 2D (Caves)

In [10]:
fig = plt.figure()
a = Axes3D( fig )
a.plot_surface( x, y, z, rstride = 1, cstride = 1, cmap = cm.jet, linewidth = 1, antialiased = True )
a.view_init(azim=0, elev=90)
plt.show()