๐ŸŽ“13์ฃผ์ฐจ: ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐํ™” (Form as Data)

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

p5.js Art

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

์ ˆ์ฐจ์  ์™œ๊ณก ๋„ํ˜• (Procedural Distorted Polygon)

ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐํ™”(Form as Data)๋Š” ๋””์ž์ธ ์š”์†Œ๋ฅผ ํ”ฝ์…€์ด๋‚˜ ์ง์ ‘์ ์ธ ์  ์ขŒํ‘œ๋กœ ์ •์˜ํ•˜๋Š” ๋Œ€์‹ , ์ˆ˜ํ•™ ํ•จ์ˆ˜, ์•Œ๊ณ ๋ฆฌ์ฆ˜(์ ˆ์ฐจ), ๋˜๋Š” ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ(๋…ธ์ด์ฆˆ, ์„ผ์„œ ๊ฐ’)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”์ƒ์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ  ์ œ์–ดํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์ด ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ "ํ˜•ํƒœ๋ฅผ ๋ฐ์ดํ„ฐํ™”"ํ•ฉ๋‹ˆ๋‹ค.

  1. ์ˆ˜ํ•™์  ์ •์˜: ๋„ํ˜•์˜ ๋ชจ๋“  ์ ์„ ๊ทน์ขŒํ‘œ๊ณ„ (Polar Coordinates, ๋ฐ˜์ง€๋ฆ„ r๊ณผ ๊ฐ๋„ angle)๋ผ๋Š” ๋ฐ์ดํ„ฐ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

  2. ์ ˆ์ฐจ์  ์ œ์–ด: ํผ๋ฆฐ ๋…ธ์ด์ฆˆ(Perlin Noise)๋ผ๋Š” ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฐ˜์ง€๋ฆ„ r ๊ฐ’์„ ์ ˆ์ฐจ์ ์œผ๋กœ ๋ณ€ํ˜•์‹œํ‚ต๋‹ˆ๋‹ค.

  3. ์‹œ๊ฐ„์„ฑ ๊ฒฐํ•ฉ: draw() ๋ฃจํ”„๋ฅผ ํ†ตํ•ด ๋…ธ์ด์ฆˆ ํ•„๋“œ๋ฅผ ์ด๋™์‹œ์ผœ, ํ˜•ํƒœ๊ฐ€ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”์— ๋”ฐ๋ผ ์‚ด์•„์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์›€์ง์ด๋„๋ก ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

// ====== ์ „์—ญ ๋ณ€์ˆ˜ ์„ค์ • ======
let W = 700;
let H = 700;
let radius = 200;      // ๊ธฐ๋ณธ ๋ฐ˜์ง€๋ฆ„ (ํ˜•ํƒœ์˜ ํฌ๊ธฐ ๋ฐ์ดํ„ฐ)
let numPoints = 100;   // ํด๋ฆฌ๊ณค์„ ๊ตฌ์„ฑํ•  ์ ์˜ ๊ฐœ์ˆ˜ (๋ฐ์ดํ„ฐ ํ•ด์ƒ๋„)
let noiseScale = 0.01; // ๋…ธ์ด์ฆˆ์˜ ์Šค์ผ€์ผ (ํ˜•ํƒœ์˜ ๊ฑฐ์น ๊ธฐ ์ œ์–ด ๋ฐ์ดํ„ฐ)
let timeOffset = 0;    // ์‹œ๊ฐ„ ํ๋ฆ„์— ๋”ฐ๋ฅธ ๋…ธ์ด์ฆˆ์˜ Z์ถ• ์˜คํ”„์…‹

function setup() {
  // ์บ”๋ฒ„์Šค ์ƒ์„ฑ ๋ฐ ๊ธฐ๋ณธ ์„ค์ •
  createCanvas(W, H);
  background(10); 
  
  // ์™ธ๊ณฝ์„ ๊ณผ ์ฑ„์šฐ๊ธฐ ์ƒ‰์ƒ ์„ค์ •
  stroke(255, 100, 150);     // ํ•‘ํฌ๋น› ์™ธ๊ณฝ์„ 
  strokeWeight(2);
  fill(255, 100, 150, 50);   // ๋ฐ˜ํˆฌ๋ช…ํ•˜๊ฒŒ ์ฑ„์šฐ๊ธฐ
}

function draw() {
  // 1. ๋ฐฐ๊ฒฝ์„ ๋ฐ˜ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ฎ์–ด ์ž”์ƒ ํšจ๊ณผ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  background(10, 20); 

  // 2. ์บ”๋ฒ„์Šค ์ค‘์•™์œผ๋กœ ์ขŒํ‘œ๊ณ„๋ฅผ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  translate(width / 2, height / 2); 

  // 3. ์ ˆ์ฐจ์  ํ˜•ํƒœ ์ƒ์„ฑ ํ•จ์ˆ˜ ํ˜ธ์ถœ
  drawProceduralShape();

  // 4. ์‹œ๊ฐ„ ๊ฒฝ๊ณผ์— ๋”ฐ๋ผ ๋…ธ์ด์ฆˆ ํ•„๋“œ(Z์ถ•)๋ฅผ ์ด๋™์‹œ์ผœ ํ˜•ํƒœ๋ฅผ ์—ฐ์†์ ์œผ๋กœ ๋ณ€ํ™”์‹œํ‚ต๋‹ˆ๋‹ค.
  timeOffset += 0.005;
}

/**
 * ์ˆ˜ํ•™์  ๋ฐ์ดํ„ฐ(๊ทน์ขŒํ‘œ)์™€ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ(๋…ธ์ด์ฆˆ)๋ฅผ ์ด์šฉํ•ด ํ˜•ํƒœ๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
 */
function drawProceduralShape() {
  beginShape();
  
  // ํด๋ฆฌ๊ณค์˜ ๋ชจ๋“  ์ ์„ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.
  for (let i = 0; i < numPoints; i++) {
    // 1. ๊ฐ๋„ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ: 0๋ถ€ํ„ฐ 360๋„(TWO_PI)๊นŒ์ง€ ๊ท ๋“ฑ ๋ถ„ํ• 
    let angle = map(i, 0, numPoints, 0, TWO_PI);

    // 2. ๋…ธ์ด์ฆˆ ์ž…๋ ฅ ์ขŒํ‘œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ (๊ทน์ขŒํ‘œ ๊ธฐ๋ฐ˜)
    // ๋…ธ์ด์ฆˆ ๊ณต๊ฐ„์„ ์›ํ˜•์œผ๋กœ ํƒ์ƒ‰ํ•˜๋ฉด์„œ, timeOffset(Z์ถ•)์„ ๋”ํ•ด ์›€์ง์ž„์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    let xoff = cos(angle) * noiseScale + timeOffset;
    let yoff = sin(angle) * noiseScale + timeOffset;

    // 3. ๋…ธ์ด์ฆˆ ๊ฐ’ ๋ฐ์ดํ„ฐ ํš๋“: ๋…ธ์ด์ฆˆ ๊ฐ’(0~1)์ด ๊ณง ๋ฐ˜์ง€๋ฆ„ ๋ณ€ํ˜•์˜ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.
    let noiseValue = noise(xoff, yoff);

    // 4. ๋ฐ˜์ง€๋ฆ„(r) ๋ฐ์ดํ„ฐ ๋ณ€ํ˜• ๋ฐ ์ •์˜ (์ ˆ์ฐจ์  ๋ชจ๋ธ๋ง)
    // ๋…ธ์ด์ฆˆ ๊ฐ’์„ -30์—์„œ +50 ํ”ฝ์…€ ๋ฒ”์œ„๋กœ ๋งคํ•‘ํ•˜์—ฌ ๊ธฐ๋ณธ ๋ฐ˜์ง€๋ฆ„(200)์— ๋”ํ•ฉ๋‹ˆ๋‹ค.
    let r = radius + map(noiseValue, 0, 1, -30, 50);

    // 5. ๊ทน์ขŒํ‘œ(r, angle)๋ฅผ ํ™”๋ฉด ์ขŒํ‘œ์ธ ์ง๊ต์ขŒํ‘œ(x, y)๋กœ ๋ณ€ํ™˜
    let x = r * cos(angle);
    let y = r * sin(angle);

    // 6. ์  ๋ฐ์ดํ„ฐ๋กœ ํ˜•ํƒœ ์ •์˜
    vertex(x, y);
  }
  endShape(CLOSE); // ํด๋ฆฌ๊ณค์˜ ์‹œ์ž‘์ ๊ณผ ๋์ ์„ ์—ฐ๊ฒฐํ•˜์—ฌ ํ˜•ํƒœ๋ฅผ ๋‹ซ์Šต๋‹ˆ๋‹ค.
}

๐Ÿ’ก ์ฝ”๋“œ ์ƒ์„ธ ์„ค๋ช…: ์ˆ˜ํ•™์ ยท๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์ œ์–ด
1. ๐Ÿ“ ๊ทน์ขŒํ‘œ๊ณ„๋ฅผ ํ†ตํ•œ ํ˜•ํƒœ ์ •์˜ (Generative Geometry)
์ด ์ฝ”๋“œ๋Š” ๋„ํ˜•์˜ ์œ„์น˜๋ฅผ ๊ทน์ขŒํ‘œ๊ณ„ (๋ฐ˜์ง€๋ฆ„ย r\text{๋ฐ˜์ง€๋ฆ„ } r, ๊ฐ๋„ย ฮธ\text{๊ฐ๋„ } \theta)๋ผ๋Š” ๋ฐ์ดํ„ฐ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ง๊ต์ขŒํ‘œ(x,yx, y)๋ณด๋‹ค ์›ํ˜• ํ˜•ํƒœ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ณ€ํ˜•ํ•˜๋Š” ๋ฐ ํ›จ์”ฌ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

  • ๊ฐ๋„ ๋ฐ์ดํ„ฐ: let angle = map(i, 0, numPoints, 0, TWO_PI);
    • numPoints๋Š” ๋„ํ˜•์„ ๊ตฌ์„ฑํ•˜๋Š” ์ ์˜ ๊ฐœ์ˆ˜๋กœ, ๋ฐ์ดํ„ฐ์˜ ํ•ด์ƒ๋„์ž…๋‹ˆ๋‹ค. ์ด ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ 00๋ถ€ํ„ฐ 360โˆ˜360^\circ(TWO_PI\text{TWO\_PI} ๋ผ๋””์•ˆ)๊นŒ์ง€ ๊ฐ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ท ๋“ฑํ•˜๊ฒŒ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์ขŒํ‘œ ๋ณ€ํ™˜: r๊ณผ angle์ด๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์Œ ๊ณต์‹์— ๋”ฐ๋ผ ํ™”๋ฉด์˜ ์ง๊ต์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ ์˜ ์ตœ์ข… ์œ„์น˜๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
    x=rโ‹…cosโก(angle)y=rโ‹…sinโก(angle)x = r \cdot \cos(\text{angle}) \\ y = r \cdot \sin(\text{angle})
  1. ๐ŸŒŠ ํผ๋ฆฐ ๋…ธ์ด์ฆˆ๋ฅผ ํ™œ์šฉํ•œ ์ ˆ์ฐจ์  ์ œ์–ด (Procedural Modeling)
    ๋„ํ˜•์˜ ํ˜•ํƒœ(๋ฐ˜์ง€๋ฆ„ rr)๋Š” ํผ๋ฆฐ ๋…ธ์ด์ฆˆ๋ผ๋Š” ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ์— ์˜ํ•ด ์ ˆ์ฐจ์ ์œผ๋กœ ์ œ์–ด๋ฉ๋‹ˆ๋‹ค. ๋…ธ์ด์ฆˆ ๊ฐ’์€ ํ˜•ํƒœ์˜ ๋ถˆ๊ทœ์น™ํ•œ ๋ณ€ํ™”๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•ต์‹ฌ ๋ฐ์ดํ„ฐ ์†Œ์Šค์ž…๋‹ˆ๋‹ค.
  • ๋…ธ์ด์ฆˆ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ (xoff, yoff):
    • ๋…ธ์ด์ฆˆ ํ•จ์ˆ˜(noise())๋Š” ์ž…๋ ฅ ์ขŒํ‘œ์— ๋”ฐ๋ผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๊ฐ ์ ์˜ ๊ฐ๋„ ฮธ\theta๋ฅผ ์ด์šฉํ•ด ๋…ธ์ด์ฆˆ ๊ณต๊ฐ„์„ ์›ํ˜•์œผ๋กœ ํƒ์ƒ‰ํ•˜๋„๋ก ์ž…๋ ฅ ์ขŒํ‘œ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    • noiseScale ๋ณ€์ˆ˜๋Š” ๋…ธ์ด์ฆˆ ์ž…๋ ฅ ์ขŒํ‘œ์˜ ๊ฐ„๊ฒฉ์„ ์ œ์–ดํ•˜์—ฌ, ๋„ํ˜•์˜ ๊ฑฐ์น ๊ธฐ๋‚˜ ์ž”์ž”ํ•จ๊ณผ ๊ฐ™์€ ์งˆ๊ฐ์„ ์ •์˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฐ˜์ง€๋ฆ„(r) ๋ฐ์ดํ„ฐ ๋ณ€ํ˜•:
    • noiseValue (0.0 ~ 1.0)๋ฅผ ํš๋“ํ•œ ํ›„, map() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๊ฐ’์„ ์›ํ•˜๋Š” ๋ฐ˜์ง€๋ฆ„ ๋ณ€๋™ ํญ์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ณ€ํ™˜๋œ ๊ฐ’์„ radius์— ๋”ํ•˜์—ฌ ๊ฐ ์ ์˜ ์ตœ์ข…r ๊ฐ’์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ์จ ์œ ๊ธฐ์ ์ด๊ณ  ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์™œ๊ณก์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  1. โ™ป๏ธ ์‹œ๊ฐ„์  ์‹œ์Šคํ…œ๊ณผ์˜ ๊ฒฐํ•ฉ (Temporal Systems)
    ์ •์ ์ธ ํ˜•ํƒœ ๋ฐ์ดํ„ฐ์— ์‹œ๊ฐ„์„ ๋„์ž…ํ•˜์—ฌ ๋™์ ์ธ ์›€์ง์ž„์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
  • ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ (timeOffset):
    • draw() ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜๋ณต๋  ๋•Œ๋งˆ๋‹ค timeOffset ๊ฐ’์ด ๋ฏธ์„ธํ•˜๊ฒŒ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์€ ๋…ธ์ด์ฆˆ ํ•จ์ˆ˜์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ž(Z์ถ•)์— ๋”ํ•ด์ง‘๋‹ˆ๋‹ค.
  • ํšจ๊ณผ: ๋…ธ์ด์ฆˆ์˜ Z์ถ•์„ ์ง€์†์ ์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ์€ ๋…ธ์ด์ฆˆ ํ•„๋“œ ์ „์ฒด๊ฐ€ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ํ๋ฅด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์‹œ๊ฐ์  ํšจ๊ณผ๋ฅผ ์ค๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋„ํ˜•์€ ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋…ธ์ด์ฆˆ ๊ฐ’์— ์˜ํ•ด ๊ณ„์‚ฐ๋˜์–ด ์‚ด์•„์žˆ๋Š” ๋“ฏ ๊ฟˆํ‹€๊ฑฐ๋ฆฌ๋Š” ํ˜•ํƒœ์˜ ๋ณ€ํ™”๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

โœ… ์˜ˆ์ œ A: ์‚ฌ์ธํŒŒ ๊ธฐ๋ฐ˜ ํ˜•ํƒœ ์ƒ์„ฑ (Procedural Curve)

let points = []; 
let amplitude = 100; // ํŒŒ๋™ ๋†’์ด
let detail = 300;    // ํฌ์ธํŠธ ๊ฐœ์ˆ˜

function setup() {
  createCanvas(800, 400);
  noFill();
  stroke(255);
  
  // ๐Ÿ“Œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋‹จ๊ณ„
  for (let i = 0; i < detail; i++) {
    let x = map(i, 0, detail, 0, width);
    let y = height / 2 + sin(i * 0.05) * amplitude;
    points.push({ x, y });
  }
}

function draw() {
  background(20);
  
  // ๐Ÿ“Œ ๋ฐ์ดํ„ฐ โ†’ ํ˜•ํƒœ ์‹œ๊ฐํ™”
  beginShape();
  for (let p of points) {
    vertex(p.x, p.y);
  }
  endShape();
}

โœ… ์˜ˆ์ œ B: ๋ฐ์ดํ„ฐ ๊ทธ๋ฆฌ๋“œ ๊ธฐ๋ฐ˜ Procedural Shape ์ƒ์„ฑ

๊ทธ๋ฆฌ๋“œ๋ฅผ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด๋กœ ๊ตฌ์„ฑํ•˜๊ณ , ๊ฐ ์…€์˜ ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธฐํ•˜ํ•™์  ํ˜•ํƒœ๋ฅผ ์ƒ์„ฑ.

// ์˜ˆ์ œ B: ๋ฐ์ดํ„ฐ ๊ทธ๋ฆฌ๋“œ โ†’ ๊ธฐํ•˜ ๊ตฌ์กฐ ์ƒ์„ฑ
let cols = 20;
let rows = 20;
let grid = [];

function setup() {
  createCanvas(600, 600);
  noStroke();

  // ๐Ÿ“Œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋‹จ๊ณ„
  for (let i = 0; i < cols; i++) {
    grid[i] = [];
    for (let j = 0; j < rows; j++) {
      grid[i][j] = noise(i * 0.2, j * 0.2); // 0 ~ 1 ๊ฐ’
    }
  }
}

function draw() {
  background(10);

  // ๐Ÿ“Œ ๋ฐ์ดํ„ฐ โ†’ ํ˜•ํƒœ ์‹œ๊ฐํ™”
  let cellW = width / cols;
  let cellH = height / rows;

  for (let i = 0; i < cols; i++) {
    for (let j = 0; j < rows; j++) {
      let val = grid[i][j];

      // ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ฅธ ์ƒ‰/ํฌ๊ธฐ ๋ณ€ํ™”
      let size = cellW * val;

      fill(val * 255, 150, 255);
      ellipse(
        i * cellW + cellW / 2,
        j * cellH + cellH / 2,
        size,
        size
      );
    }
  }
}

โœ… ์˜ˆ์ œ C: ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ฐ˜ Procedural Polygon

// ์˜ˆ์ œ C: ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ฐ˜ ์ ˆ์ฐจ์  ๋‹ค๊ฐํ˜•
let sides = 7;       // ๋ณ€ ๊ฐœ์ˆ˜ (๋ฐ์ดํ„ฐ)
let radius = 200;    // ๋ฐ˜์ง€๋ฆ„
let rotationSpeed = 0.01;

function setup() {
  createCanvas(600, 600);
  angleMode(RADIANS);
}

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

  rotate(frameCount * rotationSpeed);

  stroke(255);
  noFill();
  strokeWeight(2);

  beginShape();
  for (let i = 0; i < sides; i++) {
    let angle = TWO_PI * (i / sides);
    let x = cos(angle) * radius;
    let y = sin(angle) * radius;
    vertex(x, y);
  }
  endShape(CLOSE);
}
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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