第 7 回:喰らえ!1/4たまねぎ

プロジェクト演習Ⅳ・Ⅵ
インタラクティブゲーム制作
第7回
クォータニオンの使い方
~恐るべき1/4たまねぎの威力~
今日の内容
• 3DCGの根元
– 全てを握る行列
– オイラー角の真実
– 回転について考える
• 四元数(クォータニオ
ン)の使い方
– 数学的な立式や証明は
極力排除
– ゲームに使える部分を
可能な限り厳選
まずは3DCGの根元からおさらい
• 全てのモデルやカメラは、4×4の行列に
よって「位置、姿勢、スケール」を保持
している
– Direct3DもOpenGLも原理は同じ
• fk_Modelは自動的に変換してくれている
平行移動行列×回転行列×拡大縮小行列
– OpenGLとDirect3Dでは乗算順が逆になるの
で注意
それぞれの行列の作り方
• 平行移動
– 座標値を所定の位置に
セットするだけ
– getPosition()の値
• 拡大縮小
– 各軸の倍率を所定の
位置にセットするだけ
– getScale()の値
• 回転行列
– 一番面倒
– 最終的にはオイラー角
の値を3つの回転行列
に分割し、一定の順番
で乗算することで作成
– getAngle()の値を以下
の順番で適用する
• Y軸中心に-h度回転
• X軸中心にp度回転
• Z軸中心に-b回転
オイラー角の罠
• 基礎演習で教えているオイラー角につい
ての説明は「ウソではないが大事なこと
をかなり端折っている」と言える
– どの順番で、どういう法則で適用するのか、
という情報が無いと意味がない
• ジンバルロックの危険性
– 2軸が重なり姿勢制御ができなくなる現象
– 定点的な姿勢を表現するのには問題ないが、
連続的な回転を表現すると問題が起きやすい
オイラー角以外の
姿勢・回転制御方法
• 最終的にはオイラー角に直さなくてはならな
いが、途中の計算に異なる形式を用いること
でフォローが可能
– 方向ベクトル&アップベクトル
• 真正面の向きと、頭の向きを指定する姿勢表現
• 直感的で良いが、回転を表すのには不向き
– ベクトルを合成すると零ベクトルになる可能性がある
– axis-angle
• 任意の軸を中心に何度回転しろ、という操作
• あくまで回転表現なので、姿勢表現には不向き
– その代わりジンバルロックは起きない
• glRotateWithVec()で実現可能
そこで、クォータニオン
• 姿勢、回転のどちらも表すことができる
• 補間計算が容易で強力
– ジンバルロックも心配ない
• 唯一の難点は…
意味がわかんねぇ!
クォータニオンの正体とは
• 以下の数式で表される複素数
Q  s  xi  yj  zk
i , j , kは虚数単位
• ベクトルやオイラー角と違い、各成分の
値に直接的な意味を見いだすことは困難
– fk_Quaternionクラスを利用することを推奨
– Direct3Dでも無理矢理部分的に併用しよう
2つの姿勢の補間姿勢を求める
• 多分一番よく使う利用方法
• オイラー角で表された姿勢Aと姿勢Bの
線形補間(E=(1.0-t)A+tB)を行いたい
– オイラー角で直接この計算を行うと悲惨な
ことになる
• クォータニオンを通すことで安全確実な
変換ができる
コーディング例
// angA, angBには姿勢がセットされるとする
fk_Angle
angA, angB, angRes;
fk_Quaternion qA, qB, qAns;
double
t = 0.5; // 任意の補間パラメータを指定
// オイラー角をそれぞれクォータニオンに変換
qA.makeEuler(angA);
qB.makeEuler(angB);
// 球面線形補間を使って補間クォータニオンを算出
qAns = fk_Math::quatInterSphere(qA, qB, t);
// 補間したクォータニオンをオイラー角に変換
angRes = qAns.getEuler();
解説
• makeEuler()でオイラー角→Q変換
• getEuler()でQ→オイラー角変換
• fk_Math::quatInterSphere()
– fk_Mathクラスのstaticメンバ関数
– staticメンバとは、クラスのインスタンスを
作らなくても利用できる関数
– 関数単体で機能する物はよくstatic化される
回転合成とベクトルの回転
• こちらの利用方法も便利なので覚えよう
• setRotate(角度, x, y, z)
• setRotate(角度, 軸を表すベクトル)
– 回転軸と角度を指定して、その回転を表す
クォータニオンを作成する
– これのおかげでset()の出番はまず無い
回転合成は乗算で
• qAB = qA*qB; で実現可能
– 乗算の順序を入れ替えてしまうと意味合いが
変わってしまうので注意
• Aの回転後Bの回転を行うのと、
Bの回転後Aの回転を行うのでは異なる結果になる
– FKのオイラー角は以下の処理で変換できる
(makeEuler()で実際に同じ処理をしている)
qH.setRotate(-euler.h, 0, 1, 0);
qP.setRotate(euler.p, 1, 0, 0);
qB.setRotate(-euler.b, 0, 0, 1);
qHPB = qH * qP * qB;
あるベクトルを
自由に回転させるには
• まずは好きな回転を表すQを作る
• その上で、以下の処理を呼べば終了
– vAfter = qRot * vBefore;
• 用途は色々考えられる
– 射撃武器による銃口補正や誘導補正
– キャラやカメラをある地点に徐々に向ける
– モーションの合成(ちょっと高度だけど)
まとめ
• 3次元の回転表現は色々あり、
状況に応じて使い分けが必要
– 最終的にはオイラー角に落とし込むが、
色々問題が多い
• クォータニオンを用いることで、
補間や合成が容易になり、夢が広がる
– 回転の合成(Q同士の乗算)
– オイラー角の補間(Q同士の合成)
– ベクトルの回転(Qとベクトルの乗算)