接續上一個單元 p5.js 實戰演練(四) –– 結晶動畫實作(一) 的實作,我們現在想讓這個動畫維持「環狀」的輪廓,也就是說要調整變數 d 的隨機取值範圍,d 若太高,則整個圓形都會被填滿,d 若太低,環狀輪廓會顯得太細薄。

經過自己測試之後,我覺得將 point_num 調整為 39,將 d 的隨機調整為 3~7,會是我比較滿意的結果:

function setup() {
    createCanvas(windowWidth, windowHeight);
    stroke(0);
}

function my_random(x) { // 將 sin 函數的頻率和震幅拉大,再取其正小數部分 return fract(sin(x425.121)437.53123); }

function draw() { background(255, 30); translate(width/2, height/2); var point_num = 39; // 將 point_num 設為質數 var radius = 150; var random_step_range = [3, 7]; // 限制 d 的隨機化範圍 colorMode(HSB, 360, 100, 100, 100); stroke(90, 30, 80); fill(90, 30, 80, 10); // 將透明度設為 10,讓顏色有覆蓋疊加的效果 colorMode(RGB); var rotate_speed = 1/60/5 2 PI; beginShape(TRIANGLES); var random_seed = int(frameCount/12); // 每過 12 幀數字才會變化 var j = int(my_random(random_seed) * point_num); // 將 j 隨機化 var d = int( my_random(random_seed) * (random_step_range[1]-random_step_range[0]) + random_step_range[0] ); // 將 d 隨機範圍限制在 random_step_range 裡

for (let i = 0; i < point_num; i++) { // i 變成單純計數的變數 j += d; j %= point_num; vertex( radius cos(j 2 * PI / point_num), radius sin(j 2 * PI / point_num) ); }

endShape(); }

Imgur

然後我們再加一點旋轉,讓這個「環」再加入一些動態美感:

function setup() {
    createCanvas(windowWidth, windowHeight);
    stroke(0);
}

function my_random(x) { // 將 sin 函數的頻率和震幅拉大,再取其正小數部分 return fract(sin(x425.121)437.53123); }

function draw() { background(255, 30); translate(width/2, height/2); var point_num = 39; // 將 point_num 設為質數 var radius = 150; var random_step_range = [3, 7]; // 限制 d 的隨機化範圍 colorMode(HSB, 360, 100, 100, 100); stroke(90, 30, 80); fill(90, 30, 80, 10); // 將透明度設為 10,讓顏色有覆蓋疊加的效果 colorMode(RGB); var rotate_speed = 1/60/5 2 PI; // 該變數定義旋轉速度 push(); // 針對 rotate 做出的座標系統轉換要記得用 push pop 校正回來 rotate(frameCount * rotate_speed); beginShape(TRIANGLES); var random_seed = int(frameCount/12); // 每過 12 幀數字才會變化 var j = int(my_random(random_seed) * point_num); // 將 j 隨機化 var d = int( my_random(random_seed) * (random_step_range[1]-random_step_range[0]) + random_step_range[0] ); // 將 d 隨機範圍限制在 random_step_range 裡

for (let i = 0; i < point_num; i++) { // i 變成單純計數的變數 j += d; j %= point_num; vertex( radius cos(j 2 * PI / point_num), radius sin(j 2 * PI / point_num) ); }

endShape(); pop(); }

Imgur

最後我覺得這個環狀輪廓還是太滿了,所以我最後打算只標記一半的頂點 point_num/2

function setup() {
    createCanvas(windowWidth, windowHeight);
    stroke(0);
}

function my_random(x) { // 將 sin 函數的頻率和震幅拉大,再取其正小數部分 return fract(sin(x425.121)437.53123); }

function draw() { background(255, 30); translate(width/2, height/2); var point_num = 39; // 將 point_num 設為質數 var radius = 150; var random_step_range = [3, 7]; // 限制 d 的隨機化範圍 colorMode(HSB, 360, 100, 100, 100); stroke(90, 30, 80); fill(90, 30, 80, 10); // 將透明度設為 10,讓顏色有覆蓋疊加的效果 colorMode(RGB); var rotate_speed = 1/60/5 2 PI; // 該變數定義旋轉速度 push(); // 針對 rotate 做出的座標系統轉換要記得用 push pop 校正回來 rotate(frameCount * rotate_speed); beginShape(TRIANGLES); var random_seed = int(frameCount/12); // 每過 12 幀數字才會變化 var j = int(my_random(random_seed) * point_num); // 將 j 隨機化 var d = int( my_random(random_seed) * (random_step_range[1]-random_step_range[0]) + random_step_range[0] ); // 將 d 隨機範圍限制在 random_step_range 裡

for (let i = 0; i < point_num/2; i++) { // 只標記一半的頂點 // i 變成單純計數的變數 j += d; j %= point_num; vertex( radius cos(j 2 * PI / point_num), radius sin(j 2 * PI / point_num) ); }

endShape(); pop(); }

Imgur

大功告成!現在我們嘗試把這個環狀結晶動畫,包裝成一個函數 ring_crystallization,作為一個可以不停疊加在動畫上面的組件。

首先我們要釐清這個動畫可以把多少變數抽出來作為這個函數的參數:

  • 頂點個數(變數point_num
  • 色相(strokefill 的輸入參數)
  • 線段的連接方式(beginShape 的輸入參數)
  • 環狀結晶半徑(變數 radius
  • 旋轉速度(變數 rotate_speed
  • 變數 d 的隨機範圍(變數 random_step_range

以下是函數包裝結果:

function setup() {
    createCanvas(windowWidth, windowHeight);
    stroke(0);
}

function my_random(x) { return fract(sin(x425.121)437.53123); }

function ring_crystallization( point_num, hue, shape_mode, radius, rotate_speed, random_step_range ) { colorMode(HSB, 360, 100, 100, 100); stroke(hue, 30, 80); fill(hue, 30, 80, 10); colorMode(RGB); push(); rotate(frameCount * rotate_speed); beginShape(shape_mode); var random_seed = int(frameCount/12); var j = int(my_random(random_seed) * point_num); var d = int( my_random(random_seed) * (random_step_range[1]-random_step_range[0]) + random_step_range[0] ); for (let i = 0; i < point_num/2; i++) { j += d; j %= point_num; vertex( radius cos(j 2 * PI / point_num), radius sin(j 2 * PI / point_num) ); }

endShape(); pop(); }

function draw() { background(255, 30); translate(width/2, height/2); ring_crystallization( 39, 320, TRIANGLES, 60, -1/60/5 2 PI, [3, 15] ); ring_crystallization( 39, 160, TRIANGLES, 100, 1/60/5 2 PI, [3, 7] ); ring_crystallization( 39, 90, TRIANGLES, -170, 1/60/5 2 PI, [3, 7] ); ring_crystallization( 39, 220, TRIANGLES, 230, 1/60/5 2 PI, [3, 7] ); }

Imgur

但我發現這些環的隨機 pattern 和變動時間點有點過於一致,雖然都是每 12 幀變動一次,但我希望每個環變動的時間點都是不一樣的,另外也希望每個環從 my_random 得出的隨機值都是不一樣的(因為輸入一樣,會導致輸出一樣)。

因此我們再針對 ring_crystallization 再多加入兩個參數:

random_seed 讓隨機函數多加一個 seed,增加差異化

delay 調整隨機值變動的時間點

function setup() {
    createCanvas(windowWidth, windowHeight);
    stroke(0);
}

function my_random(x, seed) { // 讓隨機函數多加一個 seed,增加差異化 return fract(sin(x425.121)437.53123*seed); }

function ring_crystallization( point_num, hue, shape_mode, radius, rotate_speed, random_step_range, random_seed, // 讓隨機函數多加一個 seed,增加差異化 delay // 調整隨機值變動的時間點 ) { colorMode(HSB, 360, 100, 100, 100); stroke(hue, 30, 80); fill(hue, 30, 80, 10); colorMode(RGB); push(); rotate(frameCount * rotate_speed); beginShape(shape_mode); var x = int((frameCount + delay)/12); // 調整隨機值變動的時間點 var j = int(my_random(x, random_seed) * point_num); // 讓隨機函數多加一個 seed,增加差異化 var d = int( my_random(x, random_seed) * // 讓隨機函數多加一個 seed,增加差異化 (random_step_range[1]-random_step_range[0]) + random_step_range[0] ); for (let i = 0; i < point_num/2; i++) { j += d; j %= point_num; vertex( radius cos(j 2 * PI / point_num), radius sin(j 2 * PI / point_num) ); }

endShape(); pop(); }

function draw() { background(255, 30); translate(width/2, height/2); ring_crystallization( 39, 320, TRIANGLES, 60, -1/60/5 2 PI, [3, 15], 11, 5 ); ring_crystallization( 39, 160, TRIANGLES, 100, 1/60/5 2 PI, [3, 7], 20, 3 ); ring_crystallization( 39, 90, TRIANGLES, -170, 1/60/5 2 PI, [3, 7], 10, 1 ); ring_crystallization( 39, 220, TRIANGLES, 230, 1/60/5 2 PI, [3, 7], 13, 7 ); }

Imgur

作品網址:
https://openprocessing.org/sketch/2317189

到這裡我們的環狀結晶動畫宣告完成!各位讀者可以再發揮自己的創造力,讓作品增添更多趣味性與變化。