用 Perlin 噪声扭曲 Voronoi

Distorting Voronoi With Perlin Noise

根据 this blog post,有几种方法可以使 voronoi 细胞看起来更动态。我感兴趣的是他们提到的第一个:

The above illustration is the same Voronoi diagram as the one above, only now, Perlin noise has been used to distort which points belong to which cell. This creates some more interesting borders between between cells. This is kind of easy to accomplish as long as you use a pixel-by-pixel (or tile-by-tile) assignment to the closest voronoi origin, since you can quite simply offset the actual coordinates of the pixel by Perlin noise - quite closely related to distorted Perlin noise.

我在其他地方看到过类似的想法,但没有显示柏林噪声如何“扭曲”或“添加”到 voronoi 图的实际代码。我试过通过猜测来应用它,但没有运气。我的代码是这样写的,点与点之间的距离是几百,而 perlin 噪声值只有 0 到 1,所以加减噪声真的没什么用。乘法似乎打破了 voronoi。我试过将 voronoi 距离值缩放到 0 到 1 或 -1 到 1 之间,然后应用于噪声,但这也不起作用。

下面是我生成的 voronoi 图和柏林噪声的示例。如果有任何反馈或能够为我指明正确的方向,我将不胜感激。

from PIL import Image
import random
import math
import numpy as np
import noise
wid = 500
hei = 250
image = Image.new("RGB",(wid,hei))
world_test = np.zeros(image.size)
scale       = 100 # Number that determines at what distance to view the noisemap
octaves     = 6 # the number of levels of detail you want you perlin noise to have
persistence = 0.5 # number that determines how much detail is added or removed at each octave (adjusts frequency)
lacunarity  = 2.0 # number that determines how much each octave contributes to the overall shape (adjusts amplitude)
# Creates perlin noise
for x in range(wid):
    for y in range(hei):
        world_test[x][y] = ((noise.pnoise2(x/100, 
                                    y/100, 
                                    octaves     = octaves, 
                                    persistence = persistence, 
                                    lacunarity  = lacunarity, 
                                    repeatx     = wid, 
                                    repeaty     = hei, 
                                    base        = 0)))
def generate_voronoi_diagram(width, height, num_cells):
    image = Image.new("RGB", (width, height))
    putpixel = image.putpixel
    imgx, imgy = image.size
    nx = []
    ny = []
    nr = []
    ng = []
    nb = []
    #Go through number of cells
    for i in range(num_cells):
        #creat a point (x,y) and give it a specific color value
        nx.append(random.randrange(imgx))
        ny.append(random.randrange(imgy))
        nr.append(random.randrange(256))
        ng.append(random.randrange(256))
        nb.append(random.randrange(256))
    #go through each pixel in the image
    for y in range(int(imgy)):
        for x in range(int(imgx)):
            dmin = math.hypot(imgx-1, imgy-1)
            j = -1
            #go through each cell
            for i in range(num_cells):
                # d is distance from each voronoi starting point
                d = math.hypot((nx[i]-x), (ny[i]-y))
                # apply perlin distort to d
                d +=  world_test[x][y]
                #if distance is less than the current min distance,
                #set that point as the owner of this pixel and the new dmin
                if d < dmin:
                    dmin = d
                    j = i
            putpixel((x, y), (nr[j], ng[j], nb[j]))
    image.save("Voronoi_example.png", "PNG")
    image.show()
generate_voronoi_diagram(wid, hei, 30)

我想我找到了解决这个问题的方法。 如果您为每个坐标(x 和 y)创建单独的噪声贴图,并在计算 d 的过程中将该噪声贴图的值添加到每个像素,则该值将被贴图扭曲。 我想我在这个网站上找到了这个答案,但我不确定: https://gamedev.stackexchange.com/questions/182582/how-to-distort-an-image-using-perlin-noise

基于此站点,我修改了您的 python 代码,现在它应该可以工作了。

from PIL import Image
import random
import math
import numpy as np
import noise
wid = 100
hei = 100
image = Image.new("RGB",(wid,hei))
world_test_x = np.zeros(image.size)
world_test_y = np.zeros(image.size)
scale       = 0.1 # Number that determines at what distance to view the noisemap
octaves     = 6 # the number of levels of detail you want you perlin noise to have
persistence = 0.5 # number that determines how much detail is added or removed at each octave (adjusts frequency)
lacunarity  = 2.0 # number that determines how much each octave contributes to the overall shape (adjusts amplitude)
seed = 19829813472
mult = 50 # Strenght
# Creates perlin noise to distort x coordinates
for x in range(wid):
    for y in range(hei):
        world_test_x[x][y] = ((noise.pnoise2(x/100, 
                                    y/100, 
                                    octaves     = octaves, 
                                    persistence = persistence, 
                                    lacunarity  = lacunarity, 
                                    repeatx     = wid, 
                                    repeaty     = hei, 
                                    base        = 0)))*mult
# Creates perlin noise to distort y coordinates
for x in range(wid):
    for y in range(hei):
        world_test_y[x][y] = ((noise.pnoise2((x+seed)/100, 
                                    (y+seed)/100, 
                                    octaves     = octaves, 
                                    persistence = persistence, 
                                    lacunarity  = lacunarity, 
                                    repeatx     = wid, 
                                    repeaty     = hei, 
                                    base        = 0)))*mult

def generate_voronoi_diagram(width, height, num_cells):
    image = Image.new("RGB", (width, height))
    putpixel = image.putpixel
    imgx, imgy = image.size
    nx = []
    ny = []
    nr = []
    ng = []
    nb = []
    nsize=[]
    #Go through number of cells
    for i in range(num_cells):
        #creat a point (x,y) and give it a specific color value
        nx.append(random.randrange(imgx))
        ny.append(random.randrange(imgy))
        nr.append(random.randrange(256))
        ng.append(random.randrange(256))
        nb.append(random.randrange(256))
        nsize.append(0)
    #go through each pixel in the image
    for y in range(int(imgy)):
        for x in range(int(imgx)):
            dmin = math.hypot(imgx-1, imgy-1)
            j = -1
            #go through each cell
            for i in range(num_cells):
                # d is distance from each voronoi starting point
                # each point gets its coordinates distorted so d also gets distorted
                d = math.hypot((nx[i]-x+world_test_x[x][y]), (ny[i]-y+world_test_y[x][y]))
                #if distance is less than the current min distance,
                #set that point as the owner of this pixel and the new dmin
                if d < dmin:
                    dmin = d
                    j = i
                nsize[j]+=1
            putpixel((x, y), (nr[j], ng[j], nb[j]))
    image.save("Voronoi_example.png", "PNG")
    image.show()
    print(nsize)
    print(nr)
generate_voronoi_diagram(wid, hei, 8)

Here is the Voronoi map

Here is the distorted Voronoi map