iT邦幫忙

2024 iThome 鐵人賽

DAY 16
0
Modern Web

p5.js 的環形藝術系列 第 16

[Day16] p5.js 基礎教學(八) –– noise 函數

  • 分享至 

  • xImage
  •  

這次的基礎教學要介紹的是 noise 函數,和 random 函數功能相似,都是輸出一個隨機值,但和 random 不同的是,noise 可以輸出「平滑」的隨機值。

為什麼稱 noise 可以輸出「平滑」的隨機值?因為重複呼叫 random,得到的隨機值是彼此獨立的,也就是說他會在 0~1 之間亂跳:

function setup() {
  createCanvas(500, 500);

  for (let x = 0; x < 500; x += 1) {
    for (let y = 0; y < 500; y += 1) {
      stroke(random()*255);
      point(x, y);
    }
  }
}

Imgur

可以看到點和點之間的顏色值非常的不連貫,所以最後就變成了黑白雜訊值。

但如果對顏色值套入 noise 函數,我們就會得到截然不同的效果:

function setup() {
  createCanvas(500, 500);

  for (let x = 0; x < 500; x += 1) {
    for (let y = 0; y < 500; y += 1) {
      stroke(noise(x*0.01, y*0.01)*255);
      point(x, y);
    }
  }
}

Imgur

現在畫布變成了「平滑」的霧狀圖,這就是 noise 函數的功用。

語法解說

noise([x], [y], [z])

noise 函數可以接收 1~3 個參數,用比較不精確的自然語言表述,我會把 noise 函數形容為一個顛簸的路面,但這個顛簸的路面可以是一維、二維或三維,看你要接收一個參數,兩個參數或三個參數。

現在我們試著模擬出一維的顛簸路面,這裡使用到的是接收一個參數的 noise 函數:

function setup() {
    createCanvas(500, 500);

    for (let x = 0; x < 500; x += 1) {
        let h = noise(x * 0.01) * 500;
        for (let y = 0; y < 500; y += 1) {
            if ((500 - y) < h) {
                stroke(0);
                point(x, y);
            } else {
                stroke(255);
                point(x, y);
            }
        }
    }
}

Imgur

每次執行的函數圖都是不一樣的,但能確保相鄰的 x 座標,其對應的高度值都是平滑的。

假設將 noise 函數的輸出反應在灰度值上,那我們可以得到一個光帶圖:

function setup() {
  createCanvas(500, 500);

  for (let x = 0; x < 500; x += 1) {
    for (let y = 0; y < 500; y += 1) {
      stroke(noise(x*0.01)*255);
      point(x, y);
    }
  }
}

Imgur

接下來讓 noise 再接收另一個參數,但這個參數並不是另一個空間維度,而是引入一個時間軸,讓光帶圖變成光帶動畫:

function setup() {
    createCanvas(100, 100);
    // 因為每次要執行 500 * 500 次 point() 太花時間
    // 所以把畫布改成 100 * 100
}

function draw() {
    for (let x = 0; x < 100; x += 1) {
        let h = noise(x*0.01, frameCount*0.02)*255;
        for (let y = 0; y < 100; y += 1) {
            stroke(h);
            point(x, y);
        }
    }
}

Imgur

變成動態的二維路面圖也可以:

function setup() {
    createCanvas(100, 100);
    // 因為每次要執行 500 * 500 次 point() 太花時間
    // 所以把畫布改成 100 * 100
}

function draw() {
    for (let x = 0; x < 100; x += 1) {
        for (let y = 0; y < 100; y += 1) {
            let h = noise(x*0.01, y*0.01, frameCount*0.02)*255;
            stroke(h);
            point(x, y);
        }
    }
}

Imgur

彩帶動畫實作

今天的教材借鑑一下 misvamda 大大之前的教學 Day9 - 體驗P5.js提供的幾種隨機模式 寫的程式,因為實在太適合放在這裡了,介紹了 noise 函數又結合了之前介紹的 vertex 函數,非常適合作為銜接點

現在我們要用 noise 函數搭配之前提到的 vertex 函數,實作新的動畫效果,整體看起來像是隨風飄揚的彩帶:

Imgur

從彩帶的輪廓可以很明顯得看出 noise 函數的影子,這其實和二維路面動畫是很類似的概念,用表格來歸納一下他們之間的差異:

動畫類型 位置座標 noise 數值
彩帶 x 座標和深度值 y 座標
二維路面 x, y 座標 灰度值

另外彩帶的輪廓可以用 vertex 做出 noise 曲線勾勒出來,越近的曲線給予較高的灰度值,也就是越偏白色。

反之越遠,也就是越深的曲線給予越低的灰度值,也就是越偏黑色,藉由顏色的深淺反應視覺上的距離感。

現在的問題是如何用 vertex 做出一個 noise 曲線:

function setup() {
    createCanvas(600, 600);
    background(0);

    noFill();
    strokeWeight(2);
    stroke(255);

    beginShape();
    for (let x = 0; x <= 600; x += 20) {
        let n = float(noise(x * 0.001));
        let y = float(600 * n);
        vertex(x, y);
    }
    endShape();
}

Imgur

其實繪製 noise 曲線很簡單,只要用 vertex 對每 20 pixel 的 x 座標進行頂點標記,y 座標則套用 noise(x * 0.001) 就可以了。

然後下一步就是繪製多條 noise 曲線,建構出彩帶的視覺感:

function setup() {
    createCanvas(600, 600);
    background(0);

    noFill();
    strokeWeight(2);
    stroke(255);

    for (let i = 0; i < 50; i++) {
        beginShape();
        for (let x = 0; x <= 600; x += 20) {
            let n = float(noise(x * 0.001, i * 0.001));
            let y = float(600 * n);
            vertex(x, y);
        }
        endShape();
    }
}

我們畫了 30 條 noise 曲線,每一條代表其中一個深度值,這樣就能滿足上面表格說的,用 x 座標和深度來作為 noise 函數的位置座標,所以我們呼叫 noise(x * 0.001, i * 0.01),但還是沒有根據深度調整顏色值,所以看起了沒有得到我們想要的效果。

Imgur

所以我們還要根據 i 值來調整顏色值,藉由顏色的深淺來呈現深度感:

function setup() {
    createCanvas(600, 600);
    background(0);

    noFill();
    strokeWeight(2);

    for (let i = 0; i < 50; i++) {
        stroke(255 * i / 50);
        beginShape();
        for (let x = 0; x <= 600; x += 20) {
            let n = float(noise(x * 0.001, i * 0.01));
            let y = float(600 * n);
            vertex(x, y);
        }
        endShape();
    }
}

Imgur

最後再加上時間參數,就能夠實現飄逸的彩帶動畫了:

function setup() {
    createCanvas(600, 600);
    background(0);
}

function draw() {
    background(0);
    noFill();
    strokeWeight(2);

    for (let i = 0; i < 50; i++) {
        stroke(255 * i / 50);
        beginShape();
        for (let x = 0; x <= 600; x += 20) {
            let n = float(noise(x * 0.001, i * 0.01, frameCount * 0.01));
            let y = float(600 * n);
            vertex(x, y);
        }
        endShape();
    }
}

Imgur

參考資料

https://ithelp.ithome.com.tw/articles/10296797


上一篇
[Day 15] p5.js 實戰演練(五) –– 結晶動畫實作(二)
下一篇
[Day 17] p5.js 實戰演練(六) –– 煙霧動畫實作(一)
系列文
p5.js 的環形藝術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言