GLSLのreflectとその計算方法

こちらをみながらOpenGLでBasic Lightまでやっていて、GLSLのふとreflectについて気になったのでメモしておきます。

GLSLのreflectについて

GLSLのreflect関数は、文字通り入力ベクトルに対して反射ベクトルを返す関数です。具体的には下記画像の R を返却します。

reflect vector
reflect関数の動作についての図示

ここで、I は 反射ベクトルを求めたい入力ベクトル、N は反射面の方向を表す法線ベクトルです。 N は正規化されている必要があります。 反射ベクトルなのでINRN が成す角は等しくなります。ちなみにドキュメントに書いてあるとおり、反射ベクトルの計算方法は I - 2.0 * dot(N, I) * Nとなります。

反射ベクトルの導出

なぜ反射ベクトルが I - 2.0 * dot(N, I) * N となるのか導出してみます。

Rを下図のように求めることを考えてみます。ただし導出の兼ね合いで入力ベクトルIは、一旦反転させたものを考えます。

reflect step1
_R_の求め方

反射なのでINRN が成す角は等しいです。そのため上図のようにIからNに対して垂線を考えると、Iとそのベクトルhを2倍したものを足し合わせるとちょうどRになります。

また、下図のようにIからNの垂線の足が成すベクトルnを考えます。nを用いると、Rは 2n - Iと書き直せます。

reflect step2
_n_を用いた_R_の表現

nINに射影したベクトルで、一般に射影ベクトルと呼びます。nは、Nと同じ方向を向いているため、nNの長さがわかれば求まります。 さらに三角関数から、下図のようにnの長さはIの長さを用いて表現できます。

reflect step3
_n_を三角関数を用いて、_I_と、_I_と_N_の成す角で表現する

θはINが成す角で、cosθは内積を用いて下記のように計算できます。

reflect cos
_I_と_N_のなす角と内積

上記と、Nは正規化されているので単位ベクトルなため(つまり長さが1)、先程のベクトルnは下図のように書き表すことができます。

reflect step4
_n_を_I_と_N_で表現する

これでベクトルn既知の(というか入力値)値であるINで表すことができたので、最後に下図のようにRを求めます。

reflect step5
_R_の計算(途中)

ただし最後にIは導出の都合で反転させていたので、下図のようにもう一度反転させます。Iはそのまま符号を入れ替えます。2 * dot(I,N) * Nの方は内積の計算の片方が逆を向くので、こちらもdot(I,N)の符号が入れ替わります。 よって、RI - 2.0 * dot(N, I) * Nであることを示すことができました。

reflect step6
_R_の計算(最終)

まとめ

GLSLのreflect関数について調べ、またその導出を行いました。


Yuichiro MUKAI
Yuichiro MUKAIGame & Web Programmer

シブヤで働くゲームプログラマー. C#(For Unity)をメインに, 趣味でPHPなどを書きます.

Twitter / Facebook