ブログランキング・にほんブログ村へ


iPhone/iPad用潜水艦ゲームアプリ ソナーエコー iTunesにて公開中

2013年05月23日

[OpenGL ES2.0]gl_NormalMatrix 逆転置行列の正体

OpenGLシェーダの勉強をしてると、gl_NormalMatrixとかglModelViewMatrixとか他にもいろいろ予約済み定義済みの
変数名が出てくるわけだけど、同じOpenGLでも ESがついちゃうとこういうのが全然存在しない。
glPushMatrixとかないくらいだし。

じゃぁどうすんのかっていうと自分で勝手に適当な名前で uniformしてね、ってことになるわけだけど
glModelViewMatrixはともかくとしてgl_NormalMatrix、こいつがなんなのかずっとすっきりしてなかった。

だって法線専用マトリクスって、法線も同じ空間にあるわけだからモデルビューマトリクスで変換でいいじゃん。って思うわけ。
実際シェーダで法線用マトリクスの代わりにモデルビューマトリクスを使っても(vec4にいったん拡張して)結果に変わりはない。
しかもなんかこれ、モデルビューマトリクスの逆転置行列とかなんか難しいこといってるし。

reflectを扱うにあたって、ここ、いい加減なままだと気持ち悪いんできちんと理解することにした。




色々調べてみて現時点で一応納得いった説明を自分の理解でまとめてみて、
腑に落ちなかったものを
腑に落ちるものに変換する。
自分なりに。



まず、前提として法線の変換も基本的にモデルビューマトリクスでいい。

ただ、法線はベクトルで位置情報がないんで、vec4でなくvec3である。だから変換マトリクスもmat4でなく移動(translate)成分のないmat3になる(mat4の左上3x3を取ってきたもの)必要がある。

じゃあどうしてモデルビューマトリクスを単にmat4 -> mat3じゃだめなのかっていうと、スケーリングが関係する。


線分 p0 - p1のxのみを2倍したところを図にしてみた。それと同じ変換を法線ベクトルにも施す。



そうすると、法線ベクトルは直角じゃなくなってて、もはや法線ではなくなってしまってる。


自分の実験で、モデルビューマトリクスで法線も変換しても結果が変わらなかったのはスケーリングせずにメッシュモデルを1:1で表示してたからだったわけだ。



これは補正するにはどうすればいいのかっていうと。



この図でも線分のxを2倍している。
赤が上の図と同じ、xを2倍した法線ベクトル。
そして、緑が、逆にxを1/2倍した法線ベクトル。

こんなふうに線分の倍数の逆数を掛けると、いい具合にスケーリングにも対応できる。


だから、法線の変換マトリクスは、x,y,z軸回転についてはそのまま、スケールについては逆数を掛けたものになってほしい。

そんな都合のいいマトリクス…それが逆転置行列。変換の理屈はよくわかんないけど。
名前がなんか似てるけど逆行列ではない。ろくに調べもしないで逆行列みたいなもんかな、と思ってたから理解が余計におかしくなってたんだけど。

じゃあこれでx,y,z全部が2倍になったりする場合はどうなるの、って考えると法線のx,y,zについての長さが全部1/2になってしまう。つまり法線全体が短くなってしまうので、光源ベクトルとの内積も半分になり、暗くなってしまう。

だから、法線に変換マトリクスを掛けたら必ずnormalizeが必要だったのだ。
OpenGL ES1.1のとき、glScaleで大きくしたモデルが glEnable(GL_NORMALIZE); 無しだと暗くなってしまったのはこういうわけだったのだ。ついにその謎が解けた。


しかし逆に考えると、スケーリングをxだけとかzだけとかしないでいつもx、y、zを同率でやってる場合、こんな難しいこと考えなくてもモデルビューマトリクスの右上3x3でそのまま法線も変換できる。

ただし、この場合、たとえば2倍だと法線のx,y,zも2倍になってしまうから今度は逆に明るくなってしまう。
つまり、やっぱりnormalizeは必要だ。


というわけで今日の勉強、おしまい。
posted by みこあいさ at 19:21| OpenGL