CDR6275

Michio SHIRAISHI Official Site


OpenGL 4.1とOpenGL ES SL 1.0で学ぶ3次元コンピュータグラフィックス

東邦大学理学部情報科学科 白石路雄
最終更新: 2014年2月20日

4. モデリング変換

4.2 モデル行列

 前回のプログラムでは、初音ミクの形状モデルを読み込みましたが、その際に大きさが画面に収まるように全体を0.07倍しました。3次元モデルは、モデルを作った人が決めた座標系で好きな大きさで作られています。それを描画に使っている座標系(ワールド座標系と言います)に変換して使用します。この変換のことをモデリング変換と呼びます。

座標変換を数式で表す

 さて、3次元モデルが定義されている座標系での座標\((x,y,z)\)から、ワールド座標系での座標\((x^\prime,y^\prime,z^\prime)\)への変換するときに、\(x\)、\(y\)、\(z\)座標を、それぞれ\(s_x\)、\(s_y\)、\(s_z\)倍する変換を考えてみます。前回のように全ての座標を0.07倍する変換では、\(s_x=s_y=s_z=0.07\)でした。

 この変換を数式で表すと以下のようになります。

\[
\begin{aligned}
x^\prime &= s_x x \\
y^\prime &= s_y y \\
z^\prime &= s_z z
\end{aligned}
\]

 このような変換を拡大縮小変換と呼びます。これ以外にも、主な変換として、回転変換と平行移動変換があります。

 回転変換は\(x\)、\(y\)、\(z\)軸のどの軸の周りに回転させるかによって、3種類考えられます。たとえば、\(z\)軸周りの回転は次のような式になります (高校の数学Cで出てきたものを思い出してください)。

\[
\begin{aligned}
x^\prime &= x \cos \theta – y \sin \theta \\
y^\prime &= x \sin \theta + y \cos \theta \\
z^\prime &= z
\end{aligned}
\]

 また、\(x\)、\(y\)、\(z\)軸について、それぞれ\(t_x\)、\(t_y\)、\(t_z\)だけ平行移動する変換は次のような式で表されます。

\[
\begin{aligned}
x^\prime &= x + t_x \\
y^\prime &= y + t_y \\
z^\prime &= z + y_z
\end{aligned}
\]

 これらの変換をまとめて表現する手法を考えます。方法としては、行列を使った演算、たとえば、

\[
\begin{pmatrix}
x^\prime \\
y^\prime \\
z^\prime
\end{pmatrix}
=
\begin{pmatrix}
a_{11} & a_{12} & a_{13} \\
a_{21} & a_{22} & a_{23} \\
a_{31} & a_{32} & a_{33}
\end{pmatrix}
\begin{pmatrix}
x \\
y \\
z
\end{pmatrix}
\]

とできれば、便利なのですが、このような3×3行列では上記の平行移動変換が表現できません。そこで、次のような4×4行列\(M\)によって表現することにします。

\[
\begin{pmatrix}
x^\prime \\
y^\prime \\
z^\prime \\
1
\end{pmatrix}
=
M
\begin{pmatrix}
x \\
y \\
z \\
1
\end{pmatrix}
\]

 ここで、拡大縮小変換を表す行列、\(z\)軸周りの回転を表す行列、平行移動変換を表す行列は、それぞれ次のようになります (それぞれ上記の式になることを確認してください)。

 拡大縮小変換を表す行列:

\begin{pmatrix}
s_x & 0 & 0 & 0 \\
0 & s_y & 0 & 0 \\
0 & 0 & s_z & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}

 \(z\)軸周りの回転を表す行列:

\begin{pmatrix}
\cos\theta & -\sin\theta & 0 & 0 \\
\sin\theta & \cos\theta & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{pmatrix}

 平行移動変換を表す行列:

\begin{pmatrix}
1 & 0 & 0 & t_x \\
0 & 1 & 0 & t_y \\
0 & 0 & 1 & t_z \\
0 & 0 & 0 & 1
\end{pmatrix}

行列を扱うライブラリ

 このチュートリアルを初めから読んでいる方はお気づきかと思いますが、4×4行列の扱いは2.5節で説明した投影変換のところで出てきました。上記の座標変換に対応するメソッドがMatrixクラスに定義されています。

 まず、拡大縮小変換ですが、次のようなメソッドが利用できます。

scaleM(float[] m, int mOffset, float x, float y, float z)

 このメソッドを利用するには、まず16個のfloat型の要素を持つ配列を確保して第1引数として渡し、xyzにそれぞれ拡大縮小率を与えると、mでもともと入っていた行列に上記の拡大縮小変換を表す行列を掛けた行列でmが上書きされます(mOffsetmに与えるオフセットですが通常0でいいはずです)。したがって、mで与える行列は単位行列で初期化しておかなければなりません。

 したがって、拡大縮小変換を行う行列を作るには次のようにする必要があります。

float modelMatrix[16];
Matrix::setIdentityM(modelMatrix, 0);
Matrix::scaleM(modelMatrix, 0, 0.07f, 0.07f, 0.07f);

 また、回転行列を表す行列のために、

rotateM(float[] m, int mOffset, float a, float x, float y, float z)

があります。このメソッドの引数についてですが、aは回転角(ラジアンではなく度で与える)を与えます。また、\(x\)、\(y\)、\(z\)については、回転する軸を設定します。つまり、z軸周りに回転させるときには、xyに0、zに1を与えます。

 また、平行移動変換を表す行列のために、

translateM(float[] m, int mOffset, float x, float y, float z)

があります。このメソッドの引数についてですが、xyz軸に関する平行移動量を与えます。

プログラムの変更点

 今回のプログラム(ModelMatrix)では、前回のプログラムに以下の変更を加えて、前回と同様の機能を実現しています。

 まず、ModelMatrix.hで、メンバ変数として、

float modelMatrix[16];

を追加しました。また、ModelMatrix.cppについては、次のような処理を加えてあります。

シェーダの変更
 これまで使用していた頂点シェーダであるfirst.vsの代わりに、modeling.vsを作成しました。modeling.vsの中身は以下の通りです。

#version 100
attribute vec3 vertexCoordinate;
uniform mat4 modelMatrix;
void main(){
  gl_Position = modelMatrix * vec4(vertexCoordinate, 1.0);
}

この例からも分かるように、OpenGL ES Shading Languageでは、行列の乗算を*で簡単に書くことができます。