iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
Modern Web

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

[Day 21] p5.js 創作應用(九) –– 貝茲曲線隨機動畫

  • 分享至 

  • xImage
  •  

前言

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 * 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

Imgur

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

Imgur

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

Imgur

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


上一篇
[Day 20]p5.js 基礎教學(九) –– 貝茲曲線
下一篇
[Day 22] glsl 基礎教學(一) –– glsl 和 p5.js 的差異
系列文
p5.js 的環形藝術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言