■ 2004年12月29日 [OpenGL][テクスチャ] 第8回 テクスチャの投影方向の変更
GL_OBJECT_LINEAR と GL_EYE_LINEAR
glTexGen*() の引数 pname に GL_TEXTURE_GEN_MODE を指定したとき,引数 param に GL_OBJECT_LINEAR を指定した場合と GL_EYE_LINEAR を指定した場合とでは,結果がどう違うのかを試してみたいと思います.前回作成したプログラムにおいて,まず main.cpp の関数 scene() で,箱を二つ描くように変更します.
・・・ /* ** シーンの描画 */ static void scene(void) { static const GLfloat color[] = { 1.0, 1.0, 1.0, 1.0 }; /* 材質 (色) */ /* 材質の設定 */ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); /* アルファテスト開始 */ glEnable(GL_ALPHA_TEST); /* テクスチャマッピング開始 */ glEnable(GL_TEXTURE_2D); /* テクスチャ座標の自動生成を有効にする */ glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_GEN_Q); /* 箱を描く */ 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(); ・・・
あと,このままでは図形がウィンドウからはみ出てしまうので,(物体の方を奥に移動して)視点を少し後ろに下げます.display() 関数の中の以下の部分を,太字のように変更してください.
・・・ static void display(void) { ・・・ /* モデルビュー変換行列の設定 */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* 光源の位置を設定 */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* 視点の移動(物体の方を奥に移動)*/ glTranslated(0.0, 0.0, -5.0); ・・・
これをコンパイルして実行すると,次のような図形が表示されると思います.
glTexGen*() の引数 param に GL_OBJECT_LINEAR を指定した場合は,オブジェクト空間における座標値,すなわちモデルビュー変換前の座標値を用いてテクスチャ座標が生成されます.このため同じテクスチャ座標を設定したオブジェクトが二つあれば,そのそれぞれに同じテクスチャがマッピングされることになります.
では,今度は GL_EYE_LINEAR を使ってみましょう.main.cpp の中の初期化の関数 init() 内にある GL_OBJECT_LINEAR を,GL_EYE_LINEAR に書き換えてください.また GL_OBJECT_PLANE の設定は使わないので,これは #if 0 〜 #endif ではさんで無効にしておきます.
・・・ /* ** 初期化 */ static void init(void) { ・・・ /* テクスチャ環境 */ 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(void) { ・・・ /* モデルビュー変換行列の設定 */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* 光源の位置を設定 */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* 視点の移動 */ glTranslated(0.0, 0.0, -5.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]); /* 画面クリア */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* シーンの描画 */ scene(); /* ダブルバッファリング */ glutSwapBuffers(); } ・・・
すると,こんな図形が表示されます.
少しわかりにくいので,アルファテストをオフにして物体を回転してみると,こんな具合になります.
GL_EYE_LINEAR の場合は,視野空間における座標値,すなわちモデルビュー変換後の座標値を用いてテクスチャ座標が生成されます.このため,すべての物体を含む空間全体に対して,単一のテクスチャをマッピングすることが容易に行えます.したがって投影マッピングを使ってスポットライトを表現するような場合は,こちらの方が便利でしょう.なお,GL_EYE_PLANE の設定を「トラックボール処理による回転」の前に置けば,テクスチャの投影方向を固定した状態で,物体だけを回すことができます.
テクスチャの投影方向を変更する
それでは,今度はマウスで物体を回転する代わりに,テクスチャの投影方向を変更してみましょう.それには二通りの方法があります.ひとつはテクスチャ座標に回転の行列を掛ける方法です.これは display() 関数のモデルビュー変換のところで物体を回転している部分を,テクスチャ変換のところに移動するだけで実現できます.
もともとあった「トラックボール処理による回転」のところを #if 0 〜 #endif ではさんで無効にし,かわりに「テクスチャ行列の設定」の最後の部分に,「トラックボール処理による回転」の部分をコピーしてください.
・・・ static void display(void) { /* フレーム数をカウントして時間として使う */ static int frame = 0; /* フレーム数 */ double t = (double)frame / (double)FRAMES; /* 時間とともに 0→1 に変化 */ if (++frame >= FRAMES) frame = 0; /* テクスチャ行列の設定 */ 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, 1.0, 100.0); gluLookAt(0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); /* トラックボール処理による回転 */ glMultMatrixd(trackballRotation()); /* モデルビュー変換行列の設定 */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* 画面クリア */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* モデルビュー変換行列の初期化 */ glLoadIdentity(); /* 光源の位置を設定 */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* 視点の移動 */ glTranslated(0.0, 0.0, -5.0); #if 0 /* トラックボール処理による回転 */ glMultMatrixd(trackballRotation()); #endif /* テクスチャ生成関数の設定 */ 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]); /* 画面クリア */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* シーンの描画 */ scene(); /* ダブルバッファリング */ glutSwapBuffers(); } ・・・
これをコンパイルして実行すると,物体は静止したままで,物体に投影されるテクスチャの方を動かすことができます.
テクスチャ生成関数のパラメータに回転の行列を指定する
テクスチャの投影方向を変更するもうひとつの方法は,テクスチャ座標 (s, t, r, q) を生成する際に用いるパラメータ自体に回転の行列を設定する方法です.
先ほどテクスチャ変換の設定のところに追加した「トラックボール処理による回転」を,#if 0 〜 #endif ではさんで無効にしてください.そして関数 trackballRotation() で得られるトラックボール用の回転を行う行列を使って GL_EYE_PLANE を設定します.
・・・ static void display(void) { /* フレーム数をカウントして時間として使う */ static int frame = 0; /* フレーム数 */ double t = (double)frame / (double)FRAMES; /* 時間とともに 0→1 に変化 */ if (++frame >= FRAMES) frame = 0; /* テクスチャ行列の設定 */ 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, 1.0, 100.0); gluLookAt(0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); #if 0 /* トラックボール処理による回転 */ glMultMatrixd(trackballRotation()); #endif /* モデルビュー変換行列の設定 */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* 光源の位置を設定 */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* 視点の移動(物体の方を奥に移動)*/ glTranslated(0.0, 0.0, -5.0); #if 0 /* トラックボール処理による回転 */ glMultMatrixd(trackballRotation()); #endif /* テクスチャ生成関数の設定 */ 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); /* 画面クリア */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* シーンの描画 */ scene(); /* ダブルバッファリング */ glutSwapBuffers(); } ・・・
これをコンパイルして実行すると,先ほどと同様に物体に投影されるテクスチャの方を動かすことができます.
なお,この行列に透視変換行列を設定して投影マッピングを行うこともできます.しかし,この方法とテクスチャ変換行列に透視変換行列を設定する方法とを比べたとき,どちらにどういうメリットがあるのか私は良く知りません.ただ,テクスチャ生成関数のパラメータを頻繁に変更することはあまり一般的ではないような気がしますし,gluPerspective() などを使って透視変換行列を設定することも面倒に思われます.