쿼터니언을 오일러각으로 변환
관련링크
본문
쿼터니언(사원수) 회전이 짐벌락이 없고 매우 효율적인건 분명하나
모든 프로그래머가 쿼터니언을 수학적인 이해를 바탕으로 사용하는건 아닙니다.
특히나 일반적인 상황에서는 오일러 방식이 이해하기 단순합니다.
그래서 내부적으로는 쿼터니언을 쓰고 외부에서는 오일러 개념으로만 운용하는 클래스를 만들어 보고 싶었습니다.
언리얼 엔진에서 차용하는 방식인데 Rotator 클래스에서 Yaw, Pitch, Roll 로 제어를 하지만
내부에서는 쿼터니언을 사용해서 계산합니다.
그리고 그렇게 계산된 Rotation을 언제고 Yaw, Pitch, Roll 오일러 각으로 분해하는것을 보고
저도 해보고 싶어서 쿼터니언을 오일러각으로 변환하고 싶었습니다만...
당췌 키워드를 뭘 찾아야 할지도 모르겠고 의외로 관심이 없어서 놀랐습니다...
둘중 하나인거 같은데
1. 당연한걸 왜 굳이...
2. 나만 못찾는거..
여튼 거의 하루종일 찾은거 같습니다.
답은 찾았는데 왜 이렇게 돌아가는지는 잘 모르겠습니다....
여튼 한 유저가 찾은 방법이고 착안은 유니티에서 .ToEuler() 펑션에서 한것으로알고 있습니다.
public static Quaternion ToQ (Vector3 v) { return ToQ (v.y, v.x, v.z); }
public static Quaternion ToQ (float yaw, float pitch, float roll) { yaw *= Mathf.Deg2Rad; pitch *= Mathf.Deg2Rad; roll *= Mathf.Deg2Rad; float rollOver2 = roll * 0.5f; float sinRollOver2 = (float)Math.Sin ((double)rollOver2); float cosRollOver2 = (float)Math.Cos ((double)rollOver2); float pitchOver2 = pitch * 0.5f; float sinPitchOver2 = (float)Math.Sin ((double)pitchOver2); float cosPitchOver2 = (float)Math.Cos ((double)pitchOver2); float yawOver2 = yaw * 0.5f; float sinYawOver2 = (float)Math.Sin ((double)yawOver2); float cosYawOver2 = (float)Math.Cos ((double)yawOver2); Quaternion result; result.w = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2; result.x = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2; result.y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2; result.z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
return result; }
public static Vector3 FromQ2 (Quaternion q1) { float sqw = q1.w * q1.w; float sqx = q1.x * q1.x; float sqy = q1.y * q1.y; float sqz = q1.z * q1.z; float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor float test = q1.x * q1.w - q1.y * q1.z; Vector3 v;
if (test>0.4995f*unit) { // singularity at north pole v.y = 2f * Mathf.Atan2 (q1.y, q1.x); v.x = Mathf.PI / 2; v.z = 0; return NormalizeAngles (v * Mathf.Rad2Deg); } if (test<-0.4995f*unit) { // singularity at south pole v.y = -2f * Mathf.Atan2 (q1.y, q1.x); v.x = -Mathf.PI / 2; v.z = 0; return NormalizeAngles (v * Mathf.Rad2Deg); } Quaternion q = new Quaternion (q1.w, q1.z, q1.x, q1.y); v.y = (float)Math.Atan2 (2f * q.x * q.w + 2f * q.y * q.z, 1 - 2f * (q.z * q.z + q.w * q.w)); // Yaw v.x = (float)Math.Asin (2f * (q.x * q.z - q.w * q.y)); // Pitch v.z = (float)Math.Atan2 (2f * q.x * q.y + 2f * q.z * q.w, 1 - 2f * (q.y * q.y + q.z * q.z)); // Roll return NormalizeAngles (v * Mathf.Rad2Deg); }
static Vector3 NormalizeAngles (Vector3 angles) { angles.x = NormalizeAngle (angles.x); angles.y = NormalizeAngle (angles.y); angles.z = NormalizeAngle (angles.z); return angles; }
static float NormalizeAngle (float angle) { while (angle>360) angle -= 360; while (angle<0) angle += 360; return angle; } |
여기서 봐야할건 FromQ 이고요.
FromQ함수에서 변환할때 주의점은 쿼터니언은 기본적으로 360를 표현하지 못한다 합니다.
180기준으로 판별할수 밖에 없어서 180도가 넘어가는 경우 마이너스 각도로 판별하는것 까진 알겠는데..
왜 쿼터니언의 xyzw 를 뒤집에서 계산하는지는 제 수학력의 한계입니다.
어쨌든 다행히도 몇개의 각을 테스트 해보고 조합해서 결과값을 뽑아보니 아주 훌륭하게 오일러 각으로 잘 변환했습니다.
하... 왜 이거 한국문서나 영어문서 전부 이렇게 찾기 힘들었던 걸까요...
찾고나니 좀 허무해졌습니다.