Hyun's Wonderwall

[p5.js] 모션그래픽을 위한 p5.js 연습 프로젝트 본문

Study

[p5.js] 모션그래픽을 위한 p5.js 연습 프로젝트

Hyun_! 2024. 3. 9. 00:10

나는 현대 미술/음악 작품들에 흥미가 많다. 프롬프트 엔지니어링을 활용한 체험형 전시, 사이버틱한 인터랙티브 아트, 공연의 분위기를 최대한으로 끌어 올리는 브이제잉(VJing)에도 관심이 있다. 전시를 보러 다니며 언젠가 나도 미디어 아트를 만들어보고 싶다는 생각을 했었는데, 최근 가상현실 수업을 수강하다가 문득 '한번 해 볼까?'하는 용기가 생겼다.  어떤 툴로 미디어 아트를 만들 수 있는지 찾아보았다.
 
내가 찾은 툴은 p5.jsTouchDesigner다.
- TouchDesigner는 노드 연결 방식으로 코딩을 하는 것 같다.(언리얼 블루프린트 느낌으로) 아두이노랑 함께 쓰는 사례를 많이 볼 수 있었다. 3D 작업에 좋은 것 같다. 그러나 뭔가 고사양 컴퓨터를 요구할 것 같고 콜로소에 있는 강의가 너무 비싸서 패스했다. 나중에 규모 있는 프로젝트 아이디어가 떠오른다면 해보고 싶다.
- p5.js는 공식 코드 예제 사이트와 생활코딩 무료 강의가 있었다. 생활코딩 강의는 짧아서 슉슉 참고할 부분만 넘겨 봤고 공식 사이트에서 여러 예제를 구경해 보며 문법을 익혔다. html과 아주 조금  js 공부를 해 보았던 기억을 되살려보고, 챗지피티의 도움도 조금 받았다.
https://p5js.org/ko/examples/
 
프로젝트 세팅 방법은 다음과 같다.
- vscode에 p5.js 확장 프로그램 설치
- index.html 작성
- js 파일에 미디어 아트를 할 코드 작성
- 익스텐션 Live Server 켜기 (http://127.0.0.1:5500/에서 볼 수 있다)

첫 연습 결과물 영상

  • 제목 : 말의 선율
  • 기획 배경 : 싱어송라이터 윤지영의 노래들을 듣다가 흘러가듯 떠오른 아이디어를 발전시켜 보았다
  • 기획 과정 : 비눗방울 -> 인어공주? 타자기, 모스 부호 -> 악보 -> 마음의 연주
  • 멋있게 써본 설명 : 키보드를 입력할 때마다 생성되는 원은 내뱉어지는 말소리를, 각 원의 색깔은 말소리들로부터 나타나지는 다양한 감정을 의미한다. 감정마다 깊이가 달라 원의 크기 또한 다르다. 여러 색깔과 크기의 원들이 떠오른 화면은 복합적인 마음을 의미한다. 단어를 구분하는 스페이스바를 흰색의 마디로써 삽입하여 말을 마음의 연주가 담긴 악보로 재해석했다.
  • 상호작용 방법 : 키보드 입력 - 키보드의 알파벳 부분을 입력 시, 입력된 키 코드에 따라 계산된 x좌표 위치에 랜덤 색상&크기의 원을 생성한다. 스페이스 바를 누르면 마디가 구분되듯이 직사각형을 생성한다.

(테두리를 넣지 않고 빨리 이동시키면 그림을 다시 그릴 때 잔상이 생겨버려서 테두리를 넣었다. 추후 개선된 방법을 찾을 수 있길...)

<!-- index.html 파일 -->
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.9.1/lib/p5.js"></script>
    <script src="p5js_trial.js"></script>
  </head>
  <body>
    <main>
    </main>
  </body>
</html>

 

// p5js_trial.js

let pictures = []; // 그림을 저장할 배열
let squares = [];
let bgcolor = 0;

function setup() {
    createCanvas(1200, 600);
    background(bgcolor);
    stroke(bgcolor);
    strokeWeight(3);
}

function draw() {
    for (let i = 0; i < pictures.length; i++) {
        let picture = pictures[i];

        // 시간 체크
        if (millis() - picture.startTime >= 4000) { // 시간이 지났으면
            // console.log("pop");
            fill(bgcolor);
            circle(picture.x, picture.y, picture.size);
            pictures.splice(i, 1); // 배열에서 그림 제거 
            i--; // 배열 인덱스 조정
        } else {
            picture.y = picture.y - 2.0;
            picture.drawCircle();
        }
    }
    for (let i = 0; i < squares.length; i++) {
        let square = squares[i];

        // 시간 체크
        if (millis() - square.startTime >= 4500) { // 시간이 지났으면
            // console.log("pop");
            fill(bgcolor);
            rect(square.x, square.y, width, square.size);
            squares.splice(i, 1); // 배열에서 그림 제거 
            i--; // 배열 인덱스 조정
        } else {
            square.y = square.y - 2.0;
            square.drawSquare();
        }
    }
        // 색깔이 그라데이션으로 페이드아웃되면 좋을 텐데
        /*let initcolor = pictures[i].color;
        if (millis() - picture.startTime <= 2000) {
            console.log("pop");
            const interA = // 여기서 보간법 쓰면 될 거 같은데 잘 모르겠다
            pictures[i].color = fill(interA);
            picture.drawCircle();
        }
        if (initcolor == 220) {
            pictures.splice(i, 1); // 배열에서 그림 제거
            i--; // 배열 인덱스 조정
        }*/
}

function keyPressed() {
    let keyIndex = -1;
    if (key >= 'a' && key <= 'z') {
        keyIndex = key.charCodeAt(0) - 'a'.charCodeAt(0);
    }
    if (key == ' ') {
        console.log("pop");
        let picture = createSquare(0, 500, 10);
        squares.push(picture);
        picture.drawSquare();
    }
    else if (keyIndex === -1) {
        // 글자 자판이 아닐 경우
    } else {
        // 글자 자판일 경우, 랜덤한 색상의 비눗방울
        // let x = random(width);
        // let y = random(height);
        let size = random(8, 180);
        let randFill_r = Math.floor(Math.random() * 255 + 1);
        let randFill_g = Math.floor(Math.random() * 255 + 1);
        randFill_b = Math.floor(Math.random() * 255 + 1);
        // keyIndex를 바탕으로 가로 위치
        let picture = createPicture(40 + keyIndex*45, 520, size, randFill_r, randFill_g, randFill_b); // 그림 생성 함수 호출
        pictures.push(picture); // 배열에 그림 추가
        picture.drawCircle();
    }
}

// circle 생성 함수
function createPicture(x, y, size, randFill_r, randFill_g, randFill_b) {
    let picture = {
        x: x,
        y: y,
        size: size,
        randFill_r: randFill_r,
        randFill_g: randFill_g,
        randFill_b: randFill_b,
        startTime: millis(), // 생성 시간 기록
        drawCircle: function () {
            // 그림 그리기
            fill(randFill_r, randFill_g, randFill_b);
            circle(this.x, this.y, this.size);
        }
    };

    return picture;
}

// square 생성 함수
function createSquare(x, y, size) {
    let square = {
        x: x,
        y: y,
        size: size,
        startTime: millis(), // 생성 시간 기록
        drawSquare: function() {
            fill(255);
            rect(this.x, this.y, width, size);
        }
    };

    return square;
}

추후 국문이나 영문 문법적인 요소를 이용해서 변형해보고 싶다. 자음은 네모 모음은 동그라미 라든지...(쉽게 가능할 것 같은데 오늘은 여기까지...!)
한번 연습 프로젝트를 만들어보니 미디어 아트를 만드는 원리를 조금이나마 이해하게 된 것 같다.

'Study' 카테고리의 다른 글

[GDSC Ewha 5th] 리팩토링 세션  (0) 2024.03.13