前言

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); }

Imgur

這個效果的原理就是利用 background 設定適當的透明度,然後藉由圖案移動的軌跡來生成視覺美感。

這次的實作也是基於類似的原理,這是最後完成後的效果:

Imgur

移動貝茲曲線控制點

我們想藉由移動控制點來控制貝茲曲線的變化,然後再藉由 background 函數的透明度來紀錄貝茲曲線的暫留軌跡。

使用的方式是讓每個控制點都各自圍繞一個圓心旋轉,所以要給予每個控制點三個特性:

  • 圓心位置
  • 旋轉半徑
  • 旋轉速度

而使用 p5.js 的 bezier 函數必須要有四個控制點,因此定義 center_listradius_listrotate_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] ); }

Imgur

現在我們已經成功做出了一個隨週期反覆變化的貝茲曲線,接下來就是以這個貝茲曲線為基礎,做出其他鏡像的曲線。

繪製鏡像曲線

通常一個隨機性動畫,加入不同角度的鏡像,就能呈現出一個具有美感和連動性的視覺藝術。

以剛剛的貝茲曲線為基礎,若加上了相反方向的鏡像:

Imgur

若是加入五個不同方向的鏡像:

Imgur

看起來是不是鏡像角度越多,就越有一種和諧的美感。

那這個要怎麼實現呢?我們可以只用之前介紹到的 rotate 函數,將整個座標軸旋轉一定度數再重新繪製一次貝茲曲線。

如果要做兩個方向的鏡像,需要每次轉 180 度,如果要轉 5 個方向的鏡像,則要每次轉 72 度。

發現了嗎?若需要 n 個方向的鏡像,那麼我每次就需要轉 360/n 度,若以弧度來算的話,就是 $\frac{2\pi}{n}$,因此我要呼叫 rotate(1/mirror_num <em> 2 </em> 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

Imgur

這還是比較慢的版本,給他在快一點:

Imgur

或這讓他反著轉也會有意料之外的變化:

Imgur

比起之前的作品實作,這次的非常簡單,但也有非常大的變化性,讀者們可以試試看自己調整參數,一定會看到更多有趣的變化。