Worley 雜訊(一)


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

如果有兩個點,每個像素至該兩點的距離取較小者,作為灰階值 0 ~ 255 的計算來源,因為一個點會像是個圓形圖,兩個點的結果,就會像是兩個互相擠壓的泡泡。例如:

如果有很多個點,而且這些點是隨機分佈在畫布中呢?結果就像是一堆泡泡擠在一起,形成了勢力均衡:

除了泡泡之外,你應該在一些動物身上看過這類圖案,像是長頸鹿、葉片脈絡、蜻蜓翅膀紋路等,這類圖案稱為 Voronoi 圖形,如果將其中的隨機點兩個連接為線,切分該線的邊緣一定平分該線,也就是每個點至邊緣一定等距,每個點擁有的勢力相同。

計算像素至點的距離,就是在計算點給予像素的影響力,若有兩個以上的點,有些像素會大致介於點與點之間勢力均衡的邊緣,藉由灰階帶來的視覺差異,就會帶來模糊的邊緣感,從而構成 Voronoi 圖形。

若點是隨機散佈,像素至最近點的距離,就像是一種雜訊、雜訊,可以將之抽取出來成為函式:

function worleyNoise(points, x, y) {
    let mDist = Infinity;
    for(let i = 0; i < points.length; i++) {
        const dist = p5.Vector.sub(points[i], createVector(x, y)).mag();
        mDist = min(mDist, dist);
    }
    return mDist;
}

可以用這個函式來重構一下方才的範例:

只不過,這圖感覺越邊緣越模糊?有沒有辦法讓每個點的勢力範圍是相同顏色呢?可以找出離像素最近的點:

function nearest(points, x, y) {
    let mDist = Infinity;
    let mPoint;
    for(let i = 0; i < points.length; i++) {
        const dist = p5.Vector.sub(points[i], createVector(x, y)).mag();  
        if(dist < mDist) {
            mDist = dist;
            mPoint = points[i];
        }
    }
    return mPoint;
}

反過來說,該點勢力範圍內的像素,透過 nearest 計算,都是傳回該點,接著就看你要怎麼用這個點來繪圖了,或許用該點與原點的距離作為亂數種子?

如果你的點不多,而且不均勻地分佈在畫布中,可以用這種方式來建立 Voronoi 圖形;不過若點非常的多,這種方式就會很慢,因為對於每個像素,每個點都要進行計算;若你想建立大量的勢力均衡,可以有另一種更快的方式,這就之後的文件再來談了。