ytgui / temp

0 stars 0 forks source link

perlin noise #88

Closed ytgui closed 5 years ago

ytgui commented 5 years ago

https://en.wikipedia.org/wiki/Perlin_noise/ https://www.scratchapixel.com/

ytgui commented 5 years ago

Figure_1 Figure_2 Figure_3

ytgui commented 5 years ago
import math
import random
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def hermite(t):
    return t * t * (3 - 2 * t)

def quintic(t):
    return t * t * t * (t * (t * 6 - 15) + 10)

def lerp(t, a, b):
    assert(0.0 <= t <= 1.0)
    t = hermite(t)
    return a + t * (b - a)

def normalize(v):
    v = np.array(v)
    norm = np.linalg.norm(v)
    if norm == 0: 
       return v
    return v / norm

class PerlinNoise2D:
    def __init__(self, h, w, stride):
        self.h = h
        self.w = w
        self.stride = stride
        self.permutation = np.zeros(shape=[h // stride + 1, w // stride + 1, 2])
        for iy in range(h // stride + 1):
            for ix in range(w // stride + 1):
                self.permutation[iy, ix] = normalize(np.random.randn(2))

    def __call__(self, x, y):
        #
        ix0 = x // self.stride
        ix1 = (x // self.stride) + 1
        iy0 = y // self.stride
        iy1 = (y // self.stride) + 1
        #
        nx0 = self.grid_gradient(ix0, iy0, x, y)
        nx1 = self.grid_gradient(ix1, iy0, x, y)
        value0 = lerp((x - ix0 * self.stride) / self.stride, nx0, nx1)
        #
        nx0 = self.grid_gradient(ix0, iy1, x, y)
        nx1 = self.grid_gradient(ix1, iy1, x, y)
        value1 = lerp((x - ix0 * self.stride) / self.stride, nx0, nx1)
        #
        return lerp((y - iy0 * self.stride) / self.stride, value0, value1)

    def grid_gradient(self, ix, iy, x, y):
        dx = (x - ix) / self.stride
        dy = (y - iy) / self.stride
        dx, dy = normalize([dx, dy])
        assert(0.0 <= np.sqrt(dx ** 2 + dy ** 2) <= 1.0 + 1e-3)
        return (dx * self.permutation[iy, ix, 0] + dy * self.permutation[iy, ix, 1])

def main_1():
    step = 256
    h, w = 512, 512

    generator = PerlinNoise2D(h, w, step)
    terrain = np.zeros(shape=[h, w])
    for y in range(h):
        for x in range(w):
            terrain[y, x] = generator(x, y)

    plt.imshow(terrain)
    # plt.savefig("perlin/1.png")

    fig = plt.figure()
    ax = fig.gca(projection='3d')

    # Make data.
    X = np.linspace(-1, 1, w)
    Y = np.linspace(-1, 1, h)
    X, Y = np.meshgrid(X, Y)

    # Plot the surface.
    ax.set_zlim(-2.5, 2.5)
    ax.plot_surface(X, Y, terrain, linewidth=0, antialiased=False)

    # plt.savefig("perlin/2.png")
    plt.show()

if __name__ == "__main__":
    main_1()