GLSLのreflectとその計算方法

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

GLSLのreflectについて

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

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

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

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

反射ベクトルの導出

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

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

_R_の求め方

_R_の求め方

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

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

_n_を用いた_R_の表現

_n_を用いた_R_の表現

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

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

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

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

_I_と_N_のなす角と内積

_I_と_N_のなす角と内積

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

_n_を_I_と_N_で表現する

_n_を_I_と_N_で表現する

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

_R_の計算(途中)

_R_の計算(途中)

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

_R_の計算(最終)

_R_の計算(最終)

まとめ

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