koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

モデル行列に関するメモ

モデル行列の調理

モデル行列とはオブジェクトをローカル座標からワールド座標に移動する行列であり,具体的には平行移動,回転,拡大縮小の3つを行う行列である.

Unityのシェーダーとしては unity_ObjectToWorld というuniform変数で与えられる.

この行列から情報を抜き出すことを考える.

モデル行列

モデル行列 $\boldsymbol{M}$ の各成分は下記のようになっている.

\begin{equation} \boldsymbol{M} = \begin{pmatrix} m_{00} & m_{01} & m_{02} & t_x \\ m_{10} & m_{11} & m_{12} & t_y \\ m_{20} & m_{21} & m_{22} & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix} \label{ModelMatrix} \end{equation}

また,モデル行列 $\boldsymbol{M}$ は平行移動($\boldsymbol{T}$),回転($\boldsymbol{R}$),拡大縮小($\boldsymbol{S}$)を組み合わせたものであり,拡大縮小,回転,平行移動の順に適用することから,下記のようにも表現できる.

\begin{equation} \boldsymbol{M} = \boldsymbol{T} \boldsymbol{R} \boldsymbol{S} \end{equation}

平行移動行列

平行移動行列 $\boldsymbol{T}$ に関しては $t_x, t_y, t_z$ を用いて表現することが可能である.

\begin{equation} \boldsymbol{T} = \begin{pmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{equation}

Unityのshaderlabとしては平行移動成分は下記のようになる.

float3 translate = unity_ObjectToWorld._m03_m13_m23;

1始まりのswizzle演算子を用いるなら,

float3 translate = unity_ObjectToWorld._14_24_34;

インデックスアクセスを行った後,ベクトルのswizzle演算子を用いるなら,

float3 translate = unity_ObjectToWorld[3].xyz;

ベクトルの積とで求めるなら

float3 translate = mul(unity_ObjectToWorld, float4(0.0, 0.0, 0.0, 1.0)).xyz;

である.

この平行移動成分はオブジェクトのワールド座標であるが,オブジェクトの各頂点のワールド座標ではない. オブジェクトの中心というべきローカル座標の原点がワールド座標においてどこに位置するか,である.

なので,座標表示シェーダー等に用いるにはよいかもしれない.

回転行列

回転行列はX軸まわりの回転行列 $\boldsymbol{R}_x$,Y軸まわりの回転行列 $\boldsymbol{R}_y$,Z軸まわりの回転行列 $\boldsymbol{R}_z$ から成り,UnityはZ軸まわりの回転,Y軸まわりの回転,X軸まわりの回転の順に適用することから,$\boldsymbol{R}$は

\begin{equation} \boldsymbol{R} = \boldsymbol{R}_x \boldsymbol{R}_y \boldsymbol{R}_z \end{equation}

と表現できる.

X軸まわりの回転角を $\theta_x$,Y軸まわりの回転角を $\theta_y$,Z軸まわりの回転角を $\theta_z$ とすると,$\boldsymbol{R}_x$,$\boldsymbol{R}_y$,$\boldsymbol{R}_z$ はそれぞれ,

\begin{equation} \boldsymbol{R}_x = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos \theta_x & -\sin \theta_x & 0 \\ 0 & \sin \theta_x & \cos \theta_x & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{equation}

\begin{equation} \boldsymbol{R}_y = \begin{pmatrix} \cos \theta_y & 0 & \sin \theta_y & 0 \\ 0 & 1 & 0 & 0 \\ -\sin \theta_y & 0 & \cos \theta_y & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{equation}

\begin{equation} \boldsymbol{R}_z = \begin{pmatrix} \cos \theta_z & -\sin \theta_z & 0 & 0 \\ \sin \theta_z & \cos \theta_z & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{equation}

と表現できる.

拡大縮小行列

X軸方向の拡大率を $s_x$,Y軸方向の拡大率を $s_y$,Z軸方向の拡大率を $s_z$ とおくと,拡大縮小行列 $\boldsymbol{S}$ は

\begin{equation} \boldsymbol{S} = \begin{pmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{equation}

と表現できる.

回転行列と拡大縮小行列の積

モデル行列 $\boldsymbol{M} = \boldsymbol{TRS}$ のうち,平行移動は $\boldsymbol{T}$ が担当しており,回転行列 $\boldsymbol{R}$ と 拡大縮小行列 $\boldsymbol{S}$ は左上3x3の成分で構成されるので,

\begin{equation} \boldsymbol{RS} = \begin{pmatrix} m_{00} & m_{01} & m_{02} & 0 \\ m_{10} & m_{11} & m_{12} & 0 \\ m_{20} & m_{21} & m_{22} & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \end{equation}

となる. すなわち,(頑張れば) $m_{ij}$ は $\theta_x$,$\theta_y$,$\theta_z$,$s_x$,$s_y$,$s_z$ を用いて表すことができるはずであるが,とても大変なことは想像に難くない.

モデル行列から拡大縮小行列の各成分を求める

モデル行列 $\boldsymbol{M}$ から拡大縮小行列 $\boldsymbol{S}$ の各成分 $s_x, s_y, s_z$ を求めることを考える. すなわち,$s_x, s_y, s_z$ を $m_{ij}$ で表現することを考える.

ここでは平行移動成分は扱わないため同次座標で考える必要はない. ごちゃごちゃした記述にしないため,3x3の行列で考えることにする.

まず,回転行列 $\boldsymbol{R}$ は任意のベクトル $\boldsymbol{v} = \begin{pmatrix} x & y & z \end{pmatrix}^T$の大きさには影響しない. すなわち,

\begin{equation} \| \boldsymbol{Rv} \| = \| \boldsymbol{v} \| \label{RotateVector} \end{equation}

である. これは回転行列の操作の意味を考えれば自明なことであるが,一応証明する.

まずX軸まわりの回転行列 $\boldsymbol{R}_x$ について,

\begin{equation} \boldsymbol{R}_x \boldsymbol{v} = \begin{pmatrix} x \\ y \cos \theta_x - z \sin \theta_x \\ y \sin \theta_x + z \cos \theta_x \\ \end{pmatrix} \end{equation}

となるので,

\begin{eqnarray} \| \boldsymbol{R}_x \boldsymbol{v} \| & = & \sqrt{x^{2} + (y \cos \theta_x - z \sin \theta_x)^{2} + (y \sin \theta_x + z \cos \theta_x)^{2}} \nonumber \\ & = & \sqrt{x^{2} + (y^{2} \cos^{2} \theta_x - 2yz \cos \theta_x \sin \theta_x + z^{2} \sin^{2} \theta_x) + (y^{2} \sin^{2} \theta_x + 2yz \cos \theta_x \sin \theta_x + z^{2} \cos^{2} \theta_x)} \nonumber \\ & = & \sqrt{x^{2} + y^{2} (\cos^{2} \theta_x + \sin^{2} \theta_x) + z^{2} (\cos^{2} \theta_x + \sin^{2} \theta_x) + 2yz(\cos \theta_x \sin \theta_x - \cos \theta_x \sin \theta_x)} \nonumber \\ & = & \sqrt{x^{2} + y^{2} + z^{2}} \nonumber \\ & = & \| \boldsymbol{v} \| \end{eqnarray}

$\boldsymbol{R}_y$,$\boldsymbol{R}_z$ についても同様に(式の対称性から自明),

\begin{equation} \| \boldsymbol{R}_y \boldsymbol{v} \| = \| \boldsymbol{v} \| \end{equation}

\begin{equation} \| \boldsymbol{R}_z \boldsymbol{v} \| = \| \boldsymbol{v} \| \end{equation}

従って,

\begin{eqnarray} \| \boldsymbol{Rv} \| & = & \| \boldsymbol{R}_x \boldsymbol{R}_y \boldsymbol{R}_z \boldsymbol{v} \| \nonumber \\ & = & \| \boldsymbol{R}_y \boldsymbol{R}_z \boldsymbol{v} \| \nonumber \\ & = & \| \boldsymbol{R}_z \boldsymbol{v} \| \nonumber \\ & = & \| \boldsymbol{v} \| \end{eqnarray}

さて,式\eqref{RotateVector}は任意のベクトル $\boldsymbol{v}$ について成り立つので,$\boldsymbol{v} = \boldsymbol{S} \boldsymbol{e}$ とおくと($\boldsymbol{e}$ もまた任意のベクトル),

\begin{equation} \| \boldsymbol{RSe} \| = \| \boldsymbol{Se} \| \end{equation}

すなわち,

\begin{equation} \left\| \begin{pmatrix} m_{00} & m_{01} & m_{02} \\ m_{10} & m_{11} & m_{12} \\ m_{20} & m_{21} & m_{22} \end{pmatrix} \boldsymbol{e} \right\| = \left\| \begin{pmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & s_z \end{pmatrix} \boldsymbol{e} \right\| \end{equation}

となる. $\boldsymbol{e}$ は任意のベクトルなので,都合良く $\boldsymbol{e} = \begin{pmatrix} 1 & 0 & 0 \end{pmatrix}^T$ を選出すると,

\begin{eqnarray} \left\| \begin{pmatrix} m_{00} & m_{10} & m_{20} \end{pmatrix}^T \right\| & = & \left\| \begin{pmatrix} s_x & 0 & 0 \end{pmatrix}^T \right\| \nonumber \\ s_x & = & \pm \sqrt{m_{00}^2 + m_{10}^2 + m_{20}^2} \end{eqnarray}

となる. 同様に,$\boldsymbol{e} = \begin{pmatrix} 0 & 1 & 0 \end{pmatrix}^T$,$\boldsymbol{e} = \begin{pmatrix} 0 & 0 & 1 \end{pmatrix}^T$ を選出すると,

\begin{equation} s_y = \pm \sqrt{m_{01}^2 + m_{11}^2 + m_{21}^2} \end{equation}

\begin{equation} s_z = \pm \sqrt{m_{02}^2 + m_{12}^2 + m_{22}^2} \end{equation}

となる. Unityとしては負のスケールも許容しているが,計算からはスケール成分の正負までを求めることはできないことに注意.

UnityのShaderlabとしては下記のようになる.

float3 scales = float3(
    length(unity_ObjectToWorld._m00_m10_m20),
    length(unity_ObjectToWorld._m01_m11_m21),
    length(unity_ObjectToWorld._m02_m12_m22));

あるいは,1始まりのswizzle演算子を用いるなら,

float3 scales = float3(
    length(unity_ObjectToWorld._11_21_31),
    length(unity_ObjectToWorld._12_22_32),
    length(unity_ObjectToWorld._13_23_33));

となる.