如何使柏林噪声发生器更平滑?

2024-05-19 15:05:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用柏林噪声发生器来制作地图的瓷砖,但我注意到我的噪音太刺耳了,我的意思是,它有太多的海拔高度,没有平坦的地方,它们看起来不像山、岛屿、湖泊或任何东西;它们看起来太随机,有很多山峰。在

在问题的末尾,需要进行一些更改,以便解决问题。

问题对于代码来说很重要

1D:

def Noise(self, x):     # I wrote this noise function but it seems too random
    random.seed(x)
    number = random.random()
    if number < 0.5:
        final = 0 - number * 2
    elif number > 0.5:
        final = number * 2
    return final

 def Noise(self, x):     # I found this noise function on the internet
    x = (x<<13) ^ x
    return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0)

2D:

^{pr2}$

我留下了1D和2D柏林噪声的代码,因为可能有人对此感兴趣: (我花了很长时间才找到一些代码,所以我想有人会很乐意在这里找到一个示例)。
您不需要Matplotlib或NumPy来产生噪音;我只是使用它们来制作图形,以便更好地查看结果。在

import random
import matplotlib.pyplot as plt              # To make graphs
from mpl_toolkits.mplot3d import Axes3D      # To make 3D graphs
import numpy as np                           # To make graphs

class D():     # Base of classes D1 and D2
    def Cubic_Interpolate(self, v0, v1, v2, v3, x):
        P = (v3 - v2) - (v0 - v1)
        Q = (v0 - v1) - P
        R = v2 - v0
        S = v1
        return P * x**3 + Q * x**2 + R * x + S

class D1(D):
    def __init__(self, lenght, octaves):
        self.result = self.Perlin(lenght, octaves)

    def Noise(self, x):     # I wrote this noise function but it seems too random
        random.seed(x)
        number = random.random()
        if number < 0.5:
            final = 0 - number * 2
        elif number > 0.5:
            final = number * 2
        return final

    def Noise(self, x):     # I found this noise function on the internet
        x = (x<<13) ^ x
        return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0)

    def Perlin(self, lenght, octaves):
        result = []
        for x in range(lenght):
            value = 0
            for y in range(octaves):
                frequency = 2 ** y
                amplitude = 0.25 ** y            
                value += self.Interpolate_Noise(x * frequency) * amplitude
            result.append(value)
            print(f"{x} / {lenght} ({x/lenght*100:.2f}%): {round(x/lenght*10) * '#'} {(10-round(x/lenght*10)) * ' '}. Remaining {lenght-x}.")     # I don't use `os.system('cls')` because it slow down the code.
        return result

    def Smooth_Noise(self, x):
        return self.Noise(x) / 2 + self.Noise(x-1) / 4 + self.Noise(x+1) / 4

    def Interpolate_Noise(self, x):
        round_x = round(x)
        frac_x  = x - round_x
        v0 = self.Smooth_Noise(round_x - 1)
        v1 = self.Smooth_Noise(round_x)
        v2 = self.Smooth_Noise(round_x + 1)
        v3 = self.Smooth_Noise(round_x + 2)
        return self.Cubic_Interpolate(v0, v1, v2, v3, frac_x)

    def graph(self, *args):
        plt.plot(np.array(self.result), '-', label = "Line")
        for x in args:
            plt.axhline(y=x, color='r', linestyle='-')    
        plt.xlabel('X')
        plt.ylabel('Y')
        plt.title("Simple Plot")
        plt.legend()
        plt.show()

class D2(D):
    def __init__(self, lenght, octaves = 1):

        self.lenght_axes = round(lenght ** 0.5)
        self.lenght = self.lenght_axes ** 2

        self.result = self.Perlin(self.lenght, octaves)

    def Noise(self, x, y):     # I wrote this noise function but it seems too random
        n = x + y
        random.seed(n)
        number = random.random()
        if number < 0.5:
            final = 0 - number * 2
        elif number > 0.5:
            final = number * 2
        return final

    def Noise(self, x, y):     # I found this noise function on the internet
        n = x + y * 57
        n = (n<<13) ^ n
        return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0)

    def Smooth_Noise(self, x, y):
        corners = (self.Noise(x - 1, y - 1) + self.Noise(x + 1, y - 1) + self.Noise(x - 1, y + 1) + self.Noise(x + 1, y + 1) ) / 16
        sides   = (self.Noise(x - 1, y) + self.Noise(x + 1, y) + self.Noise(x, y - 1)  + self.Noise(x, y + 1) ) /  8
        center  =  self.Noise(x, y) / 4
        return corners + sides + center

    def Interpolate_Noise(self, x, y):

        round_x = round(x)
        frac_x  = x - round_x

        round_y = round(y)
        frac_y  = y - round_y

        v11 = self.Smooth_Noise(round_x - 1, round_y - 1)
        v12 = self.Smooth_Noise(round_x    , round_y - 1)
        v13 = self.Smooth_Noise(round_x + 1, round_y - 1)
        v14 = self.Smooth_Noise(round_x + 2, round_y - 1)
        i1 = self.Cubic_Interpolate(v11, v12, v13, v14, frac_x)

        v21 = self.Smooth_Noise(round_x - 1, round_y)
        v22 = self.Smooth_Noise(round_x    , round_y)
        v23 = self.Smooth_Noise(round_x + 1, round_y)
        v24 = self.Smooth_Noise(round_x + 2, round_y)
        i2 = self.Cubic_Interpolate(v21, v22, v23, v24, frac_x)

        v31 = self.Smooth_Noise(round_x - 1, round_y + 1)
        v32 = self.Smooth_Noise(round_x    , round_y + 1)
        v33 = self.Smooth_Noise(round_x + 1, round_y + 1)
        v34 = self.Smooth_Noise(round_x + 2, round_y + 1)
        i3 = self.Cubic_Interpolate(v31, v32, v33, v34, frac_x)

        v41 = self.Smooth_Noise(round_x - 1, round_y + 2)
        v42 = self.Smooth_Noise(round_x    , round_y + 2)
        v43 = self.Smooth_Noise(round_x + 1, round_y + 2)
        v44 = self.Smooth_Noise(round_x + 2, round_y + 2)
        i4 = self.Cubic_Interpolate(v41, v42, v43, v44, frac_x)

        return self.Cubic_Interpolate(i1, i2, i3, i4, frac_y)

    def Perlin(self, lenght, octaves):
        result = []
        for x in range(lenght):
            value = 0
            for y in range(octaves):
                frequency = 2 ** y
                amplitude = 0.25 ** y            
                value += self.Interpolate_Noise(x * frequency, x * frequency) * amplitude
            result.append(value)
            print(f"{x} / {lenght} ({x/lenght*100:.2f}%): {round(x/lenght*10) * '#'} {(10-round(x/lenght*10)) * ' '}. Remaining {lenght-x}.")     # I don't use `os.system('cls')` because it slow down the code.
        return result

    def graph(self, color = 'viridis'):
        # Other colors: https://matplotlib.org/examples/color/colormaps_reference.html
        fig = plt.figure()
        Z = np.array(self.result).reshape(self.lenght_axes, self.lenght_axes)

        ax = fig.add_subplot(1, 2, 1, projection='3d')
        X = np.arange(self.lenght_axes)
        Y = np.arange(self.lenght_axes)
        X, Y = np.meshgrid(X, Y)        
        d3 = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=color, linewidth=0, antialiased=False)
        fig.colorbar(d3)

        ax = fig.add_subplot(1, 2, 2)
        d2 = ax.imshow(Z, cmap=color, interpolation='none')
        fig.colorbar(d2)

        plt.show()

问题是输出似乎不适合映射。在

使用以下方法查看此输出:

test = D2(1000, 3)
test.graph()

enter image description here

我在找更光滑的。在

也许在2D噪音中很难注意到我所说的,但在1D中则容易得多:

test = D1(1000, 3)
test.graph()

enter image description here

来自互联网的噪声函数的峰值稍小且频率较低,但仍有太多的峰值。我在找更光滑的。在

可能是这样:
enter image description here

或者这样:
enter image description here

p.S:我是根据this pseudocode制作的。在

编辑:

皮卡莱克:

enter image description here

即使值较低,也有峰值,没有曲线或平滑/平坦的线条。在

geza:解决方案

多亏了geza's suggestions我找到了解决问题的方法:

def Perlin(self, lenght_axes, octaves, zoom = 0.01, amplitude_base = 0.5):
    result = []

    for y in range(lenght_axes):
        line = []
        for x in range(lenght_axes):
            value = 0
            for o in range(octaves):
                frequency = 2 ** o
                amplitude = amplitude_base ** o
                value += self.Interpolate_Noise(x * frequency * zoom, y * frequency * zoom) * amplitude
            line.append(value)
        result.append(line)
        print(f"{y} / {lenght_axes} ({y/lenght_axes*100:.2f}%): {round(y/lenght_axes*20) * '#'} {(20-round(y/lenght_axes*20)) * ' '}. Remaining {lenght_axes-y}.")
    return result

其他修改包括:

  • Z = np.array(self.result)而不是graph函数中的这个{}。在
  • round_xround_y变量中使用math.floor()(记住import math)代替Interpolate_Noise函数中的round()。在
  • Noise(第二个)中的return行修改为return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0)D2(10000, 10)enter image description here 现在唯一奇怪的是山(黄色)总是靠近同一个地方,但我认为这是改变Noise函数中的数字的问题。在

Tags: selfnumberreturndefpltrandomresultfinal
3条回答

我在你的代码中发现了这些错误:

  • 您需要乘以Interpolate_Noise参数来“缩放”地图(例如,将x与{}相乘)。如果这个函数已经生成了,你会发现
  • 将倍频程计数从3增加到更大(3个倍频程不会产生太多细节)
  • 使用振幅0.5^倍频程,而不是0.25^倍频程(但您可以使用此参数,因此0.25不是固有的差,但它不会提供太多细节)
  • 对于二维情况,需要有两个外部回路(一个用于水平,一个用于垂直)。当然,你还需要有倍频程循环)。因此,您需要使用水平和垂直位置正确地“索引”噪声,而不仅仅是x和{}。在
  • 完全去除平滑。柏林噪音不需要它。在
  • 2D noise函数有一个错误:它在返回表达式中使用x而不是{}
  • 在三次插值中,使用round,而不是math.floor。在

这是我的答案,用简单的(C++)实现了类PrLin(它不是适当的Pelin)噪声:https://stackoverflow.com/a/45121786/8157187

您可以使用简单算法使噪波更平滑->;f(n)=(f(n-1) + f(n+1))/2

不知道为什么,但它起作用了

smoother noise

你需要实现一个更积极的平滑算法。最好的方法是使用矩阵卷积。这样做的方式是,你有一个矩阵,我们称之为“内核”,应用于网格中的每个单元格,创建一个新的、经过转换的数据集。例如,内核可以是:

0.1 0.1 0.1
0.1 0.2 0.1
0.1 0.1 0.1

假设你有这样的网格:

^{pr2}$

假设我们要将内核应用于最中间的2,我们将把网格剪成内核的形状,并将每个单元与相应的内核单元相乘:

. . . . .
. 5 1 2 .       0.1 0.1 0.1       0.5 0.1 0.2
. 9 2 1 .   x   0.1 0.2 0.1   =   0.9 0.4 0.1
. 4 9 5 .       0.1 0.1 0.1       0.4 0.9 0.5
. . . . .

然后我们可以对所有这些值求和,得到单元格的新值0.5+0.1+0.2+0.9+0.4+0.1+0.4+0.9+0.5 = 4,然后在新数据集上填充该空间:

? ? ? ? ?
? ? ? ? ?
? ? 4 ? ?
? ? ? ? ?
? ? ? ? ?

。。。正如您可以想象的那样,我们必须对网格中的其他空间重复此操作,以便填充新的数据集。一旦完成了这项工作,我们就扔掉旧数据,使用这个新的网格作为我们的数据集。在

这样做的好处是可以使用大量的内核来执行非常大的平滑操作。例如,您可以使用5x5或9x9大小的内核,这将使您的噪声更加平滑。在

还有一点需要注意的是,内核的构建必须使其所有单元的总和为1,否则就不会有质量守恒(可以说,如果总和大于等于1,则峰值会越来越高,数据的平均值也会更高)。5x5矩阵的一个例子是:

0.010 0.024 0.050 0.024 0.010
0.024 0.050 0.062 0.050 0.024
0.050 0.062 0.120 0.062 0.050
0.024 0.050 0.062 0.050 0.024
0.010 0.024 0.050 0.024 0.010

确保这种质量的一种方法是简单地对矩阵进行规范化;将每个单元除以所有单元的总和。E、 g.:

1  4  16 4  1                    0.002808989    0.011235955 0.04494382  0.011235955 0.002808989
4  16 32 16 4                    0.011235955    0.04494382  0.08988764  0.04494382  0.011235955
16 32 64 32 16  (sum = 356)  >  0.04494382     0.08988764  0.179775281 0.08988764  0.04494382
4  16 32 16 4                    0.011235955    0.04494382  0.08988764  0.04494382  0.011235955
1  4  16 4  1                    0.002808989    0.011235955 0.04494382  0.011235955 0.002808989

相关问题 更多 >