柏林之声?柏林噪声!

此柏林(Ken Perlin)非彼柏林(Berlin,the capital and largest city of Germany)……

柏林噪声(Perlin Noise)应该不用赘述了。

这里直接把核心代码放上来。


def lerp(a, b, t):
    return a + t * (b - a)

这是用来进行插值的lerp函数。不过请继续往下看。


def fade(t):
    return t * t * t * (t * (t * 6 - 15) + 10)

直接lerp函数进行线性插值的效果并不好,因此我们使用这个fade函数,它的变化两头快,中间慢。

顺便一说,在https://easings.net/可以查到很多缓动函数。(无关)


def grad(hash, x, y):
    #按位与运算取出hash的最后两位
    h = hash & 3

    #u和v基于h的值在x和y之间交换
    u = x if h < 2 else y
    v = y if h < 2 else x

    #判断,取和
    return (u if (h & 1) == 0 else -u) + (v if (h & 2) == 0 else -v)

计算梯度值。没什么好说的。


def perlin(x, y, p):

    #提取坐标整数部分并保证取值范围
    x0 = int(x) & 255
    y0 = int(y) & 255

    xi = x - int(x)
    yi = y - int(y)

    #计算插值需要的的平滑值
    u = fade(xi)
    v = fade(yi)

    #确定网格每个点的索引并计算梯度
    aa = p[x0] + y0
    ab = p[x0] + y0 + 1
    ba = p[x0 + 1] + y0
    bb = p[x0 + 1] + y0 + 1

    grad_aa = grad(p[aa], xi, yi)
    grad_ab = grad(p[ab], xi, yi - 1)
    grad_ba = grad(p[ba], xi - 1, yi)
    grad_bb = grad(p[bb], xi - 1, yi - 1)

    lerp_a = lerp(grad_aa, grad_ab, v)
    lerp_b = lerp(grad_ba, grad_bb, v)

    return lerp(lerp_a, lerp_b, u)

我们可以尝试将柏林噪声混入图像中。

验证码中瞎眼选项的来源?


回到一维情形,我们可以将柏林噪声与正弦函数混合

生成处处看似随机但是总体规律性起伏的数据。游戏中的山峦等随机生成就可以这么做(扩展维数即可)。加上“时间”这一维还可以生成海浪等。