BVH 포맷 이해하기
1. 모티베이션
VMC Mixer 까지는 만들었는데, 이 신호를 모션캡쳐 파일로 저장해줄만한 괜찮은 소프트웨어가 없었습니다. BVH 저장이 가능한 일반 소프트웨어들은 Hips 부터 시작해서 Root 본이 누락되어 있었고(그래서 정작 중요한 캐릭터의 위치가 잡히지 않습니다), 블렌더 애드온인 VMC4B는 각 내삽에 오일러 각을 사용하는지 자주 뒤집히며 불완전했습니다.
깃허브에 VMCtoBVH 소프트웨어가 있었지만, .exe 파일이라서 맥에서는 기록이 불가능했습니다.
어쩔 수 없이 BVH 포맷에 대해 찾아보았고, 이정도면 만들기 수월하겠는데? 싶어서 결국 직접 만드는 데까지 이르게 되었습니다.
2. BVH 포맷
BVH 파일의 생김새입니다.
2.1 HIERARCHY
관절 목록이 적혀 있는 부분입니다.
맨 앞의 관절은 ROOT, 그 다음부터는 쭉 JOINT로 이어져 있습니다.
각 JOINT에는 오프셋과 채널 목록이 적혀 있는데, 오프셋은 그 joint의 크기를 결정한다고 봐도 될 것 같고, 채널 목록은 후술할 모션을 받아들이는 채널입니다.
주로 로테이션 값이 지정되어 있으며, 루트 본만 6채널로 해서 따로 포지션을 저장할 수 있게 되어 있습니다.
이 채널 목록의 순서가 매우 중요합니다!
2.2 MOTION
먼저 첫줄에는 프레임 숫자, 두번째 줄에는 프레임당 몇 초를 소요할 것인지(그래서, 0.0333334 라면 초당 30프레임을 뜻합니다) 적혀 있습니다.
세번째 줄부터는 실제 모션이 들어가는데, 앞에서 선언한 채널의 순서대로 값을 지정할 수 있습니다. 한 줄에 채널 전체 숫자만큼 숫자가 들어가며, 다음 프레임으로 넘어갈 때 줄바꿈이 들어갑니다.
제가 받은 파일에서는, 소수점 아래 6자리까지 지정되어 있고, (–) 부호가 붙지 않은 경우 스페이스가 하나 들어가 있어서 그것도 똑같이 구현했습니다.
참고로, 이거 꽤 오래된 포맷이라고 합니다. 그래서인지, 시간 단위는 초, 각도 단위는 degree 입니다.
3. 쿼터니온을 BVH degree로 변환하기
이건, vmc2bvh 리포지토리의 코드 에서 가져왔습니다.
원래는 Quaternion.js의 함수를 사용하려고 했는데, 오일러 변환 중 XYZ의 순서를 어떻게 지정해야 할지 잘 모르기 때문에 기존의 성공적인 코드를 빌려올 수밖에 없었습니다...
function qtod_rot(r11, r12, r21, r31, r32) {
F = (180.0 / Math.PI);
// zyx
var zrot = Math.atan2(r31, r32) * F;
var yrot = Math.asin(r21) * F;
var xrot = Math.atan2(r11, r12) * F;
return [xrot, yrot, zrot]
}
function qtod(x, y, z, w) {
var angles = qtod_rot(-2 * (y * z - w * x), w * w - x * x - y * y + z * z, 2 * (x * z + w * y), -2 * (x * y - w * z), w * w + x * x - y * y - z * z);
return angles
}
원본은 위키백과에 있는 다음 식인 것 같습니다.
(Y값 구하는 데 두번째 공식 사용)
참고로 이 포맷 계산에서는 원본 쿼터니온에서 Qx와 Qy에 -1을 곱한 값을 사용하며, (아마 cw와 ccw의 차이일 것 같습니다.) 루트 계산에서는 이 쿼터니온 값을 제곱한 새로운 쿼터니온을 사용하더군요.
이걸 구한 다음 Xrotation Yrotation Zrotation 순서대로 파일에 저장해 주었습니다.