4次元行列transformの中身を解説
ARKitを使った落書きなどのアプリのコードで、以下のようなコードがよく見られると思います。
transformってなに?となる方がいると思いますので、数学的な知識を補いながら、説明します。
guard let pointOfView = sceneView.pointOfView else { return }
let mat = pointOfView.transform
let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33)
let currentPosition = pointOfView.position + (dir * 0.1)
1行目はなんとなくわかるかと思いますが、2~4行目で、???となる方が出てくると思います。
結論から述べると、
2行目のpointOfView.transformは座標変換(アフィン変換)の4次元行列、
3行目は座標変換後の新しいz軸方向のベクトル(大きさが1の単位ベクトル)、
4行目はpointOfView.positionが現在のカメラ位置で、新しいz軸方向に0.1mだけ離れた位置
ということをそれぞれ意味しています。
これから、数学的知識も補いながら、説明します。
3次元座標変換の4次元行列とは何か
まずxy軸上での座標変換について説明します。
(x1, y1)から原点を中心にx軸方向に角度θだけ回転することで(x2, y2)に移動するときに、その座標変換の行列は以下のようになります。(わかりやすいようにそれぞれ原点からの距離を1とします。)
\begin{pmatrix}
x2 \\
y2
\end{pmatrix}
=
\begin{pmatrix}
cosθ & -sinθ \\
sinθ & cosθ
\end{pmatrix}
\begin{pmatrix}
x1 \\
y1
\end{pmatrix}
次に、原点を中心に角度θだけ回転したのち、(α, β)まで平行移動した場合について説明すると
\begin{pmatrix}
x2 \\
y2
\end{pmatrix}
=
\begin{pmatrix}
cosθ & -sinθ \\
sinθ & cosθ
\end{pmatrix}
\begin{pmatrix}
x1\\
y1
\end{pmatrix}
+
\begin{pmatrix}
α\\
β
\end{pmatrix}
実はこれは次のように式変形することで、足し算のないシンプルな計算式にすることができるようになります。
\begin{pmatrix}
x2 \\
y2 \\
1
\end{pmatrix}
=
\begin{pmatrix}
cosθ & -sinθ & α \\
sinθ & cosθ & β \\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x1 \\
y1 \\
1
\end{pmatrix}
この3次元行列をよく見ると、
\begin{pmatrix}
cosθ & -sinθ & α \\
sinθ & cosθ & β \\
0 & 0 & 1
\end{pmatrix}
左上の回転行列と、右側の平行移動のベクトルと、下の行の(0, 0, 1)で成り立っていることがわかります。
では、次にxyz軸上での座標変換を考えます。
この3次元の回転行列Rが実際にどのように求められるかの説明はここでは省かせてもらいます。
ここではこの回転行列Rが
R =
\begin{pmatrix}
m11 & m21 & m31 \\
m12 & m22 & m32 \\
m13 & m23 & m33
\end{pmatrix}
と表されるとします。
では先ほどと同様に、原点を中心に角度θだけ回転した後に点(α,β,γ)まで平行移動すると、
\begin{pmatrix}
x2 \\
y2 \\
z2
\end{pmatrix}
=
\begin{pmatrix}
m11 & m21 & m31 \\
m12 & m22 & m32 \\
m13 & m23 & m33
\end{pmatrix}
\begin{pmatrix}
x1 \\
y1 \\
z1
\end{pmatrix}
+
\begin{pmatrix}
α \\
β \\
γ
\end{pmatrix}
となり、想像ついている方もいらっしゃると思いますが、これは先ほどと同様に、
\begin{pmatrix}
x2 \\
y2 \\
z2 \\
1
\end{pmatrix}
=
\begin{pmatrix}
m11 & m21 & m31 & α\\
m12 & m22 & m32 & β\\
m13 & m23 & m33 & γ\\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
x1 \\
y1 \\
z1 \\
1
\end{pmatrix}
と変形できます。
この4次元行列こそが今回出てくるtransfromそのものです。
transformの左上の3次元行列の部分は回転行列、右側のα、β、γの値は平行移動した分、つまりα、β、γの値はカメラが初期位置を原点とした位置座標を表しています。
下の(0, 0, 0, 1)はあくまで式変形する上で必要というだけなので、無視して構いません。実際transformの値をみても、常に(0, 0, 0, 1)となっているはずです。
座標変換後のxyz軸方向のベクトルについて
一度、xy軸上の座標変換に戻って考えます。
xy軸上の回転行列は、
\begin{pmatrix}
cosθ & -sinθ \\
sinθ & cosθ
\end{pmatrix}
と表され、
\begin{pmatrix}
cosθ \\
sinθ
\end{pmatrix}
: 新しいx軸方向の単位ベクトル
\begin{pmatrix}
-sinθ \\
cosθ
\end{pmatrix}
:新しいy軸方向の単位ベクトル
を表しています。
xyz軸上の座標変換も同じで、
\begin{pmatrix}
m11 & m21 & m31 \\
m12 & m22 & m32 \\
m13 & m23 & m33
\end{pmatrix}
について、
\begin{pmatrix}
m11 \\
m12 \\
m13
\end{pmatrix}
:新しいx軸方向の単位ベクトル
\begin{pmatrix}
m21 \\
m22 \\
m23
\end{pmatrix}
:新しいy軸方向の単位ベクトル
\begin{pmatrix}
m31 \\
m32 \\
m33
\end{pmatrix}
:新しいz軸方向の単位ベクトル
特に座標変換後のz軸方向のベクトルが重要
3行目の
let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33)
は新しいz軸方向の単位ベクトルの逆方向を表しています。
pointOfView.positionは現在のカメラの位置を表しており(正確には初期位置を(0,0,0)としたときの3次元座標)、dir*0.1をプラスすることで、現在のカメラ位置から新しいz軸方向に0.1m先の座標をcurrentPositionと定義していることがわかります。
またここでは深く触れませんが、z軸方向のベクトルがわかることで一瞬で平面方程式も求められます。
スマホのxyz軸方向はどのようにして定義されているのか
そもそもx軸、y軸、z軸方向がどのように決まるのかについて説明します。
ARKit関連の記事にはよくスマホを向けたときの奥側がz軸という曖昧な表現が多いですが、
このページの図をみてもらえればわかるように、
スマホを垂直に置いた時における、正面をみて横方向がx軸、縦方向がy軸、正面方向がz軸となります。
仮に、アプリ起動時にスマホを水平に傾けた状態から始めた場合、そこから垂直になおしたときの横方向をx軸、縦方向をy軸、正面方向をz軸と定義します。(もしかしたら環境によって違うなどあるかもしれません。。)