DEV Community

張旭豐
張旭豐

Posted on

同樣是 50 行程式碼,為什麼做出來的東西像藝術品?

先玩再說

這是做出來會長這樣——看到那些「會呼吸」的漸層了嗎?不是漸變,是雜訊。但看起來像是有生命的流體。

為什麼?

別人做出來像藝術品,你做出來像數學作業

p5.js 有兩個 noise()

// 壞掉的 noise(直線)
ellipse(x, noise(x)*height, 10, 10);  // 看起來像鋸齒

// 會呼吸的 noise(加時間)
ellipse(x, noise(x, frameCount*0.01)*height, 10, 10);  // 看起來像海草
Enter fullscreen mode Exit fullscreen mode

差別只有一個維度:時間

加上 frameCount 這件事,多數教學都有講。但多數教學沒講的是:同樣是 50 行,為什麼做出來像藝術品而不是像「證明我會用 noise 函式」?

秘密一:視覺幻覺——你的大腦在幫視覺「加工」

看這張圖:

●●●●●●●●●●●●●●●●●●●●
●●●●○○○○●●●●●●●●●●●●
●●●●●●●●●●●●●●●●●●●●
Enter fullscreen mode Exit fullscreen mode

你看到什麼?

多數人說「一條線」。但實際上那只是一群各自獨立的點,大腦自動幫你連起來了。

這叫視覺幻覺——沒有意義的訊號,大腦自動幫你補上結構和意圖。

TeamLab 就是用這個原理:500 個各自輕微不同步的簡單元素,大腦把它們當成「一片會呼吸的森林」。

秘密二:讓變化「慢一點開始」——看起來自然多了

「慢慢亮」「慢慢暗」比「立刻亮立刻暗」感覺自然,為什麼?

因為現實世界裡東西改變速度很少是均勻的。一盞燈關掉的時候,是先快後慢——不是線性勻速。

不是線性的:

  • brightness = distance(線性:均勻變化,感覺像機器)
  • brightness = t * (2 - t)(快開始,慢結束,感覺像真的)

人眼對「一開始快、然後慢下來」的速度變化特別有感覺,因為這跟我們在物理世界遇到的情況一致。

function easeOutQuad(t) {
  return t * (2 - t);  // 簡單的緩出公式
}
Enter fullscreen mode Exit fullscreen mode

做出來(一):基礎流體場

let particles = [];

function setup() {
  createCanvas(windowWidth, windowHeight);
  for (let i = 0; i < 300; i++) {
    particles.push({
      x: random(width),
      y: random(height),
      speed: random(0.8, 1.5)
    });
  }
}

function draw() {
  background(255, 253, 250);  // 暖白,不是純白

  for (let p of particles) {
    // 正確的 flow field:用粒子真實位置算 noise
    let noiseScale = 0.005;
    let angle = noise(p.x * noiseScale, p.y * noiseScale, frameCount * 0.004) * TWO_PI * 2;

    p.x += cos(angle) * p.speed;
    p.y += sin(angle) * p.speed;

    // 繞圈回來
    if (p.x < 0) p.x = width;
    if (p.x > width) p.x = 0;
    if (p.y < 0) p.y = height;
    if (p.y > height) p.y = 0;

    // 日系啞色
    let col = noise(p.x * 0.01, p.y * 0.01, frameCount * 0.008);
    fill(60 + col*40, 80 + col*30, 100 + col*60, 150);
    noStroke();
    ellipse(p.x, p.y, 4, 4);
  }
}
Enter fullscreen mode Exit fullscreen mode

為什麼看起來像藝術品?

  1. 暖白底色background(255, 253, 250) 不是純白,是紙張的顏色
  2. 啞色粒子RGB(60-100, 80-110, 100-160) 不是鮮豔的藍或紅,是日本寺廟感的光
  3. 300 個粒子:數量夠多,大腦就會開始「出戲」——把機械過程詮釋成生命

做出來(二):加分項——滑鼠互動

function mouseMoved() {
  for (let p of particles) {
    let d = dist(mouseX, mouseY, p.x, p.y);
    if (d < 100) {
      // 滑鼠靠近時,粒子被推開
      let angle = atan2(p.y - mouseY, p.x - mouseX);
      p.x += cos(angle) * (100 - d) * 0.05;
      p.y += sin(angle) * (100 - d) * 0.05;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

這段加上去,畫面從「動態壁布」變成「活的空間」——滑鼠變成一種存在感。

做出來(三):再加一個維度——顏色也跟著變

把以下函式加到前面的程式碼下面,就能在同一個粒子系統裡同時控制位置和顏色:

function displayParticle(p) {
  // 用粒子的真實位置算 noise(與上方 flow field 共用同一套三維 noise 空間)
  let n = noise(p.x*0.01, p.y*0.01, frameCount*0.008);

  // 日系啞色系:低對比、有灰度
  let r = 80 + n * 80;    // 80-160
  let g = 100 + n * 60;   // 100-160
  let b = 120 + n * 60;   // 120-180
  let a = 100 + n * 100;  // 透明度也有漸層

  fill(r, g, b, a);
  noStroke();

  // 粒子大小也呼吸
  let size = 3 + sin(frameCount*0.05 + p.x * 0.1) * 2;
  ellipse(p.x, p.y, size, size);
}
Enter fullscreen mode Exit fullscreen mode

然後在 draw() 迴圈裡,每次畫完位置呼叫 displayParticle(p) 就能著色。

為什麼日系美學做出來的東西看起來比較貴?

因為低對比、灰階、有「餘韻」的顏色,大腦不會覺得被「攻擊」,反而願意停留在上面。這就是 TeamLab 每個展都排隊的原因之一——視覺不疲勞。

完整程式碼

// p5.js Noise Flow Field — 日系啞光版本
// 50行以內,做出像藝術品的流體視覺效果

let particles = [];
const NUM = 300;

function setup() {
  createCanvas(windowWidth, windowHeight);
  colorMode(RGB, 255, 255, 255, 255);

  for (let i = 0; i < NUM; i++) {
    particles.push({
      x: random(width),
      y: random(height),
      speed: random(0.8, 1.5)
    });
  }
}

function draw() {
  // 暖白底(紙張色,不是純白)
  background(255, 253, 250);

  for (let p of particles) {
    // noise 三維:x, y, time — 用粒子真實位置算
    let noiseScale = 0.005;
    let angle = noise(p.x * noiseScale, p.y * noiseScale, frameCount * 0.004) * TWO_PI * 2;

    p.x += cos(angle) * p.speed;
    p.y += sin(angle) * p.speed;

    // 從左邊出去的從右邊回來
    if (p.x < 0) p.x = width;
    if (p.x > width) p.x = 0;
    if (p.y < 0) p.y = height;
    if (p.y > height) p.y = 0;

    // 日系啞色(低對比、灰藍調)
    let n = noise(p.x * 0.01, p.y * 0.01, frameCount * 0.008);
    let c = 90 + n * 70;
    fill(c + 10, c + 30, c + 60, 120);
    noStroke();

    // 大小也呼吸
    let sz = 3 + sin(frameCount * 0.03 + p.x * 0.1) * 1.5;
    ellipse(p.x, p.y, sz, sz);
  }
}

function mouseMoved() {
  for (let p of particles) {
    let d = dist(mouseX, mouseY, p.x, p.y);
    if (d < 120) {
      let angle = atan2(p.y - mouseY, p.x - mouseX);
      let force = (120 - d) * 0.04;
      p.x += cos(angle) * force;
      p.y += sin(angle) * force;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

為什麼這個做起來「貴」?

  1. 視覺幻覺:300 個各自輕微不同步的粒子 = 大腦說「這是活的」
  2. 日系啞光:不是RGB(0,0,255),是 RGB(90,120,180) 這種「看得到情緒」的藍
  3. 讓變化有節奏:大小變化用 sin(),不是線性——看起來像呼吸,不像閃爍
  4. 滑鼠存在:不是點擊,是「靠近就離開」的空間感

下一步

想更有 Fu?試試:

  1. NUM 從 300 改成 800——數量多到一個程度,視覺會從「一群點」變成「一種物質」
  2. 把顏色從「灰藍」改成「暖橙→深褐」漸層——馬上從「北歐極簡」變成「昭和復古」
  3. 加入超聲波感測器——讓實際的「人在空間中移動」驅動粒子方向

最後一個方向,就是我們下一篇文章的主題。

Top comments (0)