雜訊結合雜訊

April 3, 2022

視需求而定,雜訊與雜訊可以疊加,也可以基於某個雜訊來計算另一個雜訊,端看你想呈現什麼效果。

碎形布朗運動

來看一個簡單的〈Perlin 雜訊〉繪製,使用 dotSCAD 的 nz_perlin1 函式:

use <polyline_join.scad>
use <util/rand.scad>
use <noise/nz_perlin1.scad>

seed = rand(0, 255);

width = 10;
height = 2;
step = 0.05;

points = [
    for(i = [0:width / step]) 
    let(x = i * step)
    [x, height * nz_perlin1(x, seed)]
];

polyline_join(points)
    circle(.025);

很簡單的一個程式,這會繪製以下的曲線:

雜訊結合雜訊

如果將 Perlin 雜訊的取值範圍加倍,而振幅減半呢?

use <polyline_join.scad>
use <util/rand.scad>
use <noise/nz_perlin1.scad>

seed = rand(0, 255);

width = 10;
height = 2;
step = 0.05;

to = width / step;

points = [
    for(i = [0:width / step]) 
    let(x = i * step)
    [x, 0.5 * height * nz_perlin1(x * 2, seed)]
];

polyline_join(points)
    circle(.025);

這會繪製以下的圖形:

雜訊結合雜訊

若試著將以上兩個雜訊疊加起來:

use <polyline_join.scad>
use <util/rand.scad>
use <noise/nz_perlin1.scad>

seed = rand(0, 255);

width = 10;
height = 2;
step = 0.05;

to = width / step;

points = [
    for(i = [0:to]) 
    let(x = i * step)
    [x, height * nz_perlin1(x, seed) + 0.5 * height * nz_perlin1(x * 2, seed)]
];
    
polyline_join(points)
    circle(.025);

由於第二個曲線的振幅較低,主要曲線的形狀會是基於第一個曲線,然而增加了第二個曲線的變化:

雜訊結合雜訊

如果重複以上的規則多次,疊加起來的曲線就會更有變化性,更像是地形起伏,因為每次都是取值範圍加倍,而振幅減半,可以簡單地用遞迴實現:

use <polyline_join.scad>
use <util/rand.scad>
use <noise/nz_perlin1.scad>

function fbm(n, x, amplitude, seed, i = 0) = 
    i == n ? 0 : amplitude * nz_perlin1(x, seed) + fbm(n, x * 2, amplitude * 0.5, seed, i + 1);

seed = rand(0, 255);

width = 10;
height = 2;
step = 0.05;
n = 3;

points = [
    for(i = [0:width / step]) 
    let(x = i * step)
    [x, fbm(n, x, height, seed)]
];
    
polyline_join(points)
    circle(.025);

你疊加的次數越多,細小的變化就會越多,也就是會有更多的細節:

雜訊結合雜訊

由於具有隨機性,就像是〈布朗運動〉的粒子運動方式,然而最終得到的地形仍會呈現一定程度的自相似性,這就構成了布朗運動的擴展,也就是最後會得到符合〈碎形布朗運動(Fractional Brownian motion)〉定義的地形。

你可以將以上的 fbm 函式更通用化一些,例如可以指定每次取值範圍、振幅縮小的倍數,或者是將之套用在二維甚至三維的 Perlin 雜訊,看看能產生什麼樣的應用。

Worley + Perlin 雜訊

Worley 雜訊〉可以簡單地結合 dotSCAD 的 sf_thicken 函式,來建立一個曲面:

use <noise/nz_worley2.scad>
use <surface/sf_thicken.scad>

size = [30, 30];
grid_w = 15;
mesh_w = 0.2;
thickness = 0.5;
seed = 51;

point_size = size / mesh_w;

sf = [
    for(y = [0:point_size.y - 1]) 
    [
        for(x = [0:point_size.x - 1]) 
        let(
            px = x * mesh_w,
            py = y * mesh_w,
            nz = nz_worley2(px, py, seed, grid_w)[2]
        )
        [px, py, nz]
    ]
];

sf_thicken(sf, thickness);

為了讓曲面看來細緻一些,可以透過 mesh_w 來決定曲面的網格大小,這會繪製出以下的結果:

雜訊結合雜訊

來思考一下,Worley 雜訊其實是距離某個細胞核的距離,若將與某細胞核勢力範圍內,具有相同雜訊值的位置收集起來,會圍成一個圓,不同雜訊值就是圍成一圈又一圈的圓。

如果雜訊值為 nz,拿來作為 nz_perlin1 函式 x 參數值計算依據,例如 nz_perlin1(nz, seed)…這樣就可以取得一小段隨機然而連續曲線,如果雜訊值為 nz,拿來作為 nz_perlin2 函式 xy 參數的起點,例如 nz_perlin2(nz, nz, seed)…這樣就可以取得一小片隨機然而連續曲面:

use <noise/nz_worley2.scad>
use <noise/nz_perlin2.scad>
use <surface/sf_thicken.scad>

size = [30, 30];
grid_w = 15;
amplitude = 1;
mesh_w = 0.2;
wave_smoothness = 2;
thickness = 0.5;
dist = "euclidean"; // [euclidean, manhattan, chebyshev, border] 
seed = 51;

point_size = size / mesh_w;

sf = [
    for(y = [0:point_size.y - 1]) 
    [
        for(x = [0:point_size.x - 1]) 
        let(
            px = x * mesh_w,
            py = y * mesh_w,
            nz = nz_worley2(px, py, seed, grid_w, dist)[2],
            n = amplitude * nz_perlin2(nz / wave_smoothness, nz / wave_smoothness, seed)
        )
        [px, py, n]
    ]
];

sf_thicken(sf, thickness);

wave_smoothness 是由來決定取多大片的 Perlin 曲面,值越大,就會在相對小的範圍內取 Perlin 雜訊,出來的曲面變化就越小,也就越平滑,由於相同的雜訊值就是圍成一圈又一圈的圓,這一小片隨機然而連續的曲面,就會形成像是漣漪的曲面:

雜訊結合雜訊

你可以試著改變一下方才程式中的 dist,看看不同的雜訊會有什麼樣的效果;那麼,你有辦法實現〈Ripple vase〉嗎?

雜訊結合雜訊

分享到 LinkedIn 分享到 Facebook 分享到 Twitter