0

3次直交行列の補間を行う話

158
0

p5.jsというライブラリの関数を作る一環で、3次直交行列の補間が必要になったので、その際の計算について触れたいと思います。Mathlogを触るのが久しぶりなので、うまくできるかわかりませんが...

扱う行列の定義

 ここで扱うのは行列式が1である3次の実直交行列です。いわゆるSO(3)と呼ばれるクラスになります。このクラスの行列の補間を行います。以下、単にSO(3)と書いてこのような行列の全体とします。いちいち実であることに言及するのが面倒なので、そのようにします。

性質

 SO(3)の行列には特徴がいくつかあります。固有値のひとつは必ず1であること、残りの2つは互いに共役な絶対値が1の複素数であること、です。
 この行列は線形変換としてどのような処理を実行するのかというと、実は球面の回転として表現することができます。たとえばSO(2)の行列はすべて原点中心の回転(もしくは恒等写像)として表現できます。SO(3)の行列は、単位行列でない限り、なんらかの軸となるベクトルを持っています。加えて角度を持っており、軸の周りにその角度だけ回転させる作用となっています。軸をどちらから見るかにより、回転の方向に2通りの任意性があるので、具体的に確定するわけではなく、いずれかを選ばなければなりません。

軸と角度による表現

 軸となる単位ベクトルを(a,b,c)とし、角度をθとします。このときのSO(3)の行列の表現は次のようになります。もちろん、単位行列ではないことを前提とします。軸が定まらないからです。

V=(cosθ+(1cosθ)a2(1cosθ)ab(sinθ)c(1cosθ)ca+(sinθ)b(1cosθ)ab+(sinθ)ccosθ+(1cosθ)b2(1cosθ)bc(sinθ)a(1cosθ)ca(sinθ)b(1cosθ)bc+(sinθ)acosθ+(1cosθ)c2)
なお、θが0の場合、a,b,cの値によらずこれは単位行列となります。このように軸によらないので、一意的な表現がありません。
 作用を確かめると、まず(a,b,c)(0,0,1)の場合、(1,0,0)を列ベクトルとみなして行列を左から掛けると、(cosθ,sinθ,0)になります。確かに、z軸を上から見た場合のθの回転となっていますね。

軸と角度の算出

 軸と角度をいかにして求めるかについて説明します。SO(3)の行列Vを取ってきます。単位行列ではないとし、成分を次のように書きます。
V=(a00a10a20a01a11a21a02a12a22)
対角成分の和を考えます。いわゆるトレースですが、これはSO(3)の行列の性質により、1+2cosθになります。θは上記の表現における角度ですが、±の任意性があります。単位行列ではないため、常にcosθは1より小さいです。固有値の内訳が1と、互いに共役な絶対値が1の複素数2つなので、実部を考えるとそのようになります。
先にこれによりcosθを出しておきます。
cosθ=a00+a11+a2212.
すると、cosθ<1なので、次の式が成立します。
a00+a11+a22=1+2cosθ,
(a00cosθ)+(a11cosθ)+(a22cosθ)=1cosθ>0.
これより、いずれかのaiicosθは正であることがわかります。a00cosθが正であるとしても一般性を失いません。このとき、目標の行列の左上の成分から、
a2=a00cosθ1cosθ,    a=a00cosθ1cosθ
としてaが定まります。これは二つありますが、正の方を取ります。これが軸ベクトルの任意性で、常に2通りの取り方が存在します。b,cが成分の和を比較することで次のように求まります。
a10+a01=2ab(1cosθ),   b=a10+a012a(1cosθ).
a20+a02=2ca(1cosθ),   c=a20+a022a(1cosθ).
また、sinθも定まります。
a12a21=2asinθ,   sinθ=a12a212a.
cosθsinθから具体的な角度が定まります。これで上記の計算をすれば、元の行列が復元されます。
 a11cosθが正の場合、a22cosθが正の場合も同様です。

補間するには

 上記の方法で角度を算出したうえで、角度に適当な0~1の係数を掛ければ、0のときは単位行列、1のときは元の行列ですから、0から1まで動かすと元の行列に滑らかに変化していきます。これで補間することができました。なおθを取る際にπからπまでの範囲の値を取れば、常に短い方で補間できます。その辺りは自由に選ぶことができます。

一般の場合

 一般にSO(3)の行列V0,V1を取ってこれらを補間するにはどうするのかというと、片方の転置(というか逆行列)を取って掛け算します。
V=V1V0t
とおきます。このVはもちろんSO(3)の行列です。これを先ほどのように補間して単位行列から元の行列まで動かします。このとき、VV0を考えれば、これはVが単位行列からVまで動く間、V0からV1まで動きます。これで滑らかに補間することができました。

補足

 ここで計算しているのはクォータニオンの成分です。が、自分はこれについては詳しくないです。クォータニオンのまま扱うと独特の積の法則などがあり割とややこしいので、直交行列のまま扱いたいと思ってこのような方法を採用しました。自分が開発のお手伝いをしているp5.jsというライブラリがカメラの取り扱いにおいてクォータニオンを採用していないので、このような方法を考案することになりました。Three.jsのこれが似たような計算をしているかもしれません。
  setFromRotationMatrix
ただこのやり方だとsinθが0のとき使えないので、成分のまま計算するには不向きなように感じました。それでこうしました。

実装の実際

 実際にカメラの補間に用いた例がこちらになります:
  p5.Camera.set()_DEMO_2
この中でカメラの行列を補間するのに実際にこれを用いています。aiicosθが正になるものを取るくだりでは、確実に正になるよう、最も大きい対角成分を取ることで対処しています。角度の算出にはMath.atan2()を用いています。なので1に対しては常にπが返ります。
 以上です。ここまでお読みいただいてありがとうございました。

投稿日:2023629
OptHub AI Competition

この記事を高評価した人

高評価したユーザはいません

この記事に送られたバッジ

バッジはありません。
バッチを贈って投稿者を応援しよう

バッチを贈ると投稿者に現金やAmazonのギフトカードが還元されます。

投稿者

黒狐
黒狐
34
5003
数学ちょっと好きです!

コメント

他の人のコメント

コメントはありません。
読み込み中...
読み込み中
  1. 扱う行列の定義
  2. 性質
  3. 軸と角度による表現
  4. 軸と角度の算出
  5. 補間するには
  6. 一般の場合
  7. 補足
  8. 実装の実際