Created
August 4, 2025 18:12
-
-
Save mcejp/5d3c10a382dd393c27406db3b2943368 to your computer and use it in GitHub Desktop.
Perlin noise in Python. Recommend to use https://github.com/mcejp/perlin-numpy instead
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import numpy as np | |
| # legal notice: can't remember if I wrote this one myself or borrowed somewhere | |
| def perlin_noise(rng, dimensions, wavelength, discrete_angles=16, samples=None): | |
| """ | |
| Generate a single layer of perlin noise. | |
| See https://mrl.cs.nyu.edu/~perlin/paper445.pdf for background. | |
| :param rng: A NumPy random number generator for reproducible randomness. | |
| :param dimensions: Dimensions of the field. Must be multiple of wavelenth | |
| :type dimensions: tuple[int, int] | |
| :param wavelength: The spatial scale of the function. | |
| :type wavelength: int | |
| :param discrete_angles: Number of discrete angles. | |
| :type discrete_angles: int | |
| :param samples: Can be passed in instead of rng. | |
| :return: A 2D NumPy array representing the generated noise field. | |
| Its dimensions are ``dimensions[0] * wavelength`` by ``dimensions[1] * wavelength`` samples. | |
| :rtype: np.ndarray | |
| :Example: | |
| Generate a noise map with specified parameters: | |
| .. code-block:: python | |
| import numpy as np | |
| from numpy.random import Generator, PCG64 | |
| from procgenlib.synthesis import perlin | |
| # Create a random number generator | |
| rng = Generator(PCG64(12345)) | |
| # Generate the heightmap | |
| heightmap = perlin(rng, | |
| dimensions=(32, 32), | |
| wavelength=8) | |
| """ | |
| W, H = dimensions | |
| WW = W//wavelength | |
| HH = H//wavelength | |
| gradients = np.zeros((WW+1, HH+1, 2)) | |
| if samples is None: | |
| samples = rng.integers(low=0, high=discrete_angles-1, size=(WW+1, HH+1)) | |
| else: | |
| assert samples.shape == (WW+1, HH+1) | |
| random_angles = 2*np.pi * (1/discrete_angles) * samples | |
| gradients[:,:,0] = np.cos(random_angles) | |
| gradients[:,:,1] = np.sin(random_angles) | |
| image = np.zeros((W, H)) | |
| X, Y = np.meshgrid(np.arange(0, W), np.arange(0, H), indexing="ij") | |
| I = X // wavelength | |
| J = Y // wavelength | |
| U = ((X+0.5) % wavelength) / wavelength | |
| V = ((Y+0.5) % wavelength) / wavelength | |
| N00 = np.zeros(X.shape) | |
| N10 = np.zeros(X.shape) | |
| N01 = np.zeros(X.shape) | |
| N11 = np.zeros(X.shape) | |
| for x in range(W): | |
| i = x // wavelength | |
| Is = I[x,:] | |
| Js = J[x,:] | |
| Us = U[x,:] | |
| Vs = V[x,:] | |
| N00[x, :] = np.diagonal(np.dot(gradients[Is, Js, :], np.array([Us, Vs]))) | |
| N10[x, :] = np.diagonal(np.dot(gradients[Is+1, Js, :], np.array([Us-1, Vs]))) | |
| N01[x, :] = np.diagonal(np.dot(gradients[Is, Js+1, :], np.array([Us, Vs-1]))) | |
| N11[x, :] = np.diagonal(np.dot(gradients[Is+1, Js+1, :], np.array([Us-1, Vs-1]))) | |
| f_u = 6*((U)**5) - 15*((U)**4) + 10*((U)**3) | |
| f_1_u = 6*((1-U)**5) - 15*((1-U)**4) + 10*((1-U)**3) | |
| f_v = 6*((V)**5) - 15*((V)**4) + 10*((V)**3) | |
| f_1_v = 6*((1-V)**5) - 15*((1-V)**4) + 10*((1-V)**3) | |
| nx0 = f_1_u*N00 + f_u*N10 | |
| nx1 = f_1_u*N01 + f_u*N11 | |
| image = f_1_v*nx0 + f_v*nx1 | |
| return image | |
| def perlin_octaves(rng, w, h, octaves, skip_octaves=0, persistence=0.5): | |
| image = np.zeros((w, h)) | |
| for i in range(skip_octaves, octaves): | |
| # example: with octaves=4, persistence evaluates to [0.0625, 0.125, 0.25, 0.5] for wavelengths [2, 4, 8, 16] | |
| image += perlin_noise(rng, w, h, 2**i) * (persistence ** (octaves-i)) | |
| return image | |
| def perlin_octaves2(rng, w, h, wavelength, octaves, persistence=0.5): | |
| image = np.zeros((w, h)) | |
| for i in range(octaves): | |
| image += perlin_noise(rng, w, h, wavelength//(2**i)) * (persistence ** i) | |
| return image |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment