クォータニオン(四元数、Quaternion)は3Dグラフィックスのプログラミングにおいて回転を表す数としてよく出てきます。
曰く、
- サイズが小さい(回転行列よりも少ない数で表せる)
- ジンバルロックが起きない
- 補間が容易
とのことで、非常に便利な理論です。
しかし、どういう原理で動いているのかを知りたいと思ってWikipediaなんかを見ると、大量の数式と謎の言葉の波に飲み込まれます。
そんなこんなで今までよくわからないままに使っていたのですが、 最近になって少しだけ理解が深まったので、現時点で知っていることについて説明してみようと思います。
対象読者
クォータニオンがどういうものなのか知りたい人向けです。 単にクォータニオンを利用した回転方法を知りたいだけであれば、他の資料を当たった方が簡単だと思います。
あまり難しい計算はしませんが、高校数学くらいの知識は持っているものとします。 具体的には、複素数、三角関数、ベクトル、行列についてです。
とはいえ、そんなによく理解していなくてもどうにかなるように書いたつもりです。 だとか、加法定理だとか、ベクトルと行列の計算方法だとか、回転行列がどんな形をしているかだとか、それくらいわかれば十分です。
複素数と座標
クォータニオンの話に入る前に準備として複素数の話をします。 というのも、クォータニオンというのは複素数の拡張だからです。
複素数というと確かのような二次方程式の解を表すために登場したなんだかよくわからない数でした。 ここではそのよくわからなさを理解しようとはせず、複素数の形だけに注目して2次元座標を表すことを考えます。
複素数は一般にという形で表せます。 このとき、とは任意の実数で、両者はなんの関係性もない独立した数です。
そこで、の値を座標、の値を座標だと思い込むと、任意の複素数は2次元座標と一対一で結びつきます。
2次元ベクトルが2次元座標を表すように、複素数もまた2次元座標を表すことができる2次元的な数なのです (ベクトルというと矢印や座標を思い浮かべるかもしれませんが、ベクトルの意味は単なる一並びの数であって、プログラミングにおける一次元配列に相当します)。
の関係はどこに行ったのかと思うかもしれませんが、 ベクトルに内積や外積といった特殊な掛け算が定義されているのと同じように、 複素数にも特殊な掛け算が定義されているというだけのことです。
複素数と回転
複素数が2次元座標を表せることはわかりました。 しかし、ベクトルではなく複素数を使う利点とは何でしょうか。 それは複素数の掛け算によって回転が簡単に表せることなのです (内積や外積により角度や直交方向が表せるのと同じ)。
ベクトルによる座標表現の場合、回転は回転行列を用いて次のように表せました。
これは座標を、原点を中心に角度回転させると、上式右辺のような座標に移るということを示しています。
さて、ここで次の複素数の計算式を見てください。
なんとなく先ほどの式に現れていたような部分が見て取れるのではないでしょうか。
左辺右側の複素数と右辺の複素数をそれぞれ座標だと考えると、左が回転前の座標、右が回転後の座標になっています。 回転行列の式と見比べると、という複素数が回転行列と同じ役割を果たしていることがわかるかと思います。 つまり、複素数を用いると、という複素数をかけるだけで角度の回転を表現できるのです。
天下り的な説明*1ではありますが、 複素数で回転を表せるということがわかりました。 ちなみに、どんな複素数でも回転を表すというわけではなく、絶対値()が1の複素数だけが回転を表します。 これは回転を表す複素数の絶対値が1だからです(論理的には逆ですが)。
クォータニオンの定義
複素数は2次元的な数で、2次元の回転を表すことができました。 ということは、3次元版の複素数を考えれば3次元の回転ができるんじゃないか、というのは自然な発想ではないでしょうか。 しかし、実は複素数を3次元に拡張するのは満たすべき性質を考えると数学的に難しいことでした。 それならばと、ひとつ飛ばして4次元にまで拡張した複素数がクォータニオンです*2。
クォータニオンは1つの実部と3つの虚部からなる数です。 3つの虚部が必要ということで、に加えて新しい虚数単位とを導入し、クォータニオンをのように表します。 が実部で、がそれぞれ虚部になります。
計算方法は概ね複素数と同じですが、乗算ではやといった項がでてきてしまうためのような演算規則が必要になります。 演算規則の定義は次の通りです。
ただし、クォータニオンでは行列の乗算のように右からかけるか左からかけるかで計算結果が異なります(交換法則が成立しない)。
上の定義に右から左から適当な数()をかけると、結局次のような関係が得られます。
複素数が2次元座標を表現できたように、クォータニオンも4次元座標を表現できます。 例えば、座標はクォータニオンと表せます。 4次元座標というとうまく想像できないかもしれませんが、計算上使うだけなので、単に4つ組の数字くらいに考えておくといいと思います。 重要なのは、どれか1つの数字を無視すれば、3次元座標になるということくらいです。
クォータニオンと回転
結論だけいうと、絶対値()が1のクォータニオンは4次元の回転を表すことができます。
しかし、改めて考えてみると4次元の回転というのはいったいどんな操作なのでしょうか。
それを確かめるためにクォータニオンに対して、 回転を表すクォータニオンを左からかけてみます (回転を表す複素数と同じ式ですが、ちゃんとの回転を表すクォータニオンになっています)。
これはつまり、次の座標変換を表しています。
ここで、実部との項、およびの項との項についてそれぞれ見てみると、 それぞれが2次元における角度の回転式になっているのがわかります。 つまり、クォータニオンは平面と平面をそれぞれ角度だけ回転させています。 4次元空間における回転といっても、結局は2次元平面上の回転なのです(3次元空間での回転でもそうですよね)。
また、この計算結果にはクォータニオンによる回転の重要な性質が現れています。 それは1点で交わる2つの平面*3上で同じ大きさの角度だけ回転させるというものです(isoclinic rotation)。 4次元空間における回転にもいくつか種類があるのですが、1つのクォータニオンの乗算で表現できる種類の回転はこれになります。
座標平面上の回転
先ほどはを左からかけることにより、 平面と平面をそれぞれ角度だけ回転できることを確かめました。
同様にして、やを左からかけると、 平面と平面、平面と平面をそれぞれ角度だけ回転できることがわかります。
まとめると次のようになります。
- を左からかけると平面と平面が角度回転
- を左からかけると平面と平面が角度回転
- を左からかけると平面と平面が角度回転
乗算方向による回転の変化
クォータニオンの乗算では左右どちらからかけるかによって計算結果が異なるのでした。 回転操作は乗算なので、当然この影響を受けます。
変化を確かめるためにを今度は右からかけてみます。
計算過程からわかるように、異なる二つの虚数単位の積が逆転することにより正負が反転し、 結果として軸を含まない平面の回転が逆向きになっていることがわかります。
他の座標平面に対する結果も合わせて次にまとめます。
- を右からかけると平面が角度回転、平面が角度回転
- を右からかけると平面が角度回転、平面が角度回転
- を右からかけると平面が角度回転、平面が角度回転
共役クォータニオンによる回転
あるクォータニオンに対して、虚部の符号を反転させたクォータニオンを共役クォータニオンといいます。 クォータニオンの共役クォータニオンはになります。 共役クォータニオンの表記法はいくつかありますが、ここではのように星をつけて表記することにします。
定義より、の共役クォータニオンはです。 これを少し変形するとになるため、 この共役クォータニオンは逆向きの回転を表していることがわかります。 やの場合も同様です。
一般に回転を表すクォータニオンの共役クォータニオンは逆向きの回転を表します。 実部まで符号を反転させてしまうと逆回転にならないため注意してください *4。
回転の合成
回転操作は乗算であり、クォータニオンの乗算には結合法則が成り立ちます。 よって、座標をクォータニオンで回転させた後にクォータニオンで回転させたいという場合には、 予め計算しておいたクォータニオンを用いて次のように計算できます。
つまり、回転を表すクォータニオン同士の乗算により回転の合成ができるのです。
また共役クォータニオンにはという性質が成り立つため、 共役クォータニオン同士の合成も元のクォータニオンの合成で表せます (共役クォータニオンは逆回転を表すので当たり前といえば当たり前ですが)。
クォータニオンの乗算は左右どちらからかけるかによって結果が変わってしまうため、 合成の際には座標に対してどちらから回転を適用するかを考慮した上で、回転の順番を意識する必要があります。
クォータニオンと3次元回転
さて、ここまでは4次元の回転を考えてきましたが、我々が扱いたいのは3次元の回転です。 クォータニオンによる回転操作でどのように3次元の回転を実現するかを考えていきます。
3次元座標と4次元座標
クォータニオンによる回転操作は4次元空間にて適用するため、 3次元の座標は4次元の座標に変換する必要があります。
言葉にするとなんだか難しそうですが、やることは簡単で成分を加えてやるだけです。 成分の値はなんでも構いません。 というのも、理論上必要なだけで、3次元の回転操作ではこの値は一切関与しないためです。
仮に値を0とすると、3次元座標は次のようにクォータニオンで表せます。
回転後の座標を表すクォータニオンも逆の手順で3次元座標へと変換できます。
回転の適用
前節では、各座標平面上の回転を表すクォータニオンを紹介しました。 これらは平面、平面、平面上で任意の角度の回転ができるものでした。 成分を除いた3次元空間で考えると、これらはそれぞれ軸周りの回転を表します。 3つの座標軸周りの回転を合成すれば、3次元のあらゆる回転を表せそうです(参照:オイラー角)。
しかし、そうなると邪魔になってくるのが、同時に回転してしまう平面、平面、平面です。 どうにかして座標軸周りの回転のみを適用したいのですが、前節で述べたとおり、 クォータニオンの乗算では必ず二つの平面上で回転が起きてしまいます。
これを解消する方法は単純で、逆方向の回転によって打ち消してしまうことです。 具体的には次に示す演算をします。
が変換前の座標、が回転を表すクォータニオンです。 まず、クォータニオンを左からかけて通常通り回転させます。 同時に、共役クォータニオンを右からかけることで軸を含む平面だけ逆回転させます。 右からかけると軸を含まない平面だけ逆回転しますが、 共役クォータニオンを利用することでそれがさらに反転し、軸を含む平面だけが逆回転するようになります。 すると、軸を含む平面は回転した量と同じだけ逆回転するため、回転が相殺されます。
一方、軸を含まない平面は二倍回転することになるため、適用するクォータニオンの回転角度は半分にしておく必要があります。 角度の回転をさせたい場合には、回転させるクォータニオンを乗算します。
各軸周りに対して、同じ操作をすると次のようになります。
よって、各軸周りの回転を合成したクォータニオンがあれば、3次元座標にあらゆる回転(任意軸回転)を加えることができます。
さて、このですが、実はわざわざ合成しなくても次の式で簡単に計算できることが知られています。
は回転角度、は回転軸の向きを表す単位ベクトル(大きさが1のベクトル)です。
計算手順のまとめ
- 3次元座標をクォータニオンに変換
- を回転軸として角度回転させるクォータニオンを計算
- 回転後の座標を表すクォータニオンを計算
- を3次元座標に変換
クォータニオンと回転の一意性
4次元の回転において異なるクォータニオンが同一の回転を表すことはありません。 クォータニオンが表現できる種類の回転に対して、対応するクォータニオンは一意に定まります。
一方で、3次元の回転においては異なるクォータニオンが同一の回転を表すことがあります。 これは3次元回転の実現方法に起因しています。
3次元の回転ではひとつの平面における回転を打ち消すためにクォータニオンを二回乗算する必要がありました。 そもそもクォータニオンによる乗算は一回での角度の回転ができます。 これを二回乗算するのですから結果の回転角度はになり、それぞれの角度に対応するものが二つずつ存在します(double-cover)。
回転を表すクォータニオンの計算式にを代入してみるとわかりやすいと思います。
と同じ回転結果となるはずにも関わらず、クォータニオンはを代入した際の値の正負を反転させた結果となっています。
このように、3次元において同じ回転を表すクォータニオンは二つずつ存在するため、 クォータニオンが一致しないからといって異なる回転だと判断してはいけません。
クォータニオンの補間
クォータニオンを回転に利用する大きな動機のひとつは補間が容易であることです。
よく使われるクォータニオンの補間に球面線形補間(spherical linear interpolation, slerp)と線形補間(linear interpolation, lerp)があります。
球面線形補間
球面線形補間はクォータニオンにおける基本的な補間です。
回転を表すクォータニオンは絶対値が1であるのでした。 回転を表すクォータニオンを座標だと考えて4次元空間に配置すると、が1なので、 これらのクォータニオンは必ず半径1の超球*5面上に存在します。 超球面上の二つのクォータニオンに対し、超球面上を通るように、最短距離を、一定速度で(線形に)移動するような補間を球面線形補間といいます。
4次元で考えづらい場合には、徐々に次元を上げていくとわかりやすいかもしれません。 2次元においては、円上の二点の間を、常に円上を通るように一定速度で移動するような補間です。 3次元になると球面上の二点の間を球面上を通るように移動する経路は無数にありますが、そのうちの最短の経路を一定速度で移動するような補間です。 4次元空間がどんな世界なのかは想像もつきませんが、同じような補間がきっとできるはずです。
球面線形補間を式にすると次の通り。
は二つのクォータニオンの間の角度です。 4次元空間における角度ってどこだろう、と思うかもしれませんが、そもそも角度というのは内積によって定義されていて、
を満たすこそが角度になります。 クォータニオンの内積は、クォータニオンをという4次元ベクトルとみなした場合のベクトルの内積と同じです。
回転を表すクォータニオンでは絶対値が1なので、それらの内積の値はに等しくなります。 よって、内積の値に逆三角関数(三角関数の逆関数でやの値から角度を求める)を適用すれば角度が求まります。
線形補間
球面線形補間はきれいな補間ができるのですが、式をみればわかるようにそれほど単純な計算ではありません。 プログラミングにおいて、膨大な回数をこなすには少し計算量が多いのです。 そこで、近似的に少ない計算量で球面線形補間を実現する方法が線形補間です。
球面線形補間では超球面上を通るように補間しましたが、線形補間では直線経路で補間します。 直線経路で補間すると、補間結果のクォータニオンは基本的に超球面上にはありません。 つまり、回転をあらわすクォータニオンでなくなってしまいます。 これでは困るため、結果のクォータニオンをその絶対値で割ってやることで、絶対値が1になるように(半径1の超球面上に乗るように)正規化します。
線形補間を式にすると次の通り。
この式は球面線形補間の式における、が非常に小さいときの近似式になっています (が十分に小さい場合にと近似できるため)。 大雑把な感覚として、補間しようとしている二つのクォータニオンが近いものであればあるほど、球面線形補間の値に近い値が得られます。
補間における注意
クォータニオンの補間には一点だけ注意しておかなければならないことがあります。 それは、同じ回転を表すもうひとつのクォータニオンの存在です。
前節で述べたとおり、3次元の回転を表すクォータニオンは符号を反転させると、 まったく同じ回転を表すもうひとつのクォータニオンになります。 これらは3次元の回転としては同じでも、4次元空間における位置は原点を挟んでまったくの逆側です。 当然、どちらのクォータニオンとの間を補間するかによって結果はまったく異なります。
基本的に補間は距離の短い経路を用いた方が直感的になるため、 3次元の回転を表すクォータニオン同士を補間する場合には、符号をうまく反転させて近いクォータニオン同士を補間します。 どちらのクォータニオンの方が近いか判定するには角度を利用します。 二つのクォータニオンの内積を計算して、値が正ならなのでそのまま補間を、 値が負ならなので片方のクォータニオンの符号を反転させて補間をします。
参考
- 四元数を用いた三次元回転計算の幾何学的意味
- Why Do Quaternions Double-Cover? – Nathan Reed’s coding blog
- https://www.essentialmath.com/GDC2013/GDC13_quaternions_final.pdf
おわりに
クォータニオンに関する記事は、使い方のみに焦点を絞ったものや難しい数学の原理を説明したものが多く、 これら二つの間を埋めるような内容の記事が少ないな、と思ったので書いてみました。 できる限りわかりやすくする、という名目のもと難しい説明が必要な内容を(重要さに関わらず)片っ端からすっ飛ばしたので、 この記事を読んだだけでは疑問点も多いかもしれませんが、他の数学的記事の理解の助けになれば幸いです。
実はこの記事、うまい説明の流れが思いつかずに数か月間積んでます。 そのままお蔵入りにするのももったいないので、とりあえず書き残したことを加筆したのですが、 当初の構成を覚えていないのでバグ(論理の破綻)が紛れ込んでいる可能性が多分にあります。 何かおかしな点を見つけたら指摘してもらえると助かります。