๐ŸŽ“14์ฃผ์ฐจ: ์ธํ„ฐ๋ž™์…˜(Interaction)๊ณผ ์‹œ๊ฐ„์„ฑ(Temporal Systems)

BamgasiJMยท2025๋…„ 11์›” 6์ผ

p5.js Art

๋ชฉ๋ก ๋ณด๊ธฐ
25/67
post-thumbnail

์‹ค์‹œ๊ฐ„ ๋ฐ˜์‘๊ณผ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ๋ณ€ํ™”๋ฅผ ํ†ตํ•œ ๊ด€๊ฐ ์ฐธ์—ฌํ˜• ์•„ํŠธ

์ด ์˜ˆ์ œ๋Š” ๋งˆ์šฐ์Šค ์ž…๋ ฅ์ด๋ผ๋Š” ๊ด€๊ฐ์˜ ์ฐธ์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ›์•„ ํ˜•ํƒœ์™€ ์ƒ‰์ƒ์„ ๋ณ€ํ™”์‹œํ‚ค๋Š” ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์ƒ์„ฑ ์•„ํŠธ์˜ ๊ธฐ์ดˆ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋˜ํ•œ, ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ํ˜•ํƒœ๊ฐ€ ์Šค์Šค๋กœ ์ง„๋™ํ•˜๋ฉฐ ๋ฐœ์ „ํ•˜๋Š” ์‹œ๊ฐ„์„ฑ ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

  • ์ธํ„ฐ๋ž™์…˜: ๋งˆ์šฐ์Šค์˜ ์œ„์น˜์™€ ํด๋ฆญ ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์‹œ๊ฐ„์„ฑ: frameCount๋ฅผ ์ด์šฉํ•ด ์‹œ์Šคํ…œ์˜ ๋ณ€ํ™” ์†๋„๋ฅผ ์ œ์–ดํ•˜๊ณ , millis() (ํ˜„์žฌ ์‹œ๊ฐ„) ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์œ ๊ธฐ์ ์ธ ์›€์ง์ž„์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

// ====== ์ „์—ญ ๋ณ€์ˆ˜ ์„ค์ • ======
let W = 800;
let H = 600;
let particles = []; // ์ž…์ž ๊ฐ์ฒด๋“ค์„ ์ €์žฅํ•  ๋ฐฐ์—ด (Array of Objects)
const numParticles = 50; // ์ž…์ž์˜ ๊ฐœ์ˆ˜ (๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ณ ๋ ค)

function setup() {
  createCanvas(W, H);
  background(0);
  noStroke();
  
  // 50๊ฐœ์˜ ์ž…์ž ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐฐ์—ด์— ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
  for (let i = 0; i < numParticles; i++) {
    particles.push(new Particle(random(width), random(height)));
  }
}

function draw() {
  // 1. ์‹œ๊ฐ„์„ฑ: ๋ฐฐ๊ฒฝ์„ ๋ฐ˜ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ฎ์–ด ์ž”์ƒ ํšจ๊ณผ (Temporal System)
  // ์ด์ „ ํ”„๋ ˆ์ž„์˜ ํ”์ ์„ ๋‚จ๊ธฐ๋ฉฐ, ์›€์ง์ž„์˜ '์‹œ๊ฐ„์„ฑ'์„ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
  background(0, 30); 

  // 2. ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๊ณ„์‚ฐ (Interaction Data)
  // ๋งˆ์šฐ์Šค์˜ ์œ„์น˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹œ์Šคํ…œ์˜ '๋Œ์–ด๋‹น๊น€' ์ค‘์‹ฌ์ ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  let targetX = mouseX;
  let targetY = mouseY;

  // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ƒํƒœ(๋ถˆ๋ฆฐ ๊ฐ’)๋ฅผ 'ํž˜'์˜ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  let attractionStrength = mouseIsPressed ? 0.05 : 0.01;

  // 3. ์ž…์ž ์—…๋ฐ์ดํŠธ ๋ฐ ๊ทธ๋ฆฌ๊ธฐ
  for (let p of particles) {
    // ๋งˆ์šฐ์Šค ์œ„์น˜(target)๋ฅผ ํ–ฅํ•ด ์ž…์ž๋ฅผ ์ด๋™์‹œํ‚ค๋Š” ํž˜์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    p.attract(targetX, targetY, attractionStrength);
    
    // ์‹œ๊ฐ„ ํ๋ฆ„์— ๋”ฐ๋ฅธ ์ง„๋™ ๋ฐ ์œ„์น˜ ์—…๋ฐ์ดํŠธ
    p.update();
    
    // ๋งˆ์šฐ์Šค์™€ ์ž…์ž์˜ ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ์ƒ‰์ƒ๊ณผ ํฌ๊ธฐ๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    p.display(targetX, targetY);
  }
}

/**
 * ์ž…์ž(Particle) ํด๋ž˜์Šค ์ •์˜
 * - ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž…์ž๋“ค์˜ ์ƒํƒœ์™€ ํ–‰๋™์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
 */
class Particle {
  constructor(x, y) {
    this.pos = createVector(x, y); // ํ˜„์žฌ ์œ„์น˜
    this.vel = createVector(0, 0); // ์†๋„ (0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ ์œ ์ง€)
    this.acc = createVector(0, 0); // ๊ฐ€์†๋„
    this.baseSize = 10;            // ๊ธฐ๋ณธ ํฌ๊ธฐ
    this.noiseOffset = random(1000); // ํผ๋ฆฐ ๋…ธ์ด์ฆˆ๋ฅผ ์œ„ํ•œ ๋ฌด์ž‘์œ„ ์˜คํ”„์…‹ (์‹œ๊ฐ„์„ฑ)
  }

  /**
   * ๋งˆ์šฐ์Šค ์œ„์น˜(target)๋กœ ๋Œ์–ด๋‹น๊ธฐ๋Š” ํž˜์„ ๊ณ„์‚ฐํ•˜์—ฌ ๊ฐ€์†๋„์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
   */
  attract(targetX, targetY, strength) {
    let target = createVector(targetX, targetY);
    // 1. ๋ฐฉํ–ฅ ๋ฒกํ„ฐ ๊ณ„์‚ฐ: target - pos
    let force = p5.Vector.sub(target, this.pos); 
    // 2. ๊ฑฐ๋ฆฌ ์ œํ•œ: ๋„ˆ๋ฌด ๋ฉ€๋ฉด ํž˜์„ ์ค„์ด๊ณ , ๋„ˆ๋ฌด ๊ฐ€๊นŒ์šฐ๋ฉด ํž˜์„ 0์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    let dist = force.mag();
    dist = constrain(dist, 5, 200); // ์ตœ์†Œ 5, ์ตœ๋Œ€ 200์œผ๋กœ ๊ฑฐ๋ฆฌ๊ฐ’ ์ œํ•œ
    
    // 3. ํž˜์˜ ํฌ๊ธฐ ๊ณ„์‚ฐ: (strength) * (1/dist^2)์˜ ๊ทผ์‚ฌ์น˜ ์ ์šฉ
    force.normalize(); // ๋ฐฉํ–ฅ๋งŒ ๋‚จ๊ธฐ๊ณ 
    force.mult(strength * (1 / (dist * 0.01))); // ํž˜์˜ ํฌ๊ธฐ ์ ์šฉ
    
    this.acc.add(force); // ๊ฐ€์†๋„์— ํž˜์„ ๋”ํ•ฉ๋‹ˆ๋‹ค.
  }
  
  /**
   * ์†๋„์™€ ์œ„์น˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ์‹œ๊ฐ„์„ฑ ์ง„๋™์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
   */
  update() {
    // 1. ์‹œ๊ฐ„์„ฑ: ํผ๋ฆฐ ๋…ธ์ด์ฆˆ๋ฅผ ์ด์šฉํ•œ ์ง„๋™ ์ถ”๊ฐ€
    // ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ(frameCount) ์œ„์น˜๊ฐ€ ๋ฏธ์„ธํ•˜๊ฒŒ ์ง„๋™ํ•ฉ๋‹ˆ๋‹ค.
    let noiseValX = map(noise(this.noiseOffset, frameCount * 0.005), 0, 1, -0.5, 0.5);
    let noiseValY = map(noise(this.noiseOffset + 10, frameCount * 0.005), 0, 1, -0.5, 0.5);
    this.acc.add(noiseValX * 0.1, noiseValY * 0.1); // ๋ฏธ์„ธํ•œ ๋žœ๋ค ๊ฐ€์†๋„ ์ถ”๊ฐ€
    
    // 2. ๋ฌผ๋ฆฌ ์—…๋ฐ์ดํŠธ
    this.vel.add(this.acc);
    this.vel.mult(0.95); // ๋งˆ์ฐฐ๋ ฅ (์†๋„๋ฅผ 0.95๋กœ ์ค„์—ฌ ์•ˆ์ •ํ™”)
    this.pos.add(this.vel);
    this.acc.mult(0); // ๊ฐ€์†๋„๋ฅผ ๋งค ํ”„๋ ˆ์ž„ ์ดˆ๊ธฐํ™” (๋งค์šฐ ์ค‘์š”)
  }

  /**
   * ์ž…์ž๋ฅผ ํ™”๋ฉด์— ๊ทธ๋ฆฌ๊ณ , ๋งˆ์šฐ์Šค ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ์ƒ‰์ƒ/ํฌ๊ธฐ ๋ณ€ํ™”๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
   */
  display(targetX, targetY) {
    // 1. ์ธํ„ฐ๋ž™์…˜: ๋งˆ์šฐ์Šค์™€์˜ ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ์ƒ‰์ƒ/ํฌ๊ธฐ ๋ฐ์ดํ„ฐ ์ •์˜
    let distToMouse = dist(this.pos.x, this.pos.y, targetX, targetY);
    
    // ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ํฌ๊ธฐ ๋ณ€ํ™” (๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์ปค์ง)
    let size = this.baseSize + map(distToMouse, 0, width, 10, 0);
    
    // ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ์ƒ‰์ƒ ๋ณ€ํ™” (๊ฐ€๊นŒ์šธ์ˆ˜๋ก ๋ฐ๊ณ  ๋ถ‰์€์ƒ‰)
    let hue = map(distToMouse, 0, width, 0, 60); // 0(Red) ~ 60(Yellow)
    let brightness = map(distToMouse, 0, width, 100, 50);

    // ์ƒ‰์ƒ ๋ชจ๋“œ๋ฅผ HSB๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ƒ‰์ƒ ์ œ์–ด๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
    colorMode(HSB, 360, 100, 100);
    fill(hue, 80, brightness, 80); // ๋ฐ˜ํˆฌ๋ช…๋„๋ฅผ 80์œผ๋กœ ์„ค์ •
    
    ellipse(this.pos.x, this.pos.y, size, size);
    colorMode(RGB, 255); // ๋‹ค์Œ ๋“œ๋กœ์ž‰์„ ์œ„ํ•ด ๋‹ค์‹œ RGB๋กœ ๋ณต๊ตฌ
  }
}
  1. โš›๏ธ ๊ฐ์ฒด ์ง€ํ–ฅ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ
  • Particle ํด๋ž˜์Šค: ์ˆ˜๋งŽ์€ ์ž…์ž์˜ ์ƒํƒœ(pos, vel, acc)์™€ ํ–‰๋™(attract, update, display)์„ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ๋ฌถ์–ด ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ๋ฅผ ๋ชจ๋“ˆํ™”ํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค๋ฉฐ, ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•  ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

  • ๋ฒกํ„ฐ ์‚ฌ์šฉ: ์œ„์น˜, ์†๋„, ๊ฐ€์†๋„ ๋ชจ๋‘ p5.Vector๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฒกํ„ฐ ์—ฐ์‚ฐ(add, sub, mult, normalize)์€ ๋ฌผ๋ฆฌํ•™์  ์›€์ง์ž„์„ ์ง๊ด€์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉฐ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

  1. ๐Ÿ–ฑ๏ธ ์ธํ„ฐ๋ž™์…˜ (๊ด€๊ฐ ์ฐธ์—ฌํ˜• ๋ฐ์ดํ„ฐ)
  • ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘: mouseX, mouseY, mouseIsPressed๋ผ๋Š” ๊ด€๊ฐ์˜ ์‹ค์‹œ๊ฐ„ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ํž˜์˜ ์ œ์–ด: attract() ํ•จ์ˆ˜๊ฐ€ ์ธํ„ฐ๋ž™์…˜์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

  • ์ค‘์‹ฌ์ : ๋งˆ์šฐ์Šค ์œ„์น˜(targetX, targetY)๊ฐ€ ์ž…์ž๋“ค์„ ๋Œ์–ด๋‹น๊ธฐ๋Š” ์ค‘์‹ฌ์  ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ํž˜ ์กฐ์ ˆ: attractionStrength๋Š” ๋งˆ์šฐ์Šค ํด๋ฆญ ์ƒํƒœ(mouseIsPressed)๋ผ๋Š” ๋ถˆ๋ฆฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์น˜ํ™”ํ•˜์—ฌ ํž˜์˜ ํฌ๊ธฐ๋ฅผ ๋‘ ๋ฐฐ ์ด์ƒ์œผ๋กœ ๋ณ€ํ™”์‹œํ‚ต๋‹ˆ๋‹ค. (ํด๋ฆญ ์‹œ ๋” ๊ฐ•ํ•˜๊ฒŒ ๋Œ์–ด๋‹น๊น€)

  • ์‹œ๊ฐ์  ๋ฐ˜์‘: display() ํ•จ์ˆ˜ ๋‚ด์—์„œ distToMouse (๋งˆ์šฐ์Šค์™€ ์ž…์ž์˜ ๊ฑฐ๋ฆฌ)๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ, ์ด ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ์ž…์ž์˜ ํฌ๊ธฐ(size)์™€ ์ƒ‰์ƒ(hue, brightness)์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณ€ํ™”์‹œํ‚ต๋‹ˆ๋‹ค. ์ด๋Š” ๊ด€๊ฐ์˜ ์œ„์น˜๊ฐ€ ์ง์ ‘ ์•„ํŠธ์›Œํฌ์˜ ํ˜•ํƒœ๋ฅผ ๋ณ€ํ˜•์‹œํ‚ค๋Š” ํšจ๊ณผ๋ฅผ ์ค๋‹ˆ๋‹ค.

  1. โณ ์‹œ๊ฐ„์„ฑ (Temporal Systems)
  • ํ”„๋ ˆ์ž„ ๊ธฐ๋ฐ˜ ์‹œ๊ฐ„: frameCount๋Š” ์‹œ์Šคํ…œ์ด ์‹œ์ž‘๋œ ์ดํ›„์˜ ํ”„๋ ˆ์ž„ ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ด๋Š” ์‹œ๊ฐ„์˜ ํ๋ฆ„์„ ๋Œ€ํ‘œํ•˜๋Š” ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ์œ ๊ธฐ์  ์ง„๋™: update() ํ•จ์ˆ˜์—์„œ frameCount๋ฅผ ํผ๋ฆฐ ๋…ธ์ด์ฆˆ์˜ ์ธ์ž๋กœ ์‚ฌ์šฉํ•˜์—ฌ, ์ž…์ž๊ฐ€ ๋งˆ์น˜ ์‚ด์•„์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ฏธ์„ธํ•˜๊ณ  ๋ถ€๋“œ๋Ÿฌ์šด ๋ถˆ๊ทœ์น™ํ•œ ์ง„๋™์„ ํ•˜๋„๋ก ๊ฐ€์†๋„๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • ์ž”์ƒ ํšจ๊ณผ: background(0, 30) ์ฝ”๋“œ๋Š” ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋ฐฐ๊ฒฝ์„ ์™„์ „ํžˆ ์ง€์šฐ์ง€ ์•Š๊ณ  ์˜…์€ ๊ฒ€์€์ƒ‰์œผ๋กœ ๋ง์น ํ•˜์—ฌ, ์ด์ „ ํ”„๋ ˆ์ž„์˜ ๋“œ๋กœ์ž‰์ด ๋ฏธ์„ธํ•˜๊ฒŒ ๋‚จ์•„ ์ž…์ž๋“ค์˜ ์›€์ง์ž„์— ์‹œ๊ฐ„์˜ ํ”์ (Temporal Trace)์„ ๋‚จ๊น๋‹ˆ๋‹ค.

โœ… ์˜ˆ์ œ A: ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ํ˜•ํƒœ ๋ณ€ํ™” (Temporal Systems)

frameCount๋ฅผ ์‹œ๊ฐ„์˜ ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ˜•ํƒœ๊ฐ€ ๊ณ„์† ์ง„๋™ํ•˜๊ณ  ๋ณ€ํ™”ํ•˜๋Š” ๊ตฌ์กฐ.

let numPoints = 300;

function setup() {
  createCanvas(800, 400);
  noFill();
  stroke(240, 200, 30);
}

function draw() {
  background(15);

  beginShape();
  for (let i = 0; i < numPoints; i++) {
    let x = map(i, 0, numPoints, 0, width);

    // ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์ง„๋™
    let t = frameCount * 0.02;
    let y = height / 2 + sin(i * 0.05 + t) * 120;

    vertex(x, y);
  }
  endShape();
}

๐Ÿ” ์„ค๋ช…

frameCount๋ฅผ ์‹œ๊ฐ„(time) ๋ฐ์ดํ„ฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•ด ๊ณ„์† ๋ณ€ํ™”ํ•˜๋Š” ํŒŒํ˜•์„ ์ƒ์„ฑ.

i * 0.05 + t๊ฐ€ ๊ฐ€์ง€๋Š” ์œ„์ƒ ๋ณ€ํ™”(phase shift) ๋•๋ถ„์— ํŒŒ๋™์ด ํ˜๋Ÿฌ๊ฐ€๋Š” ๋А๋‚Œ์„ ์ค€๋‹ค.

"์‹œ๊ฐ„์„ฑ"์„ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹œ๊ฐํ™”ํ•˜๋Š” ์˜ˆ์ œ.

โœ… ์˜ˆ์ œ B: ๋งˆ์šฐ์Šค ๊ธฐ๋ฐ˜ ์ธํ„ฐ๋ž™์…˜ โ€” Particle Attraction

๋งˆ์šฐ์Šค ์œ„์น˜๊ฐ€ โ€œ์ž…๋ ฅ ๋ฐ์ดํ„ฐโ€๊ฐ€ ๋˜์–ด ํŒŒํ‹ฐํด๋“ค์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ˜์‘.

let particles = [];

function setup() {
  createCanvas(800, 500);
  for (let i = 0; i < 200; i++) {
    particles.push({
      pos: createVector(random(width), random(height)),
      vel: createVector(random(-1, 1), random(-1, 1))
    });
  }
}

function draw() {
  background(15);

  for (let p of particles) {
    // ๋งˆ์šฐ์Šค ๋ฐฉํ–ฅ์œผ๋กœ ๋Œ์–ด๋‹น๊ธฐ๋Š” ํž˜
    let force = createVector(mouseX - p.pos.x, mouseY - p.pos.y);
    force.setMag(0.05);

    p.vel.add(force);
    p.vel.limit(3);
    p.pos.add(p.vel);

    fill(240, 200, 30);
    noStroke();
    circle(p.pos.x, p.pos.y, 6);
  }
}

โœ… ์˜ˆ์ œ C: ํด๋ฆญ/๋“œ๋ž˜๊ทธ ๊ธฐ๋ฐ˜ ํ˜•ํƒœ ์ƒ์„ฑ โ€” Gesture Drawing

์‚ฌ์šฉ์ž๊ฐ€ ๊ทธ๋ฆฐ ์„ ์„ โ€œ์ž…๋ ฅ ๋ฐ์ดํ„ฐโ€๋กœ ์‚ผ์•„ ์ž๋™์ ์œผ๋กœ ๋ณ€ํ™˜๋œ ํ˜•ํƒœ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๋ฐฉ์‹.

let path = [];

function setup() {
  createCanvas(800, 500);
  stroke(255);
  noFill();
}

function draw() {
  background(25);

  // ์‚ฌ์šฉ์ž๊ฐ€ ๊ทธ๋ฆฐ ๊ฒฝ๋กœ ์‹œ๊ฐํ™”
  beginShape();
  for (let p of path) {
    vertex(p.x, p.y);
  }
  endShape();

  // ๋“œ๋กœ์ž‰ ๊ธฐ๋ฐ˜ "๋ฐ˜์‘ํ˜• ์›"
  if (path.length > 2) {
    let last = path[path.length - 1];
    let prev = path[path.length - 2];

    let dx = last.x - prev.x;
    let dy = last.y - prev.y;
    let speed = sqrt(dx * dx + dy * dy);

    let size = map(speed, 0, 50, 10, 80);

    fill(180, 120, 255);
    noStroke();
    circle(last.x, last.y, size);
  }
}

function mouseDragged() {
  path.push(createVector(mouseX, mouseY));
}

๐Ÿ” ์„ค๋ช…

  • mouseDragged()๊ฐ€ ์‚ฌ์šฉ์ž ์ œ์Šค์ฒ˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ•์ ํ•ด path[] ๋ฐฐ์—ด์„ ๋งŒ๋“ ๋‹ค.
  • ๋งˆ์ง€๋ง‰ ๋‘ ์ ์˜ ๊ฑฐ๋ฆฌ๋กœ โ€œ์†๋„(speed)โ€๋ฅผ ๊ณ„์‚ฐ โ†’ ํ˜•ํƒœ ๋ณ€ํ™”์— ์‚ฌ์šฉ.
  • ๊ด€๊ฐ์ด ๊ทธ๋ฆฐ โ€œํ–‰์œ„โ€๊ฐ€ ๊ณง ๋ฐ์ดํ„ฐ์ด๋ฉฐ, ์‹œ์Šคํ…œ์€ ๊ทธ๊ฑธ ํ•ด์„ํ•ด์„œ ์‹œ๊ฐ์ ์œผ๋กœ ๋ฐ˜์‘ํ•œ๋‹ค.

โœ… ์˜ˆ์ œ D: ์‹œ๊ฐ„ + ์ธํ„ฐ๋ž™์…˜ ์กฐํ•ฉ โ€” Dynamic Network Web

์‚ฌ์šฉ์ž ์›€์ง์ž„ + ์‹œ๊ฐ„ ๋ณ€ํ™”๊ฐ€ ๊ฒฐํ•ฉ๋œ ์ƒ์„ฑ ์•„ํŠธ ๊ตฌ์กฐ.

let nodes = [];

function setup() {
  createCanvas(800, 400);

  for (let i = 0; i < 400; i++) {
    nodes.push({
      pos: createVector(random(width), random(height)),
      vel: createVector(random(-1, 1), random(-1, 1)),
    });
  }
}

function draw() {
  background(15);
  stroke(240, 200, 30, 80);

  // ๋ชจ๋“  ๋…ธ๋“œ ์—…๋ฐ์ดํŠธ
  for (let n of nodes) {
    // ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ํ”๋“ค๋ฆผ
    n.pos.x += n.vel.x + sin(frameCount * 0.01 + n.pos.y) * 0.4;
    n.pos.y += n.vel.y + cos(frameCount * 0.01 + n.pos.x) * 0.4;

    // ๋งˆ์šฐ์Šค ๋ฐ˜๋ฐœ๋ ฅ
    let d = dist(mouseX, mouseY, n.pos.x, n.pos.y);
    if (d < 150) {
      let repel = p5.Vector.sub(n.pos, createVector(mouseX, mouseY));
      repel.setMag(0.5);
      n.pos.add(repel);
    }

    // ๋…ธ๋“œ ๊ทธ๋ฆฌ๊ธฐ
    fill(240, 200, 30);
    noStroke();
    circle(n.pos.x, n.pos.y, 6);
  }

  // ๋…ธ๋“œ ๊ฐ„ ์—ฐ๊ฒฐ
  stroke(120, 90);
  for (let i = 0; i < nodes.length; i++) {
    for (let j = i + 1; j < nodes.length; j++) {
      let d = dist(
        nodes[i].pos.x,
        nodes[i].pos.y,
        nodes[j].pos.x,
        nodes[j].pos.y
      );
      if (d < 100) {
        line(nodes[i].pos.x, nodes[i].pos.y, nodes[j].pos.x, nodes[j].pos.y);
      }
    }
  }
}

๐Ÿ” ์„ค๋ช…

  • ์‹œ๊ฐ„์„ฑ: sin(frameCount * 0.01)์œผ๋กœ ๋ฏธ์„ธํ•œ ํ”๋“ค๋ฆผ
  • ์ธํ„ฐ๋ž™์…˜: ๋งˆ์šฐ์Šค ๋ฐ˜๋ฐœ๋ ฅ
  • ๊ตฌ์กฐ: ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ˜ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ
  • ์‹œ๊ฐ„๊ณผ ์ž…๋ ฅ์ด ๊ฒฐํ•ฉ๋œ โ€œ๊ด€๊ฐ ์ฐธ์—ฌ์  ์ƒ์„ฑ ์•„ํŠธโ€์˜ ์ „ํ˜•์ ์ธ ํŒจํ„ด
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€