プロジェクト演習Ⅳ・Ⅵ インタラクティブゲーム制作 第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とベクトルの乗算)
© Copyright 2024 ExpyDoc