p5.js 基礎教學(九) –– 貝茲曲線 講完了貝茲曲線的原理以及函數使用方法,感受到了數個控制點就能定義曲線的神奇機制。
接下來我們試著利用移動的控制點來實現貝茲曲線的視覺藝術。
還記得在最開始的單元 用 p5.js 實現生成式藝術,我們示範了如何使用簡單的邏輯來呈現不錯的視覺效果:
function setup() {
createCanvas(windowWidth, windowHeight);
background(200);
}
function draw() {
background(200, 5);
rectMode(RADIUS);
noFill();
translate(width / 2, height / 2);
rotate(frameCount/200);
rect(0, 0, 39, 39);
rotate(-frameCount/200*2);
rect(0, 0, 56, 56);
rotate(frameCount/200*2);
rect(0, 0, 80, 80);
}
這個效果的原理就是利用 background
設定適當的透明度,然後藉由圖案移動的軌跡來生成視覺美感。
這次的實作也是基於類似的原理,這是最後完成後的效果:
我們想藉由移動控制點來控制貝茲曲線的變化,然後再藉由 background
函數的透明度來紀錄貝茲曲線的暫留軌跡。
使用的方式是讓每個控制點都各自圍繞一個圓心旋轉,所以要給予每個控制點三個特性:
而使用 p5.js 的 bezier
函數必須要有四個控制點,因此定義 center_list
、radius_list
、rotate_speed_list
分別填入四個元素,個別代表每個控制點的 圓心位置
、旋轉半徑
、旋轉速度
:
var center_list = [ // 圓心位置
[0, 0],
[40, 40],
[60, 60],
[80, 80]
];
var radius_list = [ // 旋轉半徑
50,
100,
100,
100
];
var rotate_speed_list = [ // 旋轉速度
1/60/2 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/3 * 2 * Math.PI
];
然後在 draw
函數算當下的 frame 每個控制點所在的位置放入 point_list
:
var center_list = [ // 圓心位置
[0, 0],
[40, 40],
[60, 60],
[80, 80]
];
var radius_list = [ // 旋轉半徑
50,
100,
100,
100
];
var rotate_speed_list = [ // 旋轉速度
1/60/2 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/3 * 2 * Math.PI
];
function setup() {
createCanvas(700, 700);
}
function draw() {
background(200, 20); // 設置透明度生成暫留軌跡
translate(width / 2, height / 2); // 座標零點移到中心點
var point_list = [];
for (var i = 0; i < 4; i++) {
point_list.push( // 計算每個控制點在當下的位置
[
center_list[i][0] + radius_list[i] * cos(frameCount * rotate_speed_list[i]),
center_list[i][1] + radius_list[i] * sin(frameCount * rotate_speed_list[i])
]
);
}
noFill();
bezier( // 繪製貝茲曲線
point_list[0][0], point_list[0][1],
point_list[1][0], point_list[1][1],
point_list[2][0], point_list[2][1],
point_list[3][0], point_list[3][1]
);
}
現在我們已經成功做出了一個隨週期反覆變化的貝茲曲線,接下來就是以這個貝茲曲線為基礎,做出其他鏡像的曲線。
通常一個隨機性動畫,加入不同角度的鏡像,就能呈現出一個具有美感和連動性的視覺藝術。
以剛剛的貝茲曲線為基礎,若加上了相反方向的鏡像:
若是加入五個不同方向的鏡像:
看起來是不是鏡像角度越多,就越有一種和諧的美感。
那這個要怎麼實現呢?我們可以只用之前介紹到的 rotate
函數,將整個座標軸旋轉一定度數再重新繪製一次貝茲曲線。
如果要做兩個方向的鏡像,需要每次轉 180 度,如果要轉 5 個方向的鏡像,則要每次轉 72 度。
發現了嗎?若需要 n 個方向的鏡像,那麼我每次就需要轉 360/n 度,若以弧度來算的話,就是 $\frac{2\pi}{n}$,因此我要呼叫 rotate(1/mirror_num * 2 * PI);
。
所以就有以下程式:
var center_list = [ // 圓心位置
[0, 0],
[40, 40],
[60, 60],
[80, 80]
];
var radius_list = [ // 旋轉半徑
50,
100,
100,
100
];
var rotate_speed_list = [ // 旋轉速度
1/60/2 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/3 * 2 * Math.PI
];
var mirror_num = 5; // 鏡像方向數
function setup() {
createCanvas(700, 700);
}
function draw() {
background(200, 20); // 設置透明度生成暫留軌跡
translate(width / 2, height / 2); // 座標零點移到中心點
var point_list = [];
for (var i = 0; i < 4; i++) {
point_list.push( // 計算每個控制點在當下的位置
[
center_list[i][0] + radius_list[i] * cos(frameCount * rotate_speed_list[i]),
center_list[i][1] + radius_list[i] * sin(frameCount * rotate_speed_list[i])
]
);
}
noFill();
for (var i = 0; i < mirror_num; i++) {
rotate(1/mirror_num * 2 * PI); // 每次旋轉一個鏡像角度
bezier( // 繪製貝茲曲線
point_list[0][0], point_list[0][1],
point_list[1][0], point_list[1][1],
point_list[2][0], point_list[2][1],
point_list[3][0], point_list[3][1]
);
}
}
最後我們可以再設立一個變數 all_rotate_speed
指定整個動畫的旋轉速度,讓作品有更多的變化性:
var center_list = [ // 圓心位置
[0, 0],
[40, 40],
[60, 60],
[80, 80]
];
var radius_list = [ // 旋轉半徑
50,
100,
100,
100
];
var rotate_speed_list = [ // 旋轉速度
1/60/2 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/4 * 2 * Math.PI,
1/60/3 * 2 * Math.PI
];
var mirror_num = 5; // 鏡像方向數
var all_rotate_speed = 1/60/8 * 2 * Math.PI; // 指定整個動畫旋轉速度
function setup() {
createCanvas(700, 700);
}
function draw() {
background(200, 20); // 設置透明度生成暫留軌跡
translate(width / 2, height / 2); // 座標零點移到中心點
var point_list = [];
for (var i = 0; i < 4; i++) {
point_list.push( // 計算每個控制點在當下的位置
[
center_list[i][0] + radius_list[i] * cos(frameCount * rotate_speed_list[i]),
center_list[i][1] + radius_list[i] * sin(frameCount * rotate_speed_list[i])
]
);
}
rotate(frameCount * all_rotate_speed); // 旋轉整個動畫
noFill();
for (var i = 0; i < mirror_num; i++) {
rotate(1/mirror_num * 2 * PI); // 每次旋轉一個鏡像角度
bezier( // 繪製貝茲曲線
point_list[0][0], point_list[0][1],
point_list[1][0], point_list[1][1],
point_list[2][0], point_list[2][1],
point_list[3][0], point_list[3][1]
);
}
}
作品網址:
https://openprocessing.org/sketch/2313685
這還是比較慢的版本,給他在快一點:
或這讓他反著轉也會有意料之外的變化:
比起之前的作品實作,這次的非常簡單,但也有非常大的變化性,讀者們可以試試看自己調整參數,一定會看到更多有趣的變化。