座標変換:クォータニオン、オイラー角
回転による座標変換について改めて調べてみた。
ちょっと間違ったこともあるかもしれないけど…
まず、回転による座標変換には以下の3つがある。
・変換行列
・オイラー角
・クォータニオン
変換行列はアフィン変換と言われ、回転だけでなく拡縮や移動などの変換も表す4×4の行列。これについては今回は省略。
オイラー角での変換はどの軸に何度回転させるというx軸, y軸, z軸それぞれに何度という3つの情報だけで変換が表せる。たとえばX軸に90度、Y軸に90度、Z軸に0度であれば(90, 90, 0)と表せる。オイラー角での変換はジンバルという回転台のような感じで、それぞれの軸が親子構造になっているイメージ。
(wikipedia | ジンバル より借用)
オイラー角での表現では順番が大切で、どの順番で回転を行うかで結果が変わってくる。順番はX-Y-Zなどで表す。下のアニメーションは右が、X-Yの順番で変換したもので、左がY-Xで変換したもの。
(Blender上で、座標系を「Gimbal(ジンバル)」にして試しています。)
ちなみにUnityのTransform.Rotateには
Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).
とあるので、Z-X-Yで変換される??
オイラー角では3つの数字という少ない情報で回転変換ができるけど、弱点としてはジンバルロックという現象が出てしまう。ジンバルロックとは、回転軸の親子関係の真ん中の軸が90度回転したときに一番子の軸と一番親の軸が重なってしまい、3つある回転の自由度が2つになってしまうこと。まさに上でのアニメーションで赤(x軸回転)と青(Z軸回転)が重なってしまい1つ回転の自由度が奪われた状態。
(例えるなら北極点にいるときは全ての方向が南になってしまうような方向を失った状態でしょうか)
こうなるとプログラムで回転させようとしたときに回転しなかったりおかしな状態になる。言葉では分かりづらいですが下記の説明を見ると分かりやすいです。
ジンバルロックを回避するために利用できる変換がクォータニオン。Unityも内部的にはすべて回転の情報はクォータニオンで持っている。2Dでの回転は高校でもやる、1つの実数と、1つの虚数を持った複素数(二元数)を利用して可能だったけど、ハミルトンという数学者が1つの実数と3つの虚数をもった四元数の概念を作り、それを3Dでの回転に扱えるようになった。
この辺の内容は数学的にかなり難しく理解しようとすると相当難しい。またクォータニオンの値自体を見てもオイラー角と違いどういった状態なのかは想像ができない。
[unityjavascript]
var quaternion:Quaternion = new Quaternion();
quaternion.eulerAngles = new Vector3(45, 45, 45);
print(quaternion);
// 出力 : (0.5, 0.2, 0.2, 0.8)
[/unityjavascript]
この辺の詳しい話は
oreilly | 実例で学ぶゲーム3D数学(サンプルページ) | 3Dにおける方向と角変位
に書いてあります。
Unity上で回転周りの扱いは主にTransform、Quaternionあたりを扱うと実現できて、こんな感じで使える。一番左のTransform.Rotateは指定の座標軸に対して現状の状態から回転できる
[unityjavascript]
this.transform.Rotate(10, 10, 10, Space.Self);
[/unityjavascript]
真ん中はLookAtの使い方で、サンプルではマウスの位置を見る。簡単にその方向を向くようにできるので便利。
[unityjavascript]
this.transform.LookAt(this.lookAtPos);
[/unityjavascript]
一番右はQuaternionの回転の合成。Quaternionでは乗算がQuternion同士の回転変換の合成が行われてここではX軸に45度、Y軸に45度か移転させたものを現在の回転に合成。
[unityjavascript]
this.transform.rotation *= Quaternion.AngleAxis(10, new Vector3(45, 0, 0)) * Quaternion.AngleAxis(10, new Vector3(0, 45, 0));
[/unityjavascript]
Quaternion.Slerpというのが回転周りでよく使うスムーズな回転方法。Slerpは日本語で「球面線形補間」。Quaternion.Lerpというのもありこちらは「線形補間」。Slerpの方がなめらかな遷移となる。Quaternion.Learp, Quaternion.Slerpともに引数は
(from:Quaternion, to:Quaternion, t:float)
となっていて、tは 0 <= t <= 1 の範囲で、0.5ならfromとtoの間の状態を実現できる。
実際に試してみた。
左の2つはLerp/Slerpを用いた時間軸での変化。(0〜1秒)
fromからとへ1秒掛けて遷移し、見た感じあまり分からないけどSlerpの方が緩やかな遷移をしている。
右の2つはLerp/Slerpを使って、だんだんとその位置へ時間とともに近づくような処理をしている。
[unityjavascript]
Quaternion.Lerp(this.transform.rotation, this.targetRot, Time.deltaTime);
[/unityjavascript]
Lerp/Slerpともにあまり見た感じは変わらないけど、Slerpの方が気持ち滑らかに見える。下記の動画だともう少し分かりやすく、遷移の状態が曲線と直線とで表現されている。
ここのサイトのグラフにあるようにSlerpの方が少しS字曲線になってなめらかになっている。
アニメーションについてはiTweenの様なライブラリがあるので、この辺を利用すると回転を含むいろいろな遷移やそれ以外の機能もあるので便利ですね。
上記の試したUnityプロジェクトファイルはこちら
>> Download
[…] 2014.10.3 Unity基本メソッド覚書 2013.03.21 座標変換:クォータニオン、オイラー角 amazon_ad_tag ="soraneco08-22"; amazon_ad_width ="300"; amazon_ad_height ="250"; […]