テクスチャ座標の取り方
テクスチャに使う画像はテクスチャ空間中の2点 (0, 0), (1, 1) を対角線とする正方形の領域に配置されていても、ポリゴンの頂点に対応付けるテクスチャ座標が必ずこの領域内にある必要はありません。試しに、テクスチャ座標にこの領域外の座標値を設定してみましょう。

アルファテストが有効になっていると、ポリゴン全体が見えないので、ここでは一旦オフにしておきます。
/*
** シーンの描画
*/
static void scene()
{
static const GLfloat color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; /* 材質 (色) */
/* 材質の設定 */
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
/* アルファテスト無効 */
glDisable(GL_ALPHA_TEST);
...
}
すると、画像の領域外には、同じ画像が繰り返しマッピングされます。

この繰り返しを行わないようにするには、glTexParameteri() を使って GL_TEXTURE_WRAP_S と GL_TEXTURE_WRAP_T に GL_CLAMP を指定します。
/*
** 初期化
*/
static void init()
{
...
/* テクスチャの割り当て */
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXWIDTH, TEXHEIGHT, 0,
GL_RGBA, GL_UNSIGNED_BYTE, texture);
/* テクスチャを拡大・縮小する方法の指定 */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
/* テクスチャの繰り返しの指定 */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
...
}
GL_TEXTURE_WRAP_S はテクスチャ空間 (s, t) の s 軸方向の繰り返し、GL_TEXTURE_WRAP_S は t 軸方向の繰り返しを指定します。GL_CLAMP を指定すると、テクスチャは繰り返されずに、画像の最外周の色が拡張されます。GL_REPEAT を指定すると、テクスチャは繰り返しマッピングされます。初期値は GL_REPEAT になっています。

テクスチャ変換行列
実は、OpenGL ではテクスチャ空間も、物体の空間 (x, y, z, w) と同じように4次元 (s, t, r, q) になっています。そして、ここでは物体の空間と同じ座標変換を行うことができます。テクスチャ空間の座標変換を行う変換行列を、テクスチャ変換行列(テクスチャマトリックス)と呼びます。
それでは、タイヤのテクスチャがポリゴンの中央に来るように、テクスチャ変換行列を設定してみましょう。透視変換やモデルビュー変換と同様に glMatrixMode() を使って変換行列を切り替えるので、後でプログラムを見たときにそのことを把握しやすいよう、これまでと違うところに手を加えます。
/*************************
** GLUT のコールバック関数 **
*************************/
static void display()
{
/* モデルビュー変換行列の設定 */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 光源の位置を設定 */
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
/* 視点の移動(物体の方を奥に移動)*/
glTranslated(0.0, 0.0, -3.0);
/* トラックボール処理で図形を回転 */
glMultMatrixd(trackballRotation());
/* テクスチャ変換行列の設定 */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslated(-0.5, -0.5, 0.0);
/* 画面クリア */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* シーンの描画 */
scene();
/* ダブルバッファリング */
glutSwapBuffers();
}
glMatrixMode(GL_TEXTURE) で設定する変換行列をテクスチャ変換行列に切り替えます。glLoadIdentity() で初期化するのは、モデルビュー変換行列や透視変換行列と同じです。そのあと、glTranslated() を使って、テクスチャ空間上で平行移動を行っています。

んで、ちょいとややこしいのは、この変換においてテクスチャ空間がスクリーン空間に相当しているところにあります。つまり、ポリゴンの頂点に割り当てられたテクスチャ座標に対してテクスチャ変換を行って、テクスチャ空間上の座標値を得ます。そして、この座標値のところの画像の色を拾って、テクスチャマッピングを行っています。
これは、テクスチャ画像はテクスチャ空間中で静止しており、ポリゴンの頂点に対応付けたテクスチャ座標値がこの変換によって変更される、と考えれば理解しやすいのではないかと思います。

タイヤは回るもの
平行移動ができるなら回転だってできます。ということで、このタイヤを回してみましょう。雛形のプログラム自体はもとからアニメーション対応になっています。あとは、フレーム数をカウントするなり ftime()(Windows なら _ftime())なりを使って実時間を計るなどして、フレームごとの経過時間を求めます。ここでは(マシンスピードに依存してしまいますが)フレーム数をカウントすることにします。
/*************************
** 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();
この t を使って、テクスチャ座標を回転します。まず、中央に原点が来るようにテクスチャ座標を (-1, -1) に平行移動します。そしてこのテクスチャ座標を原点を中心に回転します。最後にこの原点がテクスチャの中心と一致するよう (0.5, 0.5) に平行移動します。なお、このプログラムのコーディング上の順序は、頂点の座標値の変換と同様に、これと逆順になることに注意してください。
/* テクスチャ行列の設定 */
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslated(0.5, 0.5, 0.0); /* 原点をテクスチャの中心に移動 */
glRotated(t * 360.0, 0.0, 0.0, 1.0); /* 回転 */
glTranslated(-1.0, -1.0, 0.0); /* テクスチャ座標の中央を原点に移動 */
/* 画面クリア */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* シーンの描画 */
scene();
...
回りました?これは図形(ポリゴン)の方をまったく操作しておらず、ポリゴン自体は回っていません。でも、アルファテストを有効にしてみると…
/*
** シーンの描画
*/
static void scene()
{
...
/* アルファテスト開始 */
glEnable(GL_ALPHA_TEST);
/* テクスチャマッピング開始 */
glEnable(GL_TEXTURE_2D);
...
デモプログラム
ここまでの内容でデモプログラムを作ってあります。ただし、1枚のポリゴンでは面白くないので、箱にしてあります。

今後の予定
今後は以下の内容について書くつもりですが、10月末までにどうしてもやってしまいたい(しまわなければならない)ことがありますので、しばらくそちらに集中します。
- テクスチャ座標の自動生成
- スフィアマッピング
- キューブマッピング
- プロジェクションマッピング
- 複数のテクスチャを使う
- テクスチャの部分的変更
- テクスチャオブジェクト
- マルチテクスチャ
- レンダリング結果をテクスチャに使う
- プログラマブルシェーダ(GLSL - できるかな?)