p5.js를 활용해서 웨이브 만들기🌊

Jinho Park·2023년 4월 27일
0

creativeCoding

목록 보기
1/1

계기

 최근 저의 진로에 대해 많은 고민 끝에 지금 하고 싶은 공부를 하자는 다짐하게 되었습니다! 지금 제가 하고 싶은 공부는 크리에이티브 코딩, 인터렉티브 디자인 등등 여러 이름으로 불리고 있는 분야입니다.

이 분야를 처음 알게 된 건 현재 구글에 UX Egineer로 계신 Interactive Developer 김종민님 때문입니다. HTML5 canvas를 이용해 많은 인터렉티브한 작품을 봤습니다. (정말 작품이라고 표현해야 할 정도의 임팩트였습니다.)

당시에 막연하게 나도 이런 일을 하고 싶다고 생각했습니다. 그때의 기억을 되살려 다시 김종민 님의 유튜브를 들어가 기본적인 도형을 활용한 작품을 보고 있었습니다. 그러다 최근 제가 공부한 p5.js라는 라이브러리로 이 작품들을 다시 만들어 보면 좋곘다 라는 생각이 들었습니다.

사실 처음부터 큰 규모의 창의적인 작품은 만들기 어려워 기본부터 다지고 싶었습니다. 거기에 딱 맞는 연습인 것 같아 바로 만들기 시작했습니다. 오늘은 위에 사진과 같이 Wave를 만들어 보겠습니다~!!

움직이는 원 만들기

 서론이 생각 보다 길었네요..

우선 위아래로 움직이는 원하나를 만들어 줍니다.

캔버스의 크기를 window의 크기로 맞춰주고, 공이 위아래로 움직여야 하기 때문에 주기함수인

sin 함수를 이용해 공이 위아래로 움직이게 합니다.

let y1 = 0;
let speed = 0.1;
let cur = 0; //현재 각도
let max = Math.random() * 100 + 150; //y의 최댓값을 랜덤으로 잡음

function setup() {
  createCanvas(windowWidth, windowHeight); //window크기만큼 창 설정
}

function draw() {
  background(220);
  cur = cur + speed;

  y1 = height + sin(cur) * max - windowHeight / 2; // sin 함수를 사용하여 y좌표 값 계산

  ellipse(width / 2, y1, 50, 50); // 계산된 y좌표 값으로 원을 그림
}

위아래로 움직이는 원 완성!

점 추가하기

 점을 여러 개 추가해 줍니다. 변수 cur를 배열로 만들어 각각 원마다 시작 각도를 다르게 설정해 줍니다.

그리고 map 함수를 통해서 각각 배열의 공 각도를 speed만큼 계속 더해줍니다.

let y1 = 0;
let y2 = 0;
let y3 = 0;
let y4 = 0;
let y5 = 0;
let speed = 0.1;
let cur = []; //현재 각도를 원마다 다르게 하기 위해 배열로 변경
let max = Math.random() * 100 + 150; //y의 최댓값을 랜덤으로 잡음

function setup() {
  createCanvas(windowWidth, windowHeight); //window크기만큼 창 설정
}

function draw() {
  background(220);
  //현재 배열 상태 cur = [0,1,2,3] 즉 4개의 공 생성
  for (let i = 0; i < 4; i++) {
    cur.push(i); 
  }
  
  // 각각 공을 speed만큼 계속 더해줌
  cur = cur.map(function (currentValue) {
    return currentValue + speed;
  }); 
  
  // sin 함수를 사용하여 y좌표 값 계산
  y0 = height - windowHeight / 2;
  y1 = height + sin(cur[0]) * max - windowHeight / 2; 
  y2 = height + sin(cur[1]) * max - windowHeight / 2;
  y3 = height + sin(cur[2]) * max - windowHeight / 2;
  y4 = height + sin(cur[3]) * max - windowHeight / 2;
  y5 = height - windowHeight / 2;
  
  // 계산된 y좌표 값으로 원을 그림
  ellipse((width / 5) * 0, y0, 50, 50); 
  ellipse((width / 5) * 1, y1, 50, 50); 
  ellipse((width / 5) * 2, y2, 50, 50);
  ellipse((width / 5) * 3, y3, 50, 50);
  ellipse((width / 5) * 4, y4, 50, 50);
  ellipse((width / 5) * 5, y5, 50, 50); 
}

총 6개의 공을 만들었습니다. (위 코드는 하드코딩이라 코드가 더러울 수 있습니다.🤣)

점을 선으로 잇기

 이제 각각의 원들의 좌표를 가지고 line 함수를 사용해 선으로 이어줍니다.

let y0,y1,y2,y3,y4,y5 = 0;

let speed = 0.1;
let cur = []; //현재 각도를 원마다 다르게 하기 위해 배열로 변경
let max = Math.random() * 100 + 150; //y의 최댓값을 랜덤으로 잡음

function setup() {
  createCanvas(windowWidth, windowHeight); //window크기만큼 창 설정
}

function draw() {
  background(220);
  //현재 배열 상태 cur = [0,1,2,3] 즉 4개의 공 생성
  for (let i = 0; i < 4; i++) {
    cur.push(i); 
  }
  
  // 각각 공을 speed만큼 계속 더해줌
  cur = cur.map(function (currentValue) {
    return currentValue + 0.05;
  }); 

  // sin 함수를 사용하여 y좌표 값 계산
  y0 = height - windowHeight / 2;
  y1 = height + sin(cur[0]) * max - windowHeight / 2; 
  y2 = height + sin(cur[1]) * max - windowHeight / 2;
  y3 = height + sin(cur[2]) * max - windowHeight / 2;
  y4 = height + sin(cur[3]) * max - windowHeight / 2;
  y5 = height - windowHeight / 2;

  // 각 점 사이에 곡선 그리기
  line((width / 5) * 0, y0, (width / 5) * 1, y1);
  line((width / 5) * 1, y1, (width / 5) * 2, y2);
  line((width / 5) * 2, y2, (width / 5) * 3, y3);
  line((width / 5) * 3, y3, (width / 5) * 4, y4);
  line((width / 5) * 4, y4, (width / 5) * 5, y5);
}

이상하지만 제법 웨이브의 모양이 나오고 있습니다!

직선을 곡선으로 바꾸기

 제가 찍은 점의 개수는 6개입니다. bezier함수는 4개의 좌표를 파라미터로 받습니다.

아래의 그림을 참고하시면 (제가 그렸습니다..하핫) 파란 점 끝 두 점을 기준으로 가운데 두 점이 직선을 위 아래로 휘게 만든다고 보면 됩니다.

sin 그래프 모양을 위해서 양 끝점에서의 접선을 찾아서 가운데 4개의 점에서 각각 2개의 중점을 잡아서 총 4개의 점으로 bezier곡선을 그렸습니다.

 코드에서 중요한 부분은 Wave 함수입니다. 아까 말한 대로 bezier함수를 사용했는데, 좌표를 움직이기 위해 bezierVertex함수를 사용했습니다.

그 외에는 위에서 하드코딩 했던 부분을 for 문으로 바꿨습니다.

Wave 객체는 index라는 파라미터를 받는데 이건 각 웨이브를 구분하기 위해 설정했습니다.

이렇게 하면 원하는 색상의 웨이브가 생깁니다!

let speed = 0.1;
let cur = [];
let y = [];
let max = Math.random() * 100 + 250;
let n1 = 0;
let n2 = 0;
let counter = 3;

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

function draw() {
 background(220); //배경 설정

 for (let i = 0; i < counter + 2; i++) {
   cur.push(i);
 } //각 점의 각도, i는 지금 기준 웨이브가 3개이고, 3개의 처음 높이가 0123 1234 2345 이런 식 일 때 마지막 5가 i값

 cur = cur.map(function (currentValue) {
   return currentValue + speed;
 }); //각각 각도를 speed만큼 증가

 endY = height / 2; //양 끝점 y좌표

 for (let j = 0; j < counter; j++) {
   for (let h = 0; h < 4; h++) {
     //점의 개수 = 4개
     if (h % 2 == 0) {
       y.push(endY + sin(cur[j + h]) * (max + 300));
     } else {
       y.push(endY + sin(cur[j + h]) * max);
     }
   }
 } //이중 for문으로 0123 1234 2345로 각 웨이브의 처음 시작 y를 다르게 설정한다. 그리고 각점이 사인 함수로 위아래로 움직인다

 color = ["rgba(0,199,235,0.4)", "rgba(0,146,199,0.4)", "rgba(0,87,158,0.4)"]; //시작은 0번째로 시작

 function Wave(index) {
   cx1 = ((width / 5) * 3) / 2; //bezierVertex()를 사용하기 위애 점 두 개를 1, 2번째 점의 중점의 x값을 사용
   cx2 = ((width / 5) * 5) / 2;
   n1 = 4 * (index-1);
   n2 = 2 + 4 * (index-1);
   fill(color[index-1]);
   smooth();
   beginShape();
   vertex((width / 5) * 0, endY);
   bezierVertex(cx1, y[n1], cx2, y[n2], (width / 5) * 5, endY);
   vertex((width / 5) * 5, height); // 오른쪽 아래 코너 생성으로 채움
   vertex((width / 5) * 0, height); // 왼쪽 아래 코너 생성 으로 아래 부분 채움
   endShape(CLOSE);
 }

 Wave(1); //객체 생성법 counter 하나 늘림, 색상 넣기, 객새 생성
 Wave(2);
 Wave(3);

 y = []; //배열 초기화
}

addEventListener("resize", () => {
 setup();
});

짜잔! 그러면 이렇게 예쁜 모양의 웨이브가 만들어졌습니다!!

(wave 색상은 여기를 참고했습니다.)

마치며

 사실 마지막 직선에서 곡선으로 바꿀 때 굉장히 시행착오가 있었습니다. 여러 함수를 써봤는데, 제가 원하는 곡선이 나오질 않더군요..

사실 bezier의 개념도 정확하지 않아서 하면서도 맞나 했지만 혼자 계산하면서 결국엔 완성했습니다!

굉장히 간단해 보이는 모션이었는데, 혼자 만들려고 보니 어려웠던 것 같습니다.

만들면서 수학 관련 지식이 많이 필요할 것 같다는 생각이 들었습니다.

제가 우선 부딪혀 보는 스타일이라 우선 어떻게든 만들었는데, 만들면서 애매하거나 몰랐던 개념을 다 끝나도 공부하면 좀 더 기억에 오래 남아 이렇게 공부하고 있습니다!

p5.js로 뭘 만든 건 처음인데 아직 안 써본 기능도 많고, 공부하면 정말 다양한 걸 만들 수 있을 것 같습니다.

오늘 p5.js로 웨이브 만들기 끝!

profile
프론트엔드 개발자

0개의 댓글