這次的基礎教學要介紹的是 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);
}
}
}
可以看到點和點之間的顏色值非常的不連貫,所以最後就變成了黑白雜訊值。
但如果對顏色值套入 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);
}
}
}
現在畫布變成了「平滑」的霧狀圖,這就是 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);
}
}
}
}
每次執行的函數圖都是不一樣的,但能確保相鄰的 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);
}
}
}
接下來讓 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);
}
}
}
變成動態的二維路面圖也可以:
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);
}
}
}
今天的教材借鑑一下 misvamda 大大之前的教學 Day9 - 體驗P5.js提供的幾種隨機模式 寫的程式,因為實在太適合放在這裡了,介紹了 noise 函數又結合了之前介紹的 vertex 函數,非常適合作為銜接點
現在我們要用 noise
函數搭配之前提到的 vertex
函數,實作新的動畫效果,整體看起來像是隨風飄揚的彩帶:
從彩帶的輪廓可以很明顯得看出 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();
}
其實繪製 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)
,但還是沒有根據深度調整顏色值,所以看起了沒有得到我們想要的效果。
所以我們還要根據 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();
}
}
最後再加上時間參數,就能夠實現飄逸的彩帶動畫了:
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();
}
}
https://ithelp.ithome.com.tw/articles/10296797