Faire un bruit fBm parfaitement mélangeable comme ceci se fait en deux temps. Tout d’abord, vous devez rendre la fonction de bruit Perlin elle-même exploitable. Voici un code Python pour une fonction de bruit Perlin simple qui fonctionne avec toutes les périodes allant jusqu'à 256 (vous pouvez l'étendre trivialement autant que vous le souhaitez en modifiant la première section):
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))
Le bruit de Perlin est généré à partir d'une somme de petits "surflets" qui sont le produit d'un gradient orienté de manière aléatoire et d'une fonction de réduction polynomiale séparable. Cela donne une région positive (jaune) et une région négative (bleue)
Les surflets ont une étendue de 2x2 et sont centrés sur les points du réseau entier, de sorte que la valeur du bruit Perlin à chaque point de l'espace est produite en faisant la somme des surplets aux coins de la cellule occupée.
Si vous faites en sorte que les directions du dégradé soient ajustées avec une certaine période, le bruit lui-même sera alors appliqué de manière transparente avec la même période. C'est pourquoi le code ci-dessus prend la coordonnée du réseau modulo la période avant de la hacher à travers la table de permutation.
L’autre étape est que, lors de la somme des octaves, vous souhaiterez redimensionner la période avec la fréquence de l’octave. Essentiellement, vous voudrez que chaque octave insère une seule fois l'image entière dans son intégralité, plutôt que plusieurs fois:
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
Mettez cela ensemble et vous obtenez quelque chose comme ça:
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")
Comme vous pouvez le constater, cela ne fait aucun doute:
Avec quelques petites retouches et un mappage de couleurs, voici une image de nuage en mosaïque 2x2:
J'espère que cela t'aides!