音ゲーの出来ない雑魚のブログ

LWJGLとかのことをいうブログ

#OpenGL 0から見る行列変換の仕組み

「0から」と言っていますが、「三角関数(三角比)」と「ベクトル」についてなんとなーく知っていないと、難しいと思います。

OpenGLで物体に変化(例えば、回転、変形など)を起こすには、行列というものを使わなければいけません。行列は、物体の変化だけではなく、カメラや視界の変更にも使われているので、CGに必須の技術です。(高校数学では、数Cの内容です)

まず行列とは、数字を""と""に沿って四角に並べたものです。は行列の「横」の並び、は行列の「縦」の並びです。行列の一つ一つの数字を成分と言います。


また、M×N行列というのは、M個があって、N個があるということです。

f:id:cakkby:20160422223245p:plain

OpenGLで使う「行列」は、次のような仕組みです。

f:id:cakkby:20160422223954p:plain

まず、OpenGLでは 4 × 4 行列を使います。その時、"列"で並べるようにします。(列優先

このままでは、数字の並びだけで何なのか意味分かんないですが、これらは「4つ(列)のベクトル」を表しているのです。
1列目のベクトルは、X軸の向く方向です。これは、座標を-1~1の間で表し、w成分は 0 (方向)です。
2列めはY軸の、3列目はZ軸の向く方向を表しています。
そして、4列目では、平行移動のためのベクトル(座標)が用意されています。
*1

これらの値を変えることで様々な変換をすることが出来ます。



行列による物体の動きを考える前に、
opengl-tutorial.orgの、 チュートリアル3:行列 にある引用をこちらでも引用させてもらいます。

エンジンは船を動かさない。船はそこにあり、エンジンは船の周りで世界を動かす。
The engines don’t move the ship at all. The ship stays where it is and the engines move the universe around it.

by フューチュラマ(Futurama)

つまり、

(CGの)世界の中では、物体が動いているのではなく、世界(座標軸)が動いているから、物体が動いているように見えるのだ

ということです。この思想は大切だと思います。





では、行列を使った物体の動きを考えます。(今回はクォータニオンを使っていません)

まず、物体における座標軸を定めます。物体の基本的な座標軸は"単位行列"と呼ばれています。

f:id:cakkby:20160423004838p:plain

そして、物体を動かす時の変換を"アフィン変換"と言います。基本的なアフィン変換(平行移動、拡大、回転、剪断)は次のように示されます。

(ただ、その動きを「座標軸の動き(世界の動き)」と考えるとわかりやすいので、それも足しました。「原理」などと書いてありますが、実際の行列式から考えたものです。)


f:id:cakkby:20160423003356p:plain

平行移動は、「座標軸をずらす」ことで物体が動いたようにできます。このとき、平行移動ベクトルを使います。


f:id:cakkby:20160423205659p:plain

拡大縮小は、「座標軸を拡大縮小する」ことで、相対的に物体も大きくなります。このとき、座標軸を実際に拡大するのもいいのですが、16番目の要素(一番最後)の値を0に近づけることでも、拡大縮小を表すことが出来ます。ただその時は世界(座標)全体が一様に拡大されてしまうので、個別で変えるには、こちらのほうが楽です。


f:id:cakkby:20160423221220p:plain

せん断(スキュー)は、「座標軸を傾ける」ようにすれば出来ます。変形は頂点座標を変化させるという手もありますが、せん断で済むなら、そっちのほうが楽だとは思います。

この例以外のせん断も、軸が傾くということを考えれば、やり方はなんとなくわかると思います。


f:id:cakkby:20160513233448p:plain

(X,Y,Z軸の周りを)回転するには、「座標軸ごと回す」と出来ます。三角関数(sin/cos)をたくさん使っています。


ここまでで、基本的な物体の動きは表せました。





しかし、「任意軸中心の回転」については、軸だけを考えてできるわけではないです。

f:id:cakkby:20160428001655p:plain

まず、任意軸上で、ふつうの回転を考えます。それを、実座標系に持って行って考えるようにします。
この変換は、「ロドリゲスの回転公式」というそうです。

この変換処理も、実際には「座標軸が任意軸の周りを回転している」という事になります。


また、回転×拡大のような変換も、順序を考えながらやれば出来ます。また、そのような計算をするとCPUなどに負荷がかかるので、最初から計算しておいた行列を使うという手もあります。





ここまでは、物体の変形やカメラの移動というような変換(モデルビュー変換という)をやりました。

次は、遠近法などの「世界の見え方」を行列で表します。(投影変換という)
「世界の見え方」は主に2つあって、

  • 平行投影 : 遠くも同じ大きさに見えるようにします。見取り図とかでよく使います。
  • 透視投影 : 遠くのほうが小さく見えるようにします。いわゆる遠近法で、実世界はこっちの方です。

また、これらの投影は視体積(物体が映る範囲の体積、大きいとそれだけ処理も重くなります)も表しています。

投影変換をする前は、X,Y,Zがそれぞれ -1.0~1.0 の間の空間のみが視体積で、これをクリッピング空間(正規化デバイス座標とも)と言います。

f:id:cakkby:20160511223213p:plain
(急に座標軸の向きが変わっててすみません)

そして、平行投影は次のような行列で表せます。

f:id:cakkby:20160511223306p:plain

正規化というのは、クリッピング空間(-1~1)の大きさに縮小(拡大)することです。


また、透視投影に関しては少し難しいですが、次のように表せます。

f:id:cakkby:20160511223404p:plain

Z軸を座標にする*2ことでZが遠くになるほど物体が大きくなるという考え方が少し難しいです…
また、視体積内のX(Yにも当てはまる)においては、次のような感じになっています。

f:id:cakkby:20160511223542p:plain

実際には、これらを正規化しなければいけないため、もう少し複雑になっていますが、基本的には相似を使っています。





このような感じで様々な変換を行列によって表せます。最後に行くにつれて謎な感じになってしまいましたが、一応
 どれだけ物体を変換しても行列で表せる
 すべての変換は座標軸の動きで出来ている


参考:
ロドリゲスの回転公式:ロドリゲスの回転公式   任意軸回転を表す行列  - 理工系数学のアラカルト -
投影 : 床井研究室 - 第5回 座標変換
ありがとうございました

*1: ベクトルのw成分は、軸全体の伸び(拡大率)を表していて(4次元という意味ではありません!)、"1"であるとX,Y,Z成分がそのまま実座標となり、"0"に近づくに連れて軸が伸びていきます。そして、"0"になると、軸が無限遠にまで伸びていくので、座標を表すことが出来ず、その代わりに「方向」を表すようになるのです。

*2:w=-1(逆向きの座標)にしています