雜訊結合雜訊
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
函式 x
、y
參數的起點,例如 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〉嗎?