Worley 雜訊

March 29, 2022

如果在指定一個點,每個像素至該點的距離作為灰階值計算來源,畫出來的圖會是越接近該點越黑,越遠離該點越白的圓形圖案。例如:

width = 100;

point = [width, width] / 2;
range = [0:width];
for(x = range, y = range) {
    p = [x, y];
    dist = norm(p - point);
    
    gray = dist / (width * 0.8); // 調整寬度,只是為了亮一點

    color([gray, gray, gray])
    translate(p)
        square(1);
}

這會產生以下的圖案:

Worley 雜訊

點的勢力範圍

如果有兩個點,每個像素至該兩點的距離取較小者,作為灰階值的計算來源,因為一個點會像是個圓形圖,兩個點的話:

width = 100;

p1 = [width, width] / 4;
p2 = p1 * 3;

range = [0:width];
for(x = range, y = range) {
    p = [x, y];
    dist1 = norm(p - p1);
    dist2 = norm(p - p2);
    
    gray = min(dist1, dist2) / (width * 0.8); // 調整寬度,只是為了亮一點

    color([gray, gray, gray])
    translate(p)
        square(1);
}

就會像是兩個互相擠壓的泡泡:

Worley 雜訊

如果有很多點呢?

width = 100;
n = 5;

cell_points = [for(i = [0:n - 1]) rands(0, width, 2)];

range = [0:width];
for(x = range, y = range) {
    p = [x, y];
    dists = [for(pt = cell_points) norm(p - pt)];
    
    gray = min(dists) / (width * 0.8); // 調整寬度,只是為了亮一點

    color([gray, gray, gray])
    translate(p)
        square(1);
}

這就會形成像是 Voronoi 圖案的結果:

Worley 雜訊

Voronoi 是勢力均衡下產生的圖案,就方才的範例而言,是以距離作為勢力衡量的依據,也就是範圍中每個座標比較靠近哪個點,就選擇該點勢力作為灰階值計算依據。

每個點就像是細胞核,如果作為細胞核的點是隨機產生,像素的勢力值也會是隨機,基本上就可以作為雜訊來源,只不過這隨機中有規則,才會產生像是 Voronoi 般的圖案,又稱為 Voronoi 雜訊 或 Worley 雜訊,因為它是 Steven Worley 在 1996 年引入的雜訊函式。

nz_cell 函式

dotSCAD 的 nz_cell,可以指定作為細胞核的隨機點,傳回特定點的 Worley 雜訊,例如:

use <noise/nz_cell.scad>;

width = 100;
n = 10;

cell_points = [for(i = [0:n - 1]) rands(0, width, 2)];

range = [0:width];
for(x = range, y = range) {
    p = [x, y];
    noise = nz_cell(cell_points, p);

    translate(p)
    linear_extrude(noise / 2)
        square(1);
}

這次得到的雜訊值,是作為擠出的高度,來看看產生了什麼:

Worley 雜訊

這有點像是凹陷的沙地,你也可以使用 width / 4 - noise / 2 作為擠出的高度,看來就像是泡泡擠在一起:

Worley 雜訊

Worley 雜訊的變化

Worley 雜訊不見得是要比較點與點間的直線距離,也可以是其他的距離計算方式,例如,nz_celldist 還可以使用 "manhattan" 指定〈曼哈頓距離〉、"chebyshev" 指定〈切比雪夫距離〉等,來看看指定了 "manhattan" 時的樣子:

use <noise/nz_cell.scad>;

width = 100;
n = 10;

cell_points = [for(i = [0:n - 1]) rands(0, width, 2)];

range = [0:width];
for(x = range, y = range) {
    p = [x, y];
    noise = nz_cell(cell_points, p, dist = "manhattan");
    gray = noise / (width * 0.75);
    
    color([gray, gray, gray])
    translate(p)
    linear_extrude(noise / 2)
        square(1);
}

繪出的模型做了些著色,這樣比較看得出來邊緣與高度等差異:

Worley 雜訊

如果作為細胞核的點並不是隨機,而是特別經過安排的呢?例如,位於〈黃金螺線〉上的點?

use <noise/nz_cell.scad>;
use <golden_spiral.scad>;

size = [100, 50];
half_size = size / 2;
pts_angles = golden_spiral(
    from = 3, 
    to = 10, 
    point_distance = 3
);

cell_points = [for(pt_angle = pts_angles) pt_angle[0] + half_size];

for(x = [0:size.x], y = [0:size.y]) {
    p = [x, y];
    noise = nz_cell(cell_points, p);
    gray = noise / size.y;
    
    color([gray, gray, gray])
    translate(p)
    linear_extrude(noise + 1)
        square(1);
}

這會形成漩渦的圖案:

Worley 雜訊

你可以進一步地增加螺線數量,結合〈曲線到曲面〉的 sf_thicken 函式,將雜訊值與座標結合為曲面需要的資料,就可以實現〈Voronoi & Fibonacci 2〉的模型:

Worley 雜訊

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