Есть две части, чтобы создать плавный шум fBm, подобный этому. Во-первых, вам нужно сделать функцию шума Perlin самой. Вот некоторый код Python для простой функции шума Perlin, которая работает с любым периодом до 256 (вы можете тривиально расширить его, сколько захотите, изменив первый раздел):
import random
import math
from PIL import Image
perm = range(256)
random.shuffle(perm)
perm += perm
dirs = [(math.cos(a * 2.0 * math.pi / 256),
math.sin(a * 2.0 * math.pi / 256))
for a in range(256)]
def noise(x, y, per):
def surflet(gridX, gridY):
distX, distY = abs(x-gridX), abs(y-gridY)
polyX = 1 - 6*distX**5 + 15*distX**4 - 10*distX**3
polyY = 1 - 6*distY**5 + 15*distY**4 - 10*distY**3
hashed = perm[perm[int(gridX)%per] + int(gridY)%per]
grad = (x-gridX)*dirs[hashed][0] + (y-gridY)*dirs[hashed][1]
return polyX * polyY * grad
intX, intY = int(x), int(y)
return (surflet(intX+0, intY+0) + surflet(intX+1, intY+0) +
surflet(intX+0, intY+1) + surflet(intX+1, intY+1))
Шум Перлина генерируется суммированием маленьких «серфлетов», которые являются произведением случайно ориентированного градиента и разделяемой полиномиальной функции спада. Это дает положительный регион (желтый) и отрицательный регион (синий)
Сурфлеты имеют размер 2х2 и центрированы на целочисленных точках решетки, поэтому значение шума Перлина в каждой точке пространства получается путем суммирования сурфлетов в углах ячейки, которую он занимает.
Если вы сделаете направление градиента обтеканием с некоторым периодом, то сам шум будет незаметно обернутым с тем же периодом. Вот почему приведенный выше код принимает координату решетки по модулю периода, прежде чем хешировать его через таблицу перестановок.
Другим шагом является то, что при суммировании октав вы хотите масштабировать период с частотой октавы. По сути, вы хотите, чтобы каждая октава отображала все изображение только один раз, а не несколько раз:
def fBm(x, y, per, octs):
val = 0
for o in range(octs):
val += 0.5**o * noise(x*2**o, y*2**o, per*2**o)
return val
Положите это вместе, и вы получите что-то вроде этого:
size, freq, octs, data = 128, 1/32.0, 5, []
for y in range(size):
for x in range(size):
data.append(fBm(x*freq, y*freq, int(size*freq), octs))
im = Image.new("L", (size, size))
im.putdata(data, 128, 128)
im.save("noise.png")
Как вы можете видеть, это действительно плавно:
С небольшим изменением цвета и отображением цветов, вот изображение облака размером 2x2:
Надеюсь это поможет!