回転行列から単位四元数への変換
回転姿勢を表す回転行列を単位四元数に変換します。
回転行列を とし、単位四元数を とします。
このとき、次の式が成り立ちます。(四元数による回転の式 参照)
計算機で求めることを考慮すると、 のうち、 であるものがどれかによって、場合分けします。
なぜなら、いずれかの値を定めた場合、他の値はその値で割ることで求まるためです。
の場合
が成り立ちます。
よって、 かどうかは、 で判断できます。
このとき、 は、 の中で最大とは限りませんが、 なので、 で除算をして を求めても安全であろうと考えるわけです。
の符号を正として、 を求めます。
の場合
このとき、 のうち最大であるものを最初に定め、それを基準として他の値を決めていきます。
以降で、 の大小比較を で行いますが、それは、 が成り立つためです。
が の中で最大の場合
が の中で最大かどうかは、 で判断できます。
が の中で最大であるとき、 が成り立ちます。
の符号を正として、 を求めます。
が の中で最大の場合
が の中で最大かどうかは、 で無い且つ で判断できます。
が の中で最大であるとき、 が成り立ちます。
の符号を正として、 を求めます。
が の中で最大の場合
が の中で最大かどうかは、 で無いことで判断できます。
が の中で最大であるとき、 が成り立ちます。
の符号を正として、 を求めます。
最後に
で の符号を正としている理由は、符号はどうでもよいからです。
なぜなら は回転を表す単位四元数であり、 も同じ回転を表すためです。
よって、 では の符号を正にして、残りの の値を決めています。
参考プログラム
glMatrix.jsのquat.fromMat3メソッドのソースを貼り付けておきます。
非常にうまい実装だと思います。
/** * Creates a quaternion from the given 3x3 rotation matrix. * * NOTE: The resultant quaternion is not normalized, so you should be sure * to renormalize the quaternion yourself where necessary. * * @param {quat} out the receiving quaternion * @param {ReadonlyMat3} m rotation matrix * @returns {quat} out * @function */ export function fromMat3(out, m) { // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes // article "Quaternion Calculus and Fast Animation". let fTrace = m[0] + m[4] + m[8]; let fRoot; if (fTrace > 0.0) { // |w| > 1/2, may as well choose w > 1/2 fRoot = Math.sqrt(fTrace + 1.0); // 2w out[3] = 0.5 * fRoot; fRoot = 0.5 / fRoot; // 1/(4w) out[0] = (m[5] - m[7]) * fRoot; out[1] = (m[6] - m[2]) * fRoot; out[2] = (m[1] - m[3]) * fRoot; } else { // |w| <= 1/2 let i = 0; if (m[4] > m[0]) i = 1; if (m[8] > m[i * 3 + i]) i = 2; let j = (i + 1) % 3; let k = (i + 2) % 3; fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0); out[i] = 0.5 * fRoot; fRoot = 0.5 / fRoot; out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot; out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot; out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot; } return out; }
また、他に実装している方のリンクを張っておきます。
回転行列からクォータニオン(四元数)に戻す