연구 블로그

357개 정점을 매 프레임 CPU로 움직이기

컴퓨터 비전2026.07.08 · 4분 읽기
357개 정점을 매 프레임 CPU로 움직이기

본편("리그가 없는 그림을 웹캠 포즈로 움직이기")에서는 왜 그런 구조를 선택했는지를 다뤘다. 이 글은 그 구현편이다. 본편에서 걷어냈던 수치와 공식이 여기 있다.

관절은 평균으로 합성한다

포즈 모델이 직접 주지 않는 관절은 주변 랜드마크의 평균으로 만들었다. hips는 좌우 골반, chest는 좌우 어깨, neck은 좌우 어깨와 코를 평균해 만든다. head는 코와 양 눈, 양 귀 다섯 점의 평균이고, spine은 hips와 chest의 중점이다. 손은 손목·검지·새끼·엄지, 발은 발목·뒤꿈치·발끝의 평균이다. 신뢰도는 [0, 1]로 클램핑하고, 모델이 신뢰도를 주지 않는 합성 관절에는 기본값 0.6을 부여했다.

메시와 가중치

메시는 캐릭터 영역을 덮는 16×20 그리드다. 정점은 17×21로 357개, 사각형 320개를 삼각형 640개로 나눈다. 각 정점은 가장 가까운 관절 4개를 고르고 거리의 역제곱으로 가중치를 받는다. weight_i = 1/(dist_i² + ε), ε = 1e-6, 합이 1이 되도록 정규화한다. ε는 정점이 관절과 겹쳐 거리가 0에 수렴할 때 분모가 폭발하는 걸 막는 안전장치다.

자료구조는 다섯 개, 전부 18KB

정점당 (jointIndex, weight) 쌍을 정확히 4개로 고정한 덕에 자료구조가 평탄해진다. restVertices(Float32Array)와 uvs(Float32Array), jointIndices(Uint8Array), weights(Float32Array), indices(Uint16Array) 다섯 개가 전부고, 지오메트리 전체가 약 18KB다. 이 크기가 본편의 결론, "셰이더로 넘길 필요도 없을 만큼 작다"의 근거다.

회전은 참조 벡터로 구한다

각 관절의 회전은 현재 자세와 rest 자세의 참조 벡터 각도 차로 계산한다. 회전각은 atan2(현재 참조벡터) − atan2(rest 참조벡터), 스케일은 |현재| / |rest|다. 참조 관절은 후보 리스트에서 신뢰도가 기준(0.3) 이상인 것을 고르고, 쓸 만한 게 없으면 부모 관절로 폴백한다. 예를 들어 hips의 참조 후보는 spine과 좌우 골반이다.

LBS 본체는 가중 합산이다

LBS 자체는 복잡한 알고리즘이 아니다. 매 프레임, 정점마다 영향 관절 4개의 변형(회전·스케일·이동)을 가중 합산하는 계산이 전부다. 2D 회전이라 한 관절의 기여는 x' = pos.x + cos·dx − sin·dy, y' = pos.y + sin·dx + cos·dy로 끝난다. 357개 정점에 영향 관절 4개씩이면 프레임당 1,428번의 가중 합산이고, JS 루프로 돌려도 실시간에 여유가 있다.

정리

구현의 모든 선택이 한 방향을 가리킨다. 관절 수를 줄이고, 영향 관절을 4개로 고정하고, 자료구조를 평탄하게 유지한 덕에 파이프라인 전체가 JS 한 곳에 남을 수 있었다. 이 정도 규모에서는 GPU로 보내기 위한 복잡성을 더하는 것보다 CPU에서 한 번에 처리하는 편이 나았다. 규모가 작을 때 단순함은 성능보다 값지다.

#LinearBlendSkinning#스키닝#실시간렌더링
357개 정점을 매 프레임 CPU로 움직이기4분 읽기 · 0%