소리꽃 KeyBloom TIL 1
소리꽃을 시작한 첫날. 화면을 영상으로 뽑고, 파티클을 많이 그리고, 여러 모션을 만드는 뼈대를 잡았다.
시작할 때 정하는 것
우회를 정하기 전에 표준으로 되는지 본다
화면에서 도는 파티클을 영상 파일로 뽑아야 하는데, 처음엔 OBS 같은 화면 녹화 프로그램을 따로 쓰게 할 생각이었다. 사용자에게 프로그램을 하나 더 깔라고 하는 것이라 마음에 안 들었지만 방법이 없다고 봤다.
찾아보니 브라우저에 이미 표준 기능이 있었다. 캔버스(그림을 그리는 화면 영역)를 영상 스트림으로 뽑는 captureStream과, 그 스트림을 파일로 녹화하는 MediaRecorder다. 서버도 외부 라이브러리도 없이 됐다.
시작 단계에서 “이건 안 되니까 우회하자”를 빨리 정하면 나중에 되는 방법을 알고도 못 바꾼다. 그 우회 위에 다른 것들이 쌓여 있기 때문이다. 제약을 전제로 설계를 굳히기 전에 한 번은 확인하고 넘어가는 게 싸다.
한계를 알고 미루는 것과 모르고 두는 것은 다르다
이 방식에는 한계가 있었다. 재생을 실시간으로 지켜보며 녹화하는 것이라, 도중에 컴퓨터가 버벅이면 그게 영상에 그대로 남는다.
초기 버전에는 충분하다고 보고 넘어갔다. 대신 “왜 이걸 골랐고 무엇이 아쉬운지”를 남겨 뒀다. 나중에 결과물 품질이 중요해졌을 때 다시 이 지점으로 돌아와 방식을 바꾸기로 했다.
빠르게 만들 때 아쉬운 선택을 하는 건 문제가 아니다. 문제는 그게 아쉬운 선택인 줄 모르고 넘어가는 것이다. 알고 미루면 나중에 되돌아올 지점이 되고, 모르고 두면 나중에 원인 불명의 증상이 된다.
많이 그릴 때
같은 그림을 매번 새로 만들지 않는다
파티클 개수를 올리니 화면이 느려졌다. 파티클을 ●■♥★ 같은 글자 기호로 그리고 있었는데, 글자는 하나 그릴 때마다 글꼴에서 외곽선을 꺼내 그 크기의 픽셀로 계산하는 일(래스터화)이 일어난다. 하나면 티가 안 나는데 수천 개를 매 프레임 반복하면 그게 쌓인다.
모양을 미리 한 번 그려 둔 그림 조각, 곧 스프라이트로 만들어 두고 그걸 복사해 붙이게 바꿨다. 만드는 일이 한 번으로 줄고 그다음은 옮겨 붙이기만 한다.
“매번 만들 것인가 한 번 만들고 재사용할 것인가”는 개수가 정한다. 몇 개면 매번 만드는 게 단순하고, 수천 개면 재사용이 압도적이다. 다만 재사용에는 “언제 다시 만들 것인가”라는 문제가 딸려 온다는 걸 같이 봐야 한다.
연속된 값은 단계로 나누면 미리 만들 수 있다
스프라이트를 미리 구우려면 종류가 정해져 있어야 하는데, 파티클 색은 그라데이션 위에서 제각각이라 종류가 무한했다.
색을 24단계로 끊었다. 연속된 값을 정해진 개수의 단계로 나누는 걸 양자화라고 한다. 24개를 미리 굽고 각 파티클은 자기 색에 제일 가까운 것을 고른다. 눈으로 보면 부드럽게 이어져 보인다.
무한한 것을 미리 만들 수 없을 때, 사람이 구분하지 못하는 선까지 단계를 나누면 유한해진다. 몇 단계가 충분한지는 계산이 아니라 눈으로 정한다.
갈래를 나누기 전에
공통으로 환원해 본다
파티클 모션이 여섯 가지였다. 피어오름, 방사형, 흐트러짐, 나선, 분수, 불꽃. 처음에는 종류마다 움직이는 코드를 따로 짤 뻔했다.
그런데 뜯어 보니 전부 같은 값들로 표현됐다. 속도, 중력, 항력(속도에 비례해 감속시키는 저항), 좌우 흔들림, 수명. 종류가 다른 게 아니라 이 값들이 다른 것이었다. 그래서 움직임을 계산하는 부분은 하나만 두고 — 매 프레임 위치를 조금씩 갱신하는 이런 계산을 적분기라고 부른다 — 종류마다 넣을 값만 다르게 주게 했다.
새 모션을 추가하는 게 값 한 벌을 더하는 일이 됐다. 나선을 위해 흔들림이 시간에 따라 커지는 값을 하나 추가했는데, 다른 모션은 그 값이 0이라 아무 영향이 없었다.
갈래가 여러 개 보일 때 각각 만들기 전에 “이것들이 같은 것의 다른 값은 아닌가”를 한 번 물어야 한다. 맞으면 앞으로 늘어날 갈래가 전부 공짜에 가까워진다. 아니면 그때 갈라도 늦지 않다.
나중을 위한 단위
저장은 해상도와 무관한 단위로, 픽셀은 마지막에
지금은 화면 비율이 16:9 하나지만 나중에 세로 9:16이나 정사각형으로도 뽑고 싶었다. 위치를 화면 픽셀로 저장하면 비율이 바뀌는 순간 전부 어긋난다.
그래서 모든 위치와 크기를 0~1 사이 비율로 저장하고, 실제 픽셀은 그릴 때만 화면 크기를 곱해 낸다. 이렇게 해상도에 안 매인 좌표를 정규화 좌표라고 한다. 데이터는 그대로 두고 화면 크기만 바꿔 다시 곱하면 어떤 비율에서도 같은 그림이 나온다.
같은 판단을 전에 여둘까에서도 했다. 가구 배치를 밀리미터로 저장하고 픽셀은 그릴 때 환산한 것이다. 결국 규칙은 하나다 — 저장하는 단위는 화면에 안 매이게 두고, 화면 단위로 바꾸는 일은 마지막 한 곳에서만 한다.
이 판단은 처음에 하면 거의 공짜인데 나중에 하면 데이터를 전부 옮겨야 한다. 시작할 때 “이 값이 화면에 매여 있나”를 한 번 보는 게 좋다.
밖에 안 물어보고 만들 수 있으면 원칙이 지켜진다
반주 파일을 안 넣어도 타이밍 확인용 소리가 나면 좋겠는데, 진짜 악기 소리를 쓰려면 음원 파일을 어디선가 받아 와야 한다. 이 앱은 밖에 아무것도 안 물어보고 혼자 도는 걸 원칙으로 삼았다.
브라우저 표준인 Web Audio의 오실레이터(파형을 직접 만들어 소리를 내는 발진기)로 간단한 음을 만들어 썼다. 진짜 악기 소리는 아니지만 타이밍을 확인하는 데는 충분했다.
원칙을 세워 두면 이런 자리에서 판단이 빨라진다. “밖에 안 물어본다”를 정해 두니 음원을 받아 오는 선택지가 애초에 후보에서 빠지고, 그 안에서 되는 방법을 찾게 된다. 제약이 오히려 결정을 쉽게 만든다.
요약
- 제약을 전제로 설계를 굳히기 전에 표준으로 되는지 한 번 확인한다
- 아쉬운 선택을 하는 건 괜찮다. 아쉬운 줄 모르고 넘어가는 게 문제다
- 몇 개냐가 “매번 만들기”와 “한 번 만들고 재사용”을 가른다
- 무한한 값은 사람이 구분 못 하는 선까지 나누면(양자화) 유한해진다
- 갈래를 각각 만들기 전에 “같은 것의 다른 값은 아닌가”를 묻는다
- 저장 단위는 화면에 안 매이게(정규화 좌표), 화면 단위 변환은 마지막 한 곳에서
- 원칙을 세워 두면 후보가 줄어 판단이 빨라진다