Es gibt zwei Teile, um fBm-Rauschen wie dieses nahtlos zu kacheln. Zunächst müssen Sie die Perlin-Rauschfunktion selbst kachelbar machen. Hier ist ein Python-Code für eine einfache Perlin-Rauschfunktion, die mit einer beliebigen Periode bis zu 256 arbeitet (Sie können sie beliebig erweitern, indem Sie den ersten Abschnitt ändern):
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))
Perlin-Rauschen wird aus einer Summe kleiner "Surflets" erzeugt, die das Produkt eines zufällig orientierten Gradienten und einer trennbaren Polynom-Falloff-Funktion sind. Dies ergibt einen positiven Bereich (gelb) und einen negativen Bereich (blau).
Die Surflets haben eine Ausdehnung von 2 × 2 und sind auf die ganzzahligen Gitterpunkte zentriert, sodass der Wert des Perlin-Rauschens an jedem Punkt im Raum durch Summieren der Surflets an den Ecken der Zelle, die sie einnehmen, erzeugt wird.
Wenn Sie die Verlaufsrichtungen mit einer bestimmten Periode umbrechen lassen, wird das Rauschen selbst nahtlos mit derselben Periode umbrochen. Aus diesem Grund nimmt der obige Code das Gitterkoordinatenmodul für die Periode, bevor es durch die Permutationstabelle gehasht wird.
Der andere Schritt ist, dass Sie beim Summieren der Oktaven die Periode mit der Frequenz der Oktave skalieren möchten. Im Wesentlichen soll jede Oktave das gesamte gerade Bild einmal und nicht mehrmals kacheln:
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
Füge das zusammen und du bekommst so etwas:
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")
Wie Sie sehen können, funktioniert dies in der Tat nahtlos:
Mit einigen kleinen Optimierungen und Farbzuordnungen ist hier ein Wolkenbild, das 2x2 gekachelt ist:
Hoffe das hilft!