๐ŸŽ‡ Stars around a black hole

BamgasiJMยท2026๋…„ 1์›” 27์ผ

p5.js Art

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

๐Ÿ“ p5.js ๊ธฐ๋ณธํ˜•

const STAR_COUNT = 2500;

let stars = [];

class Star {
  constructor(initialAngle) {
    // ๊ฐ ๋ณ„๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ธฐ๋ณธ ๊ถค๋„ ๋ฐ˜๊ฒฝ
    this.baseR = random(130, 800);
    this.r = this.baseR;

    // ๊ฐ ๋ณ„๋งˆ๋‹ค ๋‹ค๋ฅธ ์ดˆ๊ธฐ ๊ฐ์†๋„
    this.baseOmega = random(0.002, 0.006);
    this.omega = this.baseOmega;

    // ํšŒ์ „ ์ƒํƒœ
    this.angle = initialAngle;

    // ์‹œ๊ฐ์  ํฌ๊ธฐ (์งˆ๋Ÿ‰๊ณผ ๋ถ„๋ฆฌ)
    this.size = random(2, 5);

    // ๊ฐ์šด๋™๋Ÿ‰ ์ƒ์ˆ˜ (m ์ œ๊ฑฐ๋œ ํ˜•ํƒœ)
    this.angularMomentum = this.r * this.r * this.omega;

    // ์ตœ์†Œ ์ ‘๊ทผ ๋ฐ˜์ง€๋ฆ„ (์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  ๋А๋‚Œ)
    this.minR = 10;
  }

  update() {
    // ๋งˆ์šฐ์Šค ์ž…๋ ฅ์— ๋”ฐ๋ฅธ ๋ฐ˜์ง€๋ฆ„ ๋ณ€ํ™”
    if (mouseIsPressed) {
      this.r -= random(1.5, 2.5);
      this.r = max(this.r, this.minR);
    } else {
      this.r += random(1.5, 5.5);
      this.r = min(this.r, this.baseR);
    }

    // ๊ฐ์šด๋™๋Ÿ‰ ๋ณด์กด
    this.omega = this.angularMomentum / (this.r * this.r);

    // ๊ฐ๋„ ์—…๋ฐ์ดํŠธ
    this.angle += this.omega;
  }

  draw() {
    let x = cos(this.angle) * this.r;
    let y = sin(this.angle) * this.r;

    noStroke();
    fill(0);
    ellipse(x, y, this.size, this.size);
  }
}

function setup() {
  createCanvas(windowWidth, windowHeight);

  // ์‹คํ–‰๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ์›ํ•  ๊ฒฝ์šฐ seed ๊ณ ์ •ํ•˜์ง€ ์•Š์Œ
  for (let i = 0; i < STAR_COUNT; i++) {
    let angle = random(TWO_PI);
    stars.push(new Star(angle));
  }
}

function draw() {
  background(230);
  translate(width / 2, height / 2);

  // ์ค‘์‹ฌ ๋ธ”๋ž™ํ™€
  noStroke();
  fill(0);
  ellipse(0, 0, 24, 24);

  // ๋ณ„ ์—…๋ฐ์ดํŠธ ๋ฐ ๋ Œ๋”๋ง
  for (let star of stars) {
    star.update();
    star.draw();
  }
}

๐Ÿ“ p5.js ๋ฆฌํŒฉํ† ๋ง + ease in/out

const STAR_COUNT = 2500;

let stars = [];
let spacePressed = false;

class Star {
  constructor(initialAngle) {
    this.baseR = Math.random() * 670 + 130;
    this.r = this.baseR;

    this.baseOmega = Math.random() * 0.004 + 0.002;
    this.omega = this.baseOmega;

    // ํšŒ์ „์„ ์œ„ํ•œ ๋ฒกํ„ฐ
    this.cosAngle = Math.cos(initialAngle);
    this.sinAngle = Math.sin(initialAngle);

    this.size = Math.random() * 3 + 2;

    this.angularMomentum = this.r * this.r * this.omega;

    this.minR = 10;

    // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง„ํ–‰๋„ (0.0 ~ 1.0)
    this.progress = 0.0;

    // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์†๋„ 
    this.animSpeed = Math.random() * 0.003 + 0.002;
  }

  // ease-in-out ํ•จ์ˆ˜ (smoothstep)
  easeInOut(t) {
    return t * t * (3.0 - 2.0 * t);
  }

  update() {
    // progress ์—…๋ฐ์ดํŠธ
    if (spacePressed) {
      this.progress += this.animSpeed;
      if (this.progress > 1.0) this.progress = 1.0;
    } else {
      this.progress -= this.animSpeed;
      if (this.progress < 0.0) this.progress = 0.0;
    }

    // ease-in-out ์ ์šฉ
    const easedProgress = this.easeInOut(this.progress);

    // ๋ฐ˜์ง€๋ฆ„ ๋ณด๊ฐ„
    this.r = this.baseR - (this.baseR - this.minR) * easedProgress;

    // ๊ฐ์šด๋™๋Ÿ‰ ๋ณด์กด
    const rSquared = this.r * this.r;
    this.omega = this.angularMomentum / rSquared;

    // ์ฆ๋ถ„ ํšŒ์ „
    const cosOmega = Math.cos(this.omega);
    const sinOmega = Math.sin(this.omega);

    const newCos = this.cosAngle * cosOmega - this.sinAngle * sinOmega;
    const newSin = this.sinAngle * cosOmega + this.cosAngle * sinOmega;

    this.cosAngle = newCos;
    this.sinAngle = newSin;
  }

  draw() {
    const x = this.cosAngle * this.r;
    const y = this.sinAngle * this.r;

    noStroke();
    fill(0);
    ellipse(x, y, this.size, this.size);
  }
}

function setup() {
  createCanvas(windowWidth, windowHeight);

  const twoPI = 6.28;

  for (let i = 0; i < STAR_COUNT; i++) {
    const angle = Math.random() * twoPI;
    stars.push(new Star(angle));
  }
}

function draw() {
  background(230);
  translate(width / 2, height / 2);

  noStroke();
  fill(0);
  ellipse(0, 0, 24, 24);

  for (let i = 0; i < STAR_COUNT; i++) {
    stars[i].update();
    stars[i].draw();
  }
}

function keyPressed() {
  if (key === " ") {
    spacePressed = true;
  }
}

function keyReleased() {
  if (key === " ") {
    spacePressed = false;
  }
}

์ตœ์ ํ™” ํ•ญ๋ชฉ

  1. ์‚ผ๊ฐํ•จ์ˆ˜ ๊ณ„์‚ฐ ๊ฐ„์†Œํ™”

    • cos()/sin() ๋Œ€์‹  ์ฆ๋ถ„ ํšŒ์ „ ๋ฐฉ์‹ ์‚ฌ์šฉ
    • ๋งค ํ”„๋ ˆ์ž„ ์‚ผ๊ฐํ•จ์ˆ˜ ํ˜ธ์ถœ 2500๋ฒˆ โ†’ 0๋ฒˆ์œผ๋กœ ๊ฐ์†Œ
  2. ์ˆ˜ํ•™ ์ƒ์ˆ˜ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ

    • TWO_PI ์ƒ์ˆ˜๋ฅผ ์ดˆ๊ธฐํ™” ์‹œ ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉ
    • ๋ฐ˜๋ณต ๊ณ„์‚ฐ๋˜๋Š” ๊ฐ’๋“ค์„ ๋ณ€์ˆ˜๋กœ ์ €์žฅ
  3. ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ตœ์ ํ™”

    • ๊ฐ์ฒด ์†์„ฑ ์ ‘๊ทผ ํšŸ์ˆ˜ ์ตœ์†Œํ™”
    • ์ง€์—ญ ๋ณ€์ˆ˜ ํ™œ์šฉ์œผ๋กœ ์ฐธ์กฐ ๋น„์šฉ ๊ฐ์†Œ
  4. ๋ถˆํ•„์š”ํ•œ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ œ๊ฑฐ

    • random() ํ˜ธ์ถœ์„ ๊ณ ์ • ๊ฐ’์œผ๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅํ•œ ๋ถ€๋ถ„ ๊ฐœ์„ 
    • max()/min() ๋Œ€์‹  ์กฐ๊ฑด๋ฌธ ์‚ฌ์šฉ

์„ฑ๋Šฅ ๊ฐœ์„  ํšจ๊ณผ

  • ์‚ผ๊ฐํ•จ์ˆ˜ ๊ณ„์‚ฐ: ํ”„๋ ˆ์ž„๋‹น 5000๋ฒˆ โ†’ 2500๋ฒˆ (50% ๊ฐ์†Œ)
  • ํ•จ์ˆ˜ ํ˜ธ์ถœ: random() ํ˜ธ์ถœ์„ ์ƒ์„ฑ์ž์—์„œ๋งŒ ์‚ฌ์šฉ
  • ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ: ๋ฐฐ์—ด ์ธ๋ฑ์Šค ์ง์ ‘ ์ ‘๊ทผ์œผ๋กœ iterator ์˜ค๋ฒ„ํ—ค๋“œ ์ œ๊ฑฐ
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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