GL_OBJECT_LINEAR と GL_EYE_LINEAR
glTexGen*() の引数 pname に GL_TEXTURE_GEN_MODE を指定したとき,引数 param に GL_OBJECT_LINEAR を指定した場合と GL_EYE_LINEAR を指定した場合とでは,結果がどう違うのかを試してみたいと思います.
前回作成したプログラムにおいて,まず main.cpp の関数 scene() で,箱を2つ描くように変更します.ここで2つの箱を配置するために座標変換を追加しているので、その対象の変換行列を GL_MODELVIEW に切り替えます.
/*
** シーンの描画
*/
static void scene()
{
...
/* テクスチャ座標の自動生成を有効にする */
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
/* 箱を2つ描く */
glPushMatrix();
glTranslated(-1.2, 0.0, 0.0);
box(1.0, 1.0, 1.0);
glPopMatrix();
glPushMatrix();
glTranslated(1.2, 0.0, 0.0);
box(1.0, 1.0, 1.0);
glPopMatrix();
/* テクスチャ座標の自動生成を無効にする */
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);
...
}
あと,このままでは図形がウィンドウからはみ出てしまうので,(物体の方を奥に移動して)視点を少し後ろに下げます.display() 関数の中の glTranslated() の第 3 引数(z 値)を -5.0 に変更してください.
static void display()
{
...
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* 視点の移動(物体の方を奥に移動)*/
glTranslated(0.0, 0.0, -5.0);
//gluLookAt(1.5, 2.0, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
これをコンパイルして実行すると,次のような図形が表示されると思います.

glTexGen*() の引数 param に GL_OBJECT_LINEAR を指定した場合は,オブジェクト空間における座標値,すなわちモデルビュー変換前の座標値を用いてテクスチャ座標が生成されます.このため同じテクスチャ座標を設定したオブジェクトが2つあれば,そのそれぞれに同じテクスチャがマッピングされることになります.
では,今度は GL_EYE_LINEAR を使ってみましょう.main.cpp の中の初期化の関数 init() 内にある GL_OBJECT_LINEAR を,GL_EYE_LINEAR に書き換えてください.また GL_OBJECT_PLANE の設定は使わないので,これは #if 0 〜 #endif ではさんで無効にしておきます.
/*
** 初期化
*/
static void init()
{
...
/* テクスチャ環境 */
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
#if 0
/* 混合する色の設定 */
static const GLfloat blend[] = { 0.0, 1.0, 0.0, 1.0 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend);
#endif
/* 頂点の視野空間における座標値をテクスチャ座標に使う */
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
#if 0
/* テクスチャ座標生成関数の設定 */
glTexGendv(GL_S, GL_OBJECT_PLANE, genfunc[0]);
glTexGendv(GL_T, GL_OBJECT_PLANE, genfunc[1]);
glTexGendv(GL_R, GL_OBJECT_PLANE, genfunc[2]);
glTexGendv(GL_Q, GL_OBJECT_PLANE, genfunc[3]);
#endif
/* アルファテストの判別関数 */
glAlphaFunc(GL_GREATER, 0.5);
かわりに GL_EYE_PLANE の設定をビュー変換(視野変換,モデルビュー変換の視点の位置を設定している部分)の後で行います.
static void display()
{
...
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* 視点の移動 */
glTranslated(0.0, 0.0, -5.0);
//gluLookAt(1.5, 2.0, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
/* トラックボール処理で図形を回転 */
//glMultMatrixd(trackballRotation());
/* テクスチャ生成関数の設定 */
glTexGendv(GL_S, GL_EYE_PLANE, genfunc[0]);
glTexGendv(GL_T, GL_EYE_PLANE, genfunc[1]);
glTexGendv(GL_R, GL_EYE_PLANE, genfunc[2]);
glTexGendv(GL_Q, GL_EYE_PLANE, genfunc[3]);
/* テクスチャ行列の設定 */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslated(0.5, 0.5, 0.0);
glRotated(t * 360.0, 0.0, 0.0, 1.0);
glScaled(0.5, 0.5, 1.0);
/* テクスチャ行列に透視変換行列と視野変換行列を掛ける */
gluPerspective(60.0, 1.0, 0.1, 10.0);
gluLookAt(0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
/* トラックボール処理でテクスチャを回転 */
glMultMatrixd(trackballRotation());
/* 画面クリア */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* シーンの描画 */
scene();
/* ダブルバッファリング */
glutSwapBuffers();
}
すると,こんな図形が表示されます.

少しわかりにくいので,アルファテストをオフにして物体を回転してみると,こんな具合になります.

GL_EYE_LINEAR の場合は,視野空間における座標値,すなわちモデルビュー変換後の座標値を用いてテクスチャ座標が生成されます.このため,すべての物体を含む空間全体に対して,単一のテクスチャをマッピングすることが容易に行えます.したがって投影マッピングを使ってスポットライトを表現するような場合は,こちらの方が便利でしょう.

テクスチャの投影方向を固定する
GL_EYE_LINEAR では,デルビュー変換後の座標値を用いてテクスチャ座標が生成されます.そこでトラックボール処理で図形を回転」の glMultMatrixd( trackballRotation() ); をコメントを外して,その前に GL_EYE_PLANE の設定を置いてみてください,また,テクスチャの投影方向を固定するために,「トラックボール処理でテクスチャを回転」の方の glMultMatrixd( trackballRotation() ); をコメントにして無効にしてください.
static void display()
{
...
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* 視点の移動 */
glTranslated(0.0, 0.0, -5.0);
//gluLookAt(1.5, 2.0, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
/* テクスチャ生成関数の設定 */
glTexGendv(GL_S, GL_EYE_PLANE, genfunc[0]);
glTexGendv(GL_T, GL_EYE_PLANE, genfunc[1]);
glTexGendv(GL_R, GL_EYE_PLANE, genfunc[2]);
glTexGendv(GL_Q, GL_EYE_PLANE, genfunc[3]);
/* トラックボール処理で図形を回転 */
glMultMatrixd(trackballRotation());
/* テクスチャ行列の設定 */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslated(0.5, 0.5, 0.0);
glRotated(t * 360.0, 0.0, 0.0, 1.0);
glScaled(0.5, 0.5, 1.0);
/* テクスチャ行列に透視変換行列と視野変換行列を掛ける */
gluPerspective(60.0, 1.0, 0.1, 10.0);
gluLookAt(0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
/* トラックボール処理でテクスチャを回転 */
//glMultMatrixd(trackballRotation());
...
}
これをコンパイルして実行すると,テクスチャの投影方向は静止したままで,物体に投影されるテクスチャの方を動かすことができます.
テクスチャ生成関数のパラメータに回転の行列を指定する
テクスチャの投影方向を変更するもうひとつの方法は,テクスチャ座標 (s, t, r, q) を生成する際に用いるパラメータ自体に回転の行列を設定する方法です.
\[\left(\begin{matrix} s\\ t\\ r\\ q \end{matrix}\right) = \left(\begin{matrix} genfunc[0][0] & genfunc[0][1] & genfunc[0][2] & genfunc[0][3] \\ genfunc[1][0] & genfunc[1][1] & genfunc[1][2] & genfunc[1][3] \\ genfunc[2][0] & genfunc[2][1] & genfunc[2][2] & genfunc[2][3] \\ genfunc[3][0] & genfunc[3][1] & genfunc[3][2] & genfunc[3][3] \end{matrix}\right) \left(\begin{matrix} x\\ y\\ z\\ w \end{matrix}\right)\]関数 trackballRotation() で得られるトラックボール用の回転を行う行列を使って,GL_EYE_PLANE を設定します.加えて「トラックボール処理で図形を回転」と「トラックボール処理でテクスチャを回転」の両方の glMultMatrixd( trackballRotation() ); をコメントにして無効にします.
static void display()
{
...
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* 視点の移動 */
glTranslated(0.0, 0.0, -5.0);
//gluLookAt(1.5, 2.0, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
/* トラックボール処理で図形を回転 */
//glMultMatrixd(trackballRotation());
/* テクスチャ生成関数の設定 */
glTexGendv(GL_S, GL_EYE_PLANE, trackballRotation() + 0);
glTexGendv(GL_T, GL_EYE_PLANE, trackballRotation() + 4);
glTexGendv(GL_R, GL_EYE_PLANE, trackballRotation() + 8);
glTexGendv(GL_Q, GL_EYE_PLANE, trackballRotation() + 12);
/* テクスチャ行列の設定 */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslated(0.5, 0.5, 0.0);
glRotated(t * 360.0, 0.0, 0.0, 1.0);
glScaled(0.5, 0.5, 1.0);
/* テクスチャ行列に透視変換行列と視野変換行列を掛ける */
gluPerspective(60.0, 1.0, 0.1, 10.0);
gluLookAt(0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
/* トラックボール処理でテクスチャを回転 */
//glMultMatrixd(trackballRotation());
/* 画面クリア */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* シーンの描画 */
scene();
/* ダブルバッファリング */
glutSwapBuffers();
}
これをコンパイルして実行すると,先ほどと同様に物体に投影されるテクスチャの方を動かすことができます.
なお,この行列に透視変換行列を設定して投影マッピングを行うこともできます.しかし,この方法とテクスチャ変換行列に透視変換行列を設定する方法とを比べたとき,どちらにどういうメリットがあるのか私は良く知りません.ただ,テクスチャ生成関数のパラメータを頻繁に変更することはあまり一般的ではないような気がしますし,gluPerspective() などを使って透視変換行列を設定することも面倒に思われます.