«トリビア 最新 第8回 テクスチャの投影方向の変更»

床井研究室

※このブログは遅くとも 2027 年 3 月に管理者の定年退職により閉鎖します (移転先は管理者本人共々模索中)

■ 2004年12月28日 [OpenGL][テクスチャ] 第7回 テクスチャ座標の自動生成

2005年07月05日 13:02更新

あああもう年末だ

学生さん向けにテクスチャマッピングの解説文を書き始めたのは,確か今年の夏休みだったような気がします.なのに,なぜか今日は御用納めの日です.スバラシイ.「まぁ,なんてことでしょう」(加藤みどり調).などと悠長なことをゆうてる場合ではナイ.このまま人生が終わってしまうような不安感に怯える毎日です.

何を自動生成してもらえるのか

この前の投影マッピングの話では,テクスチャ座標の自動生成の前フリとして,「頂点座標をそのままテクスチャ座標に使ったらどうなるか」という例を示しました.しかし,OpenGL のテクスチャ座標を自動生成する機能を使うなら,この作業は不要です.この例は自動生成という機能がしてくれることを理解してもらうつもりで書いてみたんですが,今回の話をすぐに続けて書かないと意味が無いですよね.ごめんなさい.

ということで,「頂点座標をテクスチャ座標に使うことなど,プログラミングするまでも無く,OpenGL 側で自動的にやってくれる」んですね.じゃあ,試しにやってみましょう.雛形に使うプログラムはこの前の話と同様に,テクスチャ座標の話の最後のところで用意したサンプルプログラムを使います.

これをコンパイルして実行すると,立方体の6つの面のそれぞれにマッピングされたタイヤの画像がくるくる回っているアニメーションが表示されます.例によって,マウスでドラッグして,物体を回すことができます.

テクスチャ座標の自動生成機能を有効にする

それでは,この立方体に対してテクスチャ座標の自動生成を行ってみます.このサンプルプログラムに含まれる box.cpp の glTexCoord2dv() の行を削除するか,あるいは #if 0 〜 #endif ではさんで無効にしてください.

・・・
  
/*
** 箱の描画
*/
void box(double x, double y, double z)
{
  ・・・
  
  /* 四角形6枚で箱を描く */
  glBegin(GL_QUADS);
  for (j = 0; j < 6; ++j) {
    glNormal3dv(normal[j]);
    for (i = 0; i < 4; ++i) {
#if 0
      /* テクスチャ座標の指定 */
      glTexCoord2dv(texcoord[j][i]);
#endif
      /* 対応する頂点座標の指定 */
      glVertex3dv(vertex[j][i]);
    }
  }
  glEnd();
}
  
・・・

次に main.cpp の先頭部分あたりで,テクスチャ生成関数のパラメータを定義しておきます.

・・・
  
/*
** テクスチャ
*/
#define TEXWIDTH  256                      /* テクスチャの幅    */
#define TEXHEIGHT 256                      /* テクスチャの高さ   */
static const char texture1[] = "tire.raw"; /* テクスチャファイル名 */
static double genfunc[][4] = {   /* テクスチャ生成関数のパラメータ */
  { 1.0, 0.0, 0.0, 0.0 },
  { 0.0, 1.0, 0.0, 0.0 },
};
  
・・・

そして初期化の関数 init() で,このパラメータを使ってテクスチャの生成関数を設定します.

・・・
  
/*
** 初期化
*/
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_OBJECT_LINEAR);
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  
  /* テクスチャ座標生成関数の設定 */
  glTexGendv(GL_S, GL_OBJECT_PLANE, genfunc[0]);
  glTexGendv(GL_T, GL_OBJECT_PLANE, genfunc[1]);
  
  /* アルファテストの判別関数 */
  glAlphaFunc(GL_GREATER, 0.5);
  
・・・
glTexGeni(GLenum coord, GLenum pname, GLint param)
glTexGen*() 関数はテクスチャ座標生成関数の設定を行います.引数 coord には,生成するテクスチャ座標 (s, t, r, q) を,GL_S, GL_T, GL_R, GL_Q によって指定します.引数 pname には,glTexGeni() 関数では GL_TEXTURE_GEN_MODE のみが指定できます.その場合,引数 param にはテクスチャ座標の生成方法として GL_OBJECT_LINEAR, GL_EYE_LINEAR, あるいは GL_SPHERE_MAP のいずれかを指定します.同じ機能の関数として glTexGenf() および glGexGend() がありますが,引数 param の型が異なります.
glTexGendv(GLenum coord, GLenum pname, const GLdouble *params)
テクスチャ座標生成関数のパラメータを設定するには,glTexGendv() 関数の引数 pname に GL_OBJECT_PLANE あるいは GL_EYE_PLANE を指定し,params に4要素の配列を指定します.GL_OBJECT_PLANE を指定して設定したパラメータは,GL_TEXTURE_GEN_MODE に GL_OBJECT_LINEAR を指定したときに使用され,GL_EYE_PLANE を指定して設定したパラメータは,GL_TEXTURE_GEN_MODE に GL_EYE_LINEAR を指定したときに使用されます.同じ機能の関数として glTexGenfv() および glTexGeniv() がありますが,引数 params の型が異なります.

頂点に割り当てたテクスチャ座標や自動生成したテクスチャ座標は,テクスチャ座標生成関数による変換を経て,テクスチャのサンプリングに用いられます.テクスチャ座標の自動生成を行うには,図形の描画時にそれを有効にします.

・・・
  
/*
** シーンの描画
*/
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);
  
  /* 箱を描く */
  box(1.0, 1.0, 1.0);
    
  /* テクスチャ座標の自動生成を無効にする */
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  
  /* テクスチャマッピング終了 */
  glDisable(GL_TEXTURE_2D);
  
  /* アルファテスト終了 */
  glDisable(GL_ALPHA_TEST);
}
  
・・・

これをコンパイルして実行すると,頂点座標をそのままテクスチャ座標に使った場合と同じ結果が得られます.

正面から平行投影マッピング

テクスチャ座標生成関数のパラメータ

引数 pname に GL_TEXTURE_GEN_MODE を指定し,param に GL_OBJECT_LINEAR を指定して glTexGen*() を呼び出した時には,テクスチャ座標の生成に頂点の物体空間上(モデルビュー変換の前,すなわちローカル座標系)での座標値が用いられます.一方,GL_TEXTURE_GEN_MODE に GL_EYE_LINEAR を指定して glTexGeni() を呼び出した時には,テクスチャ座標の生成に頂点の視野空間上(モデルビュー変換の後,すなわち視点座標系)での座標値が用いられます.

生成されるテクスチャ座標は,引数 pname に GL_OBJECT_PLANE あるいは GL_EYE_PLANE を指定して glTexGendv() を呼び出したときに引数 params に与えていた値と,頂点の座標値と積和(内積)で求められます.すなわち,頂点の座標値を (x, y, z, w) とするとき,テクスチャ座標 g は次式により求められます.

g = x * params[0] + y * params[1] + z * params[2] + w * params[3];

先のプログラムでは,s に対するテクスチャ座標生成関数のパラメータを (1, 0, 0, 0) に,t に対するテクスチャ座標生成関数のパラメータを (0, 1, 0, 0) に設定しているので,s = (x, y, z, w)・(1, 0, 0, 0) = x,t = (x, y, z, w)・(0, 1, 0, 0) = y となり,頂点の座標値の (x, y) が,そのまま (s, t) として用いられます.

投影マッピング

自動生成したテクスチャ座標に対してテクスチャ変換行列による変換を行おうとする場合には,(s, t) に加えて r と q についてもテクスチャ座標を自動生成しておいた方が無難です.そこで,まず r と q 方向のテクスチャ生成関数のパラメータを追加します.

・・・
  
/*
** テクスチャ
*/
#define TEXWIDTH  256                      /* テクスチャの幅    */
#define TEXHEIGHT 256                      /* テクスチャの高さ   */
static const char texture1[] = "tire.raw"; /* テクスチャファイル名 */
static double genfunc[][4] = {  /* テクスチャ座標生成関数のパラメータ */
  { 1.0, 0.0, 0.0. 0.0 },
  { 0.0, 1.0, 0.0, 0.0 },
  { 0.0, 0.0, 1.0, 0.0 },
  { 0.0, 0.0, 0.0, 1.0 },
};
  
・・・

そして,これを使って r と q に対するテクスチャ生成関数の設定を追加します.

・・・
  
/*
** 初期化
*/
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_OBJECT_LINEAR);
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  
  /* テクスチャ座標生成関数の設定 */
  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]);
  
  /* アルファテストの判別関数 */
  glAlphaFunc(GL_GREATER, 0.5);
  
・・・

最後に,r と q に対するテクスチャの自動生成を有効にします.

・・・
  
/*
** シーンの描画
*/
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);
  
  /* 箱を描く */
  box(1.0, 1.0, 1.0);
  
  /* テクスチャ座標の自動生成を無効にする */
  glDisable(GL_TEXTURE_GEN_S);
  glDisable(GL_TEXTURE_GEN_T);
  glDisable(GL_TEXTURE_GEN_R);
  glDisable(GL_TEXTURE_GEN_Q);
  
  /* テクスチャマッピング終了 */
  glDisable(GL_TEXTURE_2D);
  
  /* アルファテスト終了 */
  glDisable(GL_ALPHA_TEST);
}
  
・・・
この状態でコンパイルして実行しても,結果は先ほどと変わりません.それでは以前の投影マッピングと同様に,main.cpp を次のように変更してください.
・・・
 
/****************************
** GLUT のコールバック関数 **
****************************/
 
/* トラックボール処理用関数の宣言 */
#include "trackball.h"
 
/* アニメーションのサイクル */
#define FRAMES 360
 
static void display(void)
{
  /* フレーム数をカウントして時間に使う */
  static int frame = 0;
  double t = (double)frame / (double)FRAMES;
  
  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);
  
  /* モデルビュー変換行列の設定 */
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  ・・・

これをコンパイルして実行して,以前と同じ結果が得られることを確かめてください.

テクスチャを広げて立方体の側面にもマッピング

編集 «トリビア 最新 第8回 テクスチャの投影方向の変更»