スライド 1 - [Game Science Project Web Page]

プロジェクト演習Ⅳ
インタラクティブゲーム制作
プログラミング4
2011/11/15
クォータニオンの使い方
~恐るべき1/4たまねぎの威力~
今日の内容
• 3DCGの根元
– 全てを握る行列
– オイラー角の真実
– 回転について考える
• クォータニオン
(四元数)の使い方
– 数学的な立式や証明は
極力排除
– ゲームに使える部分を
可能な限り厳選
まずは3DCGの根元からおさらい
• 全てのモデルやカメラは、4×4の行列に
よって「位置、姿勢、スケール」を保持
している
– Direct3DもOpenGLも原理は同じ
• fk_Modelは自動的に変換してくれている
• XNAはModelの中のEffectsで設定する
平行移動行列×回転行列×拡大縮小行列
– OpenGLとDirect3Dでは乗算順が逆になるの
で注意(MV系とVM系の違い)
描画モデル以外に必要な行列
• View行列
– 世界の全てを、視点の
位置と方向を基準に変
換する行列
– モデル行列と内容は同
じ(演算時には逆行列
化して用いる)
– FKではfk_Modelの変
数をfk_Sceneにエン
トリーして設定
– XNAではEffectsに
個別設定
• Projection行列
– 視点から見た風景を
2次元に畳み込む行列
– 近くの物は大きく、遠
くの物は小さくなる
– FKではデフォルトの
行列が設定済み
• 変更したい場合は
fk_Sceneで設定
– XNAではEffectsに
個別設定
モデル行列の作り方
• 平行移動
– 座標値を所定の位置に
セットするだけ
– getPosition()の値
• 拡大縮小
– 各軸の倍率を所定の
位置にセットするだけ
– getScale()の値
• 回転行列
– 一番面倒
– 最終的にはオイラー角
の値を3つの回転行列
に分割し、一定の順番
で乗算することで作成
– getAngle()の値を以下
の順番で適用する
• Y軸中心に-h度回転
• X軸中心にp度回転
• Z軸中心に-b回転
オイラー角の罠
• 基礎演習で教えているオイラー角につい
ての説明は「ウソではないが大事なこと
をかなり端折っている」と言える
– どの順番で、どういう法則で適用するのか、
という情報が無いと意味がない
• ジンバルロックの危険性
– 2軸が重なり姿勢制御ができなくなる現象
– 定点的な姿勢を表現するのには問題ないが、
連続的な回転を表現すると問題が起きやすい
オイラー角以外の
姿勢・回転制御方法
• 最終的にはオイラー角に直す必要があるが、
途中計算に異なる形式を用いることでフォロ
ーが可能
– 方向ベクトル&アップベクトル
• 真正面の向きと、頭の向きを指定する姿勢表現
• 直感的で良いが、回転を表すのには不向き
– ベクトルを合成すると零ベクトルになる可能性がある
– axis-angle
• 任意の軸を中心に何度回転しろ、という操作
• あくまで回転表現なので、姿勢表現には不向き
– 姿勢表現に使う場合は、ある基準の方向から「どの軸で何
度回転したか」の順番を覚えておいて再現する必要がある
– その代わりジンバルロックは起きない
そこで、クォータニオン
• 姿勢、回転のどちらも表すことができる
• 補間計算が容易で強力
– ジンバルロックも心配ない
• 唯一の難点は…
意味がわかんねぇ!
クォータニオンの正体とは
• 以下の数式で表される複素数
Q  s  xi  yj  zk
i , j , kは虚数単位
• ベクトルやオイラー角と違い、各成分の
値に直接的な意味を見いだすことは困難
– fk_Quaternionクラスを利用することを推奨
– XNAにも標準で存在するので使ってみよう
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とベクトルの乗算)
今日の課題
• OBBのサンプルシーンで以下のような
処理を作ってみよう
– ロックオンポイントを3カ所作る
– ボタンを押す事に操作キャラがロックオンし
ている対象が切り替わる
– 操作キャラがロックオンポイントへ「滑らか
に」向くようにする