あああもう年末だ
学生さん向けにテクスチャマッピングの解説文を書き始めたのは、確か今年の夏休みだったような気がします。なのに、なぜか今日は御用納めの日です。スバラシイ。「まぁ、なんてことでしょう」(加藤みどり調)。などと悠長なことをゆうてる場合ではナイ。このまま人生が終わってしまうような不安感に怯える毎日です。
何を自動生成してもらえるのか
この前の投影マッピングの話では、テクスチャ座標の自動生成の前フリとして、「頂点座標をそのままテクスチャ座標に使ったらどうなるか」という例を示しました。しかし、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 texture_file[] = "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()
{
...
/* テクスチャ環境 */
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);
- void 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の型が異なります。
頂点に割り当てたテクスチャ座標や自動生成したテクスチャ座標は、テクスチャ座標生成関数による変換を経て、テクスチャのサンプリングに用いられます。テクスチャ座標の自動生成を行うには、図形の描画時にそれを有効にします。
これは 2004 年の記事です。OpenGL (3.1以降 Core Profile) および OpenGL ES 2.0/3.0 以降では、
glTexGen*()関数は廃止されています。
/*
** シーンの描画
*/
static void scene()
{
static const GLfloat color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; /* 材質 (色) */
/* 材質の設定 */
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 = params[0] * x + params[1] * y + params[2] * z + params[3] * w;
先のプログラムでは、s に対するテクスチャ座標生成関数のパラメータを (1, 0, 0, 0) に、t に対するテクスチャ座標生成関数のパラメータを (0, 1, 0, 0) に設定しているので、s = (1, 0, 0, 0)・(x, y, z, w) = x、t = (0, 1, 0, 0)・(x, y, z, w) = y となり、頂点の座標値の (x, y) が、そのまま (s, t) として用いられます。
投影マッピング
自動生成したテクスチャ座標に対してテクスチャ変換行列による変換を行おうとする場合には、(s, t) に加えて r と q についてもテクスチャ座標を自動生成しておいた方が無難です。そこで、まず r と q 方向のテクスチャ生成関数のパラメータを追加します。
/*
** テクスチャ
*/
#define TEXWIDTH 256 /* テクスチャの幅 */
#define TEXHEIGHT 256 /* テクスチャの高さ */
static const char texture_file[] = "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()
{
...
/* テクスチャ環境 */
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()
{
static const GLfloat color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; /* 材質 (色) */
/* 材質の設定 */
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 のコールバック関数 **
*************************/
/* アニメーションのサイクル */
#define FRAMES 360
static void display()
{
/* フレーム数をカウントして時間として使う */
static int frame = 0; /* フレーム数 */
double t = (double)frame / (double)FRAMES; /* 時間とともに 0→1 に変化 */
/* アニメーションのサイクルごとにフレーム数をリセットする */
if (++frame >= FRAMES) frame = 0;
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* 視点の移動(物体の方を奥に移動)*/
glTranslated(0.0, 0.0, -3.0);
//gluLookAt(1.5, 2.0, 2.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
/* トラックボール処理で図形を回転 */
//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());
/* 画面クリア */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* シーンの描画 */
scene();
/* ダブルバッファリング */
glutSwapBuffers();
}
これをコンパイルして実行して、以前と同じ結果が得られることを確かめてください。

