Closed steo85it closed 5 years ago
Hi,
To make Perlin noise tileable, you must set the gradients of the right side equal to the ones of the left side and the same for top and bottom. So, here is the code to generate tileable 2D Perlin noise:
def generate_perlin_noise_2d(shape, res):
def f(t):
return 6*t**5 - 15*t**4 + 10*t**3
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
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))
# Make the noise tileable
gradients[-1,:] = gradients[0,:]
gradients[:,-1] = gradients[:,0]
# Same as before
g00 = gradients[0:-1,0:-1].repeat(d[0], 0).repeat(d[1], 1)
g10 = gradients[1: ,0:-1].repeat(d[0], 0).repeat(d[1], 1)
g01 = gradients[0:-1,1: ].repeat(d[0], 0).repeat(d[1], 1)
g11 = gradients[1: ,1: ].repeat(d[0], 0).repeat(d[1], 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 = f(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)
Then, the fractal noise function will also generate tileable noise.
Seems, there is a typo in the condition, shape
must be a multiple of 2^(octaves-1)*res
. I will fix that in the README, thanks!
Hope it helps.
Makes absolute sense, it's elegant and it also works perfectly! :) Thanks a lot for the other clarification too, now I should have what I need.
You're welcome! :)
Nice picture by the way.
@steo85it Hi, I'm very interested in how you generated the texture simulation. Would you share some more details or possibly the code to get your results?
Thank you.
Hi, sorry, just saw your message. I only slightly modified the code provided by the author to adapt it to my needs (texture with a certain amplitude over a given scale, consistent with planetary terrain roughness). Plus, if you want shading, you can get hillshade here https://github.com/titusjan/hill_shading .
def generate_fractal_noise_2d(shape, res, octaves=1, persistence=0.5):
noise = np.zeros(shape)
frequency = 1
amplitude = 1
for _ in range(octaves):
noise += amplitude * generate_perlin_noise_2d(shape, (frequency * res[0], frequency * res[1]))
frequency *= 2
amplitude *= persistence
return noise
def generate_periodic_fractal_noise_2d(amplitude, shape, res, octaves=1, persistence=0.5):
noise = generate_fractal_noise_2d(shape, res, octaves, persistence)
noise *= amplitude
return noise
if __name__ == '__main__':
import matplotlib.pyplot as plt
from hillshade import hill_shade
np.random.seed(62)
shape_text = 1024
res_text = 2**3
depth_text = 5
size_stamp = 0.25
noise = generate_periodic_fractal_noise_2d(30, (shape_text, shape_text), (res_text, res_text), depth_text, persistence=0.65)
noise = hill_shade(noise,terrain=noise * 10)
plt.figure()
plt.imshow(noise, cmap='cubehelix', interpolation='None', extent=(0, 0.25, 0, 0.25))
plt.colorbar()
plt.savefig("tmp/test_fract_noise_res1.pdf")
This should be it. Please try and see what comes out (changing the random seed will change the output, obviously). Cheers.
Hi! Thanks for making the code available, I'm trying to use it to generate a high-resolution texture/topography for a planetary science simulation. The idea would be to obtain something similar to this map (which was obtained by a double integration and filtering of a white noise grid)
I manage to obtain something quite similar using your code with res=1, octaves=8 and persistence=0.8 It's quite good, but it clearly shows some effects of the mirroring I used to make it periodic as the former one. I actually found an interesting answer about how to make a tileable (i.e., with periodic boundaries) fractal noise here (see the most voted answer by Boojum from 2012) but I'm struggling to see how to adapt it to your code. Would you say it's easily possible? Also, I'm having some trouble with your shape vs res vs octaves condition: I get the impressione that the program does not allow more than 10 octaves for a 1024 x 1024 shape. I also tried to use a res=1, octave=16 on a 256x256 tile, but I get a
message. What I'm I misunderstanding in your condition that shape = Nresoctaves? Thanks!