3次元座標変換のメモ書き
これは Zenn で公開している内容と同じです.→ 3次元座標変換のメモ書き
Docusaurusの数式処理を確認するための文書です.ここでは数式表示の処理をクライアント側で行うようにしていますので、Zennの記事で見ると快適だと思います.
はじめに
入社してきた新入社員が期待と不安とやる気に満ちあふれているところで,まずは研修から始めるところが多いかと思います. 研修なんて受けたことがないので,きちんと基礎を叩き込んでくれるところで研修受けたい.そう思うこの頃です.
今回は3次元座標変換について,巷にはそういった情報は沢山あるのですが,自分なりの整理も含めて少し書くことにしました.新人向けに書いていますので,最初は基本的なところから入っていきます.
基本的な座標変換
座標系(coordinate system)
3次元空間は実世界と同じであり,上下・左右・前後の3つの次元です.この上下・左右・前後はそれぞれ直交関係にあります.そして,例えば上下というのは,私から見た上下と,あなたから見た上下は必ずしも一致するとは限りません.つまり,上下・左右・前後というのはどこか基点があるということです.この場合,私やあなたが基点であり,向いている方向に依存しています.次に,例えば前方に5歩進むとしたら人によって進む距離に違いがあります.もし,前方に1メートル進む場合は全員同じ距離になりますが,今度は1メートル進むための歩数が変わってきます.このように,単位も重要になってきます.よって,基点と向きと単位を決めれば,その3次元空間での座標が特定できることになります.この基点を原点(origin)とし,向き(axis),単位(unit)を決めたものを座標系といいます.ここでは上下をY,左右をX,前後をZとします.
3次元空間では座標系を決めるときに2つの種類があります.左手座標系と右手座標系です.左手座標系ではZ方向が奥に向かって正であり,DirectXやUnrealEngine,Unityなどが左手座標系です.右手座標系はZ方向が手前に向かって正であり,OpenGLやWebGLなどが右手座標系です.座標系が同じだとしても, 上下方向がYやZだったりするので,座標系と軸を確認するようにしましょう.
ここでは右手座標系で説明します.
ベクトル(vector)
座標系が決まれば,位置を表現することができます.3次元空間での位置はベクトルで表されます.まず,点$p$の位置をXYZの各軸で測った値をそれぞれx,y,zとすると
$$ p = (x,y,z) $$
で位置を表現できます.ベクトルは方向と大きさを表していて,例えば点$q$から点$p$に向かうベクトル$v$は
$$ q = (0,30,0), \quad p = (0,50,0), \quad \vec{v} = \vec{qp} = (0,20,0) $$
となります.これにはベクトルの始点,終点の位置,大きさ,方向がありますね.ここで,ベクトル$\vec{v}$の始点を原点$O = (0,0,0)$とすれば,
$$ \vec{OV} = \vec{v} = (0,20,0) $$
となって,大きさと方向のみを表すことになります.このときの$x,y,z$のことをそれぞれX成分,Y成分,Z成分といい,この表記を成分表示といいます.
ベクトルの成分表示が出来たので,ベクトルから大きさと方向をそれぞれ取り出してみましょう.まず,大きさですがこれは始点から終点までの距離になります.始点を原点とすればピタゴラスの定理を使ってベクトルの大きさ$|\vec{v}|$は
$$ |\vec{v}| = \sqrt{x^2+y^2+z^2} $$
で得られます.3次元ベクトルが3つの値を持っていることに対して,ベクトルの大きさは1つの量を表す値です.この値をベクトルに対してスカラ(scalar)といいます.
次に方向は,大きさが1のベクトルで表します.このようなベクトルを単位ベクトル(unit vector)といいます.単位ベクトルはベクトルの各成分を大きさで割ることで求められます.
$$ \frac{\vec{v}}{|\vec{v}|} = \left(\frac{x}{|\vec{v}|},\frac{y}{|\vec{v}|},\frac{z}{|\vec{v}|}\right) $$
これを正規化(normalize)といいます.つまり,ベクトルを正規化すればそれは方向を表すベクトルということになります.
※ベクトルや行列の演算について,これから出てきますが詳しいことはここでは解説しません.ベクトルや行列などの線形代数については他を参照したり,もしくは私が書いた「CGのための数学」を参照してください.
基底(basis)
ベクトルの成分表示でXYZで測った値を表記することがわかりました.もちろん,このXYZというのはそのベクトルを測った座標系に基づいています.この座標系とベクトルの関係を見ていきましょう.
XYZというのは座標系における軸のことです.これまで,上下・左右・前後という曖昧な表現でしたが,方向を表すベクトル,つまり単位ベクトルを使うと,XYZの向きをベクトルで表すことが出来ます.一般的な座標系として,左右をX,上下をY,前後をZとしたとき,それぞれの向き(単位ベクトル)$\vec{e} _ x,\vec{e} _ y,\vec{e} _ z$は
$$ \vec{e}_x = (1,0,0), \quad \vec{e}_y = (0,1,0), \quad \vec{e}_z = (0,0,1) $$
となります.これを使うとベクトルの成分表示は
$$ \vec{p} = \left(\begin{matrix}p_x \newline p_y \newline p_z\end{matrix}\right) = p_x \left(\begin{matrix}1 \newline 0 \newline 0\end{matrix}\right) + p_y \left(\begin{matrix}0 \newline 1 \newline 0\end{matrix}\right) + p_z \left(\begin{matrix}0 \newline 0 \newline 1\end{matrix}\right) = p_x \vec{e}_x + p_y \vec{e}_y + p_z \vec{e}_z $$
となります.この$\vec{e}_x,\vec{e}_y,\vec{e}_z$はお互い直交関係になっています.ベクトルの内積が0のとき,直交であることから
$$ \vec{e}_x \cdot \vec{e}_y = \vec{e}_y \cdot \vec{e}_z = \vec{e}_z \cdot \vec{e}_x = 0 $$
の関係であることがわかります.これらの単位ベクトルによって座標系の向きを決めることができます.このXYZの向きベクトルの組を基底といい,それぞれのベクトルのことを基底ベクトルといいます.ベクトルの内積は片方が単位ベクトルのとき,もう片方のベクトルのその単位ベクトル方向の大きさを求められます.これを使って,ある座標系の基底ベクトルと,位置を表すベクトルから
$$ \begin{aligned} p_x &= \vec{p} \cdot \vec{e}_x \newline p_y &= \vec{p} \cdot \vec{e}_y \newline p_z &= \vec{p} \cdot \vec{e}_z \newline \end{aligned} $$
と各成分が得られます.さらに行列を使って,$\vec{e}_x = i, \vec{e}_y = j, \vec{e}_z = k$とおくと
$$ \begin{aligned} M &= \left(\begin{matrix}i_x & i_y & i_z \newline j_x & j_y & j_z \newline k_x & k_y & k_z\end{matrix}\right) \newline \vec{p}' &= M\vec{p} \newline &= \vec{p}\cdot i + \vec{p}\cdot j + \vec{p}\cdot k \end{aligned} $$
となります.この行列$M$はXYZの基底ベクトルで構成されていることから,基底を表していることが想像できると思います.行列とベ クトルの内積の性質から,基底を表す行列$M$に,任意のベクトル$\vec{p}$をかけると(一次変換),行列$M$で表す基底での成分に変換することができ,この値は一意に決まります.よって,この行列$M$はベクトル$\vec{p}$から$\vec{p}'$の写像を表しています.
基底ベクトルというのは常に単位ベクトルというわけでもなく,また,3次元での基底は直交していなくても定義することができます.このような座標系は斜交座標系といったりします.ここでは,右手座標系ですので,基底ベクトルは直交関係であり,また,基底ベクトルが単位ベクトルであるとします.このような基底を正規直交基底といいます.
基底変換(basis transformation)
いよいよ座標変換に入っていきます.座標変換といっても色々種類がありますので,1つずつ見ていきましょう.まずは基底変換です.基底というのはすでに見てきたように,基底ベクトルの組であり,基底ベクトルは単位ベクトルで互いに直交になっています.座標系で出てきた「原点」「向き」「単位」のうち,「原点」は$O=(0,0,0)$のままでどの基底でも同じです.また,単位も基底ベクトルは常に単位ベクトルですので,単位も変わりません.結局,基底は向きのみを表しているといえます.そうすると基底が変わるということはどういうことでしょうか.原点が固定で,大きさも変わらないとなれば,それは向 きが「回転」すると考えられます.つまり,基底変換は回転させる座標変換といえます.
ここで少し三角関数について復習しておきましょう.まずは三角比から.
$$ \sin\theta = \frac{a}{c}, \quad \cos\theta = \frac{b}{c}, \quad \tan\theta = \frac{a}{b} $$
単位円で三角関数を見てみると
$$ x = \cos\theta, \quad y = \sin\theta $$
となります.また,ピタゴラスの定理から
$$ \sin\theta^2 + \cos\theta^2 = 1 $$
が得られます.三角関数の逆関数にはそれぞれ$\arcsin, \arccos, \arctan$があります.これらの関係は
$$ \begin{aligned} \theta &= \arcsin(\sin\theta) \quad ( -\frac{\pi}{2} \leqq \theta \leqq \frac{\pi}{2} ), \newline \theta &= \arccos(\cos\theta) \quad ( 0 \leqq \theta \leqq \pi ), \newline \theta &= \arctan(\tan\theta) \quad ( -\frac{\pi}{2} < \theta < \frac{\pi}{2} ) \end{aligned} $$
となります.次に加法定理です.次の図を見てください.
$\Delta abc$において,斜辺の長さが$1$から
$$ \overline{ac} = \sin(A+B), \quad \overline{bc} = \cos(A+B) $$
$\Delta abd$において,
$$ \overline{ad} = \sin B, \quad \overline{bd} = \cos B $$
次に
$$ \begin{aligned} \overline{af} &= \overline{ad}\cos A = \cos A \sin B \newline \overline{fc} &= \overline{de} = \overline{bd}\sin A = \sin A \cos B \end{aligned} $$
$\overline{ac} = \overline{af}+\overline{fc}$の関係から
$$ \begin{aligned} \sin(A+B) &= \sin A\cos B + \cos A\sin B \newline \cos(A+B) &= \cos A\cos B - \sin A\sin B \newline \sin(A-B) &= \sin A\cos B - \cos A\sin B \newline \cos(A-B) &= \cos A\cos B + \sin A\sin B \end{aligned} $$
となります.
それでは回転を見ていきましょう.次の図のように点$p$を点$p'$に回転する場合を考えます.ここでは$z=0$とします.
すると点$p$の各成分は
$$ x = r\cos\phi, \quad y = r\sin\phi $$
となります.回転後の点$p'$の各成分は加法定理を使って
$$ \begin{aligned} x' &= r\cos(\theta+\phi) = r\cos\theta\cos\phi - r\sin\theta\sin\phi \newline y' &= r\sin(\theta+\phi) = r\sin\theta\cos\phi + r\cos\theta\sin\phi \end{aligned} $$
となります.ここで
$$ r = \frac{x}{\cos\phi}, \quad r = \frac{y}{\sin\phi} $$
を代入して,整理すると
$$ \begin{aligned} x' &= \frac{x\cos\theta\cos\phi}{\cos\phi} - \frac{y\sin\theta\sin\phi}{\sin\phi} = x\cos\theta - y\sin\theta \newline y' &= \frac{x\sin\theta\cos\phi}{\cos\phi} + \frac{y\cos\theta\sin\phi}{\sin\phi} = x\sin\theta + y\cos\theta \newline z' &= z \end{aligned} $$
となって,行列で書くと
$$ \left(\begin{matrix}x' \newline y' \newline z'\end{matrix}\right) = \left(\begin{matrix} \cos\theta & -\sin\theta & 0 \newline \sin\theta & \cos\theta & 0 \newline 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix}x \newline y \newline z\end{matrix}\right) $$
となります.この行列の基底ベクトルに注目してみると,回転させたい方向とは逆の方向に回転したベクトルになっています.つまり,反対方向に 回転させた基底を使って変換を行うと,回転した座標になるということになります.
この回転行列はZ軸を基準に回転を表していることになります.このZ軸回転の行列を$R_z$とおくと
$$ R_z = \left(\begin{matrix} \cos\theta & -\sin\theta & 0 \newline \sin\theta & \cos\theta & 0 \newline 0 & 0 & 1 \end{matrix}\right) $$
となります.同じようにX軸,Y軸の回転行列$R_x,R_y$は
$$ \begin{aligned} R_x &= \left(\begin{matrix} 1 & 0 & 0\newline 0 & \cos\theta & -\sin\theta \newline 0 & \sin\theta & \cos\theta \end{matrix}\right) \newline R_y &= \left(\begin{matrix} \cos\theta & 0 & \sin\theta\newline 0 & 1 & 0 \newline -\sin\theta & 0 & \cos\theta \end{matrix}\right) \end{aligned} $$
となります.ここで各回転行列の行ベクトル,および列ベクトルは
$$ \sin\theta^2 + \cos\theta^2 = 1 $$
から,すべて単位ベクトルであり,列ベクトルにおいても互いに内積がゼロなので直交していることがわかります.
行列は掛け合わせて合成できるので,例えばXYZ回転の行列$R _ {xyz}$は
$$ R _ {xyz} = R_z R_y R_x $$
となります.ここでは取り上げませんが,任意の軸を基準に回転する行列の式も簡単に見つかると思います.これらの回転行列を組み合わせて基底変換の行列をつくることが出来ます.
剛体変換(rigid-body transformation)
基底変換では座標系のうち,向きしか変換できませんでした.ここで,原点を平行移動させる変換を考えます.このような大きさが変わらず,向きと位置が変わる変換を剛体変換といいます.剛体(rigid body)は力を加えても形状を変えない物体のことで物理シミュレーションでよく使います. 基底変換では向きしか変換できなかったので,これに加えて平行移動する変換をしなければなりません.基底変換ではある点$p$を点$p'$に変換する行列$M$から
$$ \vec{p}' = M\vec{p} $$
と表せました.ここで,この変換に$\vec{t}$の平行移動を追加するには
$$ \vec{p}' = M\vec{p} + \vec{t} $$
とすればよいことになります.これは1次関数の形になっていますね.しかし,このままでは3行3列の行列で表すことができず,扱いづらいものになっています.そこで,4次元の同次座標系(homogeneous coordinate)を使います(同次座標系は斉次座標系とも呼ばれます).4次元の同次座標系を使うと4次元空間の断面と3次元空間の座標を同じ(写像)扱いにすることができます.4次元空間の位置は$w$が追加された$x,y,z,w$で表し,3次元空間との関係は
$$ \left(\begin{matrix}x \newline y \newline z\end{matrix}\right) = \left(\begin{matrix}x/w \newline y/w \newline z/w\end{matrix}\right) $$
となります.通常は$w=1$として
$$ \left(\begin{matrix}x \newline y \newline z\end{matrix}\right) = \left(\begin{matrix}x \newline y \newline z \newline 1\end{matrix}\right) $$
とします.ちなみに$w=0$とすると,無限遠点となり大きさが無限となります.ただし,無限遠点でも向きは存在 しているので,$w=0$の場合は向きだけを表しているとも考えることができます.
4次元の同次座標系を使うことで4行4列の行列を扱えるようになりました.平行移動を行列で表記すると
$$ T = \left(\begin{matrix} 1 & 0 & 0 & t_x \newline 0 & 1 & 0 & t_y \newline 0 & 0 & 1 & t_z \newline 0 & 0 & 0 & 1 \end{matrix}\right) $$
となります.基底変換の行列は回転を表す行列なので,回転行列を$R$とおくと,剛体変換は次のようになります.
$$ \begin{aligned} \vec{p}' = M\vec{p} + \vec{t} = TR\vec{p} &= \left(\begin{matrix} 1 & 0 & 0 & t_x \newline 0 & 1 & 0 & t_y \newline 0 & 0 & 1 & t_z \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix} R _ {11} & R _ {12} & R _ {13} & 0 \newline R _ {21} & R _ {22} & R _ {23} & 0 \newline R _ {31} & R _ {32} & R _ {33} & 0 \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix}p_x \newline p_y \newline p_z \newline 1\end{matrix}\right) \newline &= \left(\begin{matrix} R _ {11} & R _ {12} & R _ {13} & t_x \newline R _ {21} & R _ {22} & R _ {23} & t_y \newline R _ {31} & R _ {32} & R _ {33} & t_z \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix}p_x \newline p_y \newline p_z \newline 1\end{matrix}\right) \end{aligned} $$
アフィン変換(affine transformation)
基底変換の式
$$ \vec{p}' = M\vec{p} $$
は一次変換でした.次に,剛体変換の式は基底変換に平行移動を追加した式
$$ \vec{p}' = M\vec{p} + \vec{t} $$
は1次関数と同じ形になっていましたね.もちろん,これは写像を表しています.この1次関数をベクトル空間で一般化し,一次変換と平行移動を合わせた変換をアフィン変換といいます.よって,剛体変換も基底変換もアフィン変換の特殊な形となります.アフィン変換では回転,平行移動の他に,拡大・縮小,鏡映,せん断があります.
先に鏡映とせん断についてです.鏡映(reflection)は基準となる軸に対して対称となる位置に移動する変換です.例えば,右手座標系と左手座標系を相互に変換する場合は次のような行列になります.
$$ M _ {reflection} = \left(\begin{matrix} 1 & 0 & 0 \newline 0 & 1 & 0 \newline 0 & 0 & -1 \end{matrix}\right) $$
せん断(shearing)はスキュー(skew)変換とも呼ばれ,歪ませる変換です.これは別軸の成分の値が他の軸の成分に影響させることで歪ませます.例えば,$Y$軸方向の値分,$X$軸方向にずらす場合は
$$ M _ {skew} = \left(\begin{matrix} 1 & 1 & 0 \newline 0 & 1 & 0 \newline 0 & 0 & 1 \end{matrix}\right) $$
となります.
次は拡大・縮小です.この行列$S$は次のようになります.
$$ S = \left(\begin{matrix} s_x & 0 & 0 \newline 0 & s_y & 0 \newline 0 & 0 & s_z \end{matrix}\right) $$
拡大・縮小行列で変換してみると
$$ \vec{p}' = S\vec{p} = \left(\begin{matrix} s_x & 0 & 0 \newline 0 & s_y & 0 \newline 0 & 0 & s_z \end{matrix}\right) \left(\begin{matrix}p_x \newline p_y \newline p_z\end{matrix}\right) = \left(\begin{matrix}s_x p_x \newline s_y p_y \newline s_z p_z\end{matrix}\right) $$
となります.この拡大・縮小と回転,平行移動の3つを組み合わせて座標変換行列をつくるのが基本的なやり方となります.式で表すと
$$ \begin{aligned} \vec{p}' &= TRS\vec{p} \newline &= \left(\begin{matrix} 1 & 0 & 0 & t_x \newline 0 & 1 & 0 & t_y \newline 0 & 0 & 1 & t_z \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix} R _ {11} & R _ {12} & R _ {13} & 0 \newline R _ {21} & R _ {22} & R _ {23} & 0 \newline R _ {31} & R _ {32} & R _ {33} & 0 \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix} s_x & 0 & 0 & 0 \newline 0 & s_y & 0 & 0 \newline 0 & 0 & s_z & 0 \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix}p_x \newline p_y \newline p_z \newline 1\end{matrix}\right) \newline &= \left(\begin{matrix} s_x R _ {11} & s_y R _ {12} & s_z R _ {13} & t_x \newline s_x R _ {21} & s_y R _ {22} & s_z R _ {23} & t_y \newline s_x R _ {31} & s_y R _ {32} & s_z R _ {33} & t_z \newline 0 & 0 & 0 & 1 \end{matrix}\right) \left(\begin{matrix}p_x \newline p_y \newline p_z \newline 1\end{matrix}\right) \end{aligned} $$
となります.また,TRSの変換行列において4行目のベクトルは固定値になっているため
$$ TRS = \left(\begin{matrix} s_x R _ {11} & s_y R _ {12} & s_z R _ {13} & t_x \newline s_x R _ {21} & s_y R _ {22} & s_z R _ {23} & t_y \newline s_x R _ {31} & s_y R _ {32} & s_z R _ {33} & t_z \newline \end{matrix}\right) $$
と3行4列の部分だけあればよいことになります.これは変換行列のデータ量を小さくするときに利用します.
TRSの変換行列が得られたので,今度はTRSから拡大・縮小,回転,平行移動に分解することを考えてみましょう.まず,平行移動は$t_x,t_y,t_z$をそのまま取り出すことができます.問題は拡大・縮小と回転です.これを抽出するためには回転行列の性質を利用します.
ここからは少しややこしくなりますが,1つ1つ整理しながら読み進んでもらえるといいかなと思います.
基底変換のところで回転行列について扱いました.$\theta$回転させる場合,反対方向に回転,つまり$-\theta$回転した基底を使えば$\theta$回転することになるということでしたね.さらにいうと,回転した座標を回転した基底で変換すると,回転前と同じ座標になります.ここで注意なのが,回転した基底は回転行列とは異なるということです.基底を表す行列$M$,点を$p$として,回転前の行列$M_1$,回転前の点$p_1$,回転後の行列$M_2$,回転後の点$p_2$とすれば
$$ M_1 p_1 = M_2 p_2 $$
の関係が成り立ちます.また,逆行列を使えば
$$ p_1 = M_1^{-1} M_2 p_2, \quad p_2 = M_2^{-1} M_1 p_1 $$
となります.$\theta$回転する基底を作ると,それは$-\theta$回転した基底ベクトルということでした.この行列の逆行列を考えてみると ,$\theta$回転した基底ベクトルになっているということになります.これはまさに回転後の基底です.ここまでをまとめると,
- 基底を表す行列の逆行列は,回転後の基底である
- 基底を表す行列は,回転後の基底における回転前の基底である
となります.次に回転行列は直交行列の性質を持っています.直交行列はその転置行列を自身にかけると単位行列となるものです.つまり,直交行列の転置行列は逆行列ということになります.
$$ M^TM = MM^T = I, \quad MM^{-1} = M^{-1}M = I, \quad \therefore M^T = M^{-1} $$
また,直交行列の行列式の性質も確認してみましょう.行列$A,B$の行列式の関係は
$$ |AB| = |A||B| $$
です.これに$A=M,B=M^T$を代入すると
$$ |MM^T| = |M||M^T| $$
行列式の性質として行列とその行列の転置行列の行列式は同じです.
$$ |M| = |M^T| $$
よって
$$ |MM^T| = |M||M^T| = |M||M| = |M|^2 $$
となります.一方で
$$ MM^T = I $$
および,単位行列$I$の行列式は$|I|=1$ということがわかっているので
$$ |MM^T| = |I| = 1 $$
となり,
$$ |MM^T| = |I| = |M|^2 = 1 $$
となります.よって,二乗して$1$となる値が直交行列の行列式となるので,$1$と$-1$ということになります.また,回転行列のように行ベクトル,列ベクトルがすべて単位ベクトルであり,互いに直交である場合は行列式の値が$1$になります.
これまで直交行列の性質をみてきました.回転行列は直交行列ですので,直交行列の性質から回転行列の逆行列は転置行列と等しくなります.このことを踏まえて
- 基底を表す行列の逆行列は,回転後の基底である