■ 2005年06月15日 [OpenGL][テクスチャ] 第19回 マルチテクスチャ
仕事が遅い
つくづく,自分の仕事の遅さに困り果てています.たった90分の授業1コマの準備に丸一週間費やすなんてことはざらです.こんなことをしていたら,研究はおろか,他の授業にも影響が出ます.以前なら夜に時間を稼ぐなんてこともできましたけど,最近はそういうことをすると影響が長引くようになり,なかなか思うようにいきません.
そのことを今かかっているお医者さんに話したら,「それは不安感のせいではないか」と指摘されました.確かに,授業にしても研究発表にしても相手がいるわけで,その相手による評価は避けることができません.そして,私はそういうときに,極度の不安感を感じてしまうようです.その結果,準備に不必要なほど神経質になったり,準備そのものに手が付けられなかったりしてしまうんですね.
実はこの blog 自体も,うちの学生さん以外にも読んでいただいている方がいらっしゃることを知って,ここのところかなり気が重くなっていました.有意義なことを書こうと思うのですが,後で読むととても陳腐な内容に思えて情けなくなってしまいまうんです.これは本でも論文でも同じなんですけれど.
お医者さんには「手を抜く方法を見つけなさい」とアドバイスされたんですが,「手抜き」とか言っておきながら,本当はそういうことがなかなか難しい性分だったりするので,悩ましいところです.ま,でも,何もできなくなるのも困るので,学生さんには申し訳ないけど,もうちょっと力を抜いてやっていこうと思っています.この blog も,本来それを必要としている学生さんのニーズに絞って書くことにしたら,また何とか書き始められました.
ということで,今回はマルチテクスチャです.マルチテクスチャについては,以前にも簡単なサンプルを書きましたが,今回はもう少し実際的なサンプルを使った説明を書いてみたいと思います.
テクスチャをマッピングした箱を描く
雛形として,とりあえず下の左のようなテクスチャをマッピングした箱を描くプログラムを使うことにします.
複数のテクスチャを重ねてマッピングする
この箱にキューブマッピングによる環境マッピングを施すことを考えてみましょう.この箱には既にテクスチャがマッピングされています.そこに環境の映り込みを表現しようとすれば,もとのテクスチャの上に環境のテクスチャを重ねてマッピングしなければなりません.その際には重なるテクスチャの合成を行う必要もあります.
マルチテクスチャはこれらの機能を提供します.これは(プログラム可能なハードウェアが出現するまでの)リアルタイムグラフィックスにおいて,中心的な役割を果たすものでした.
Windows の場合
マルチテクスチャはキューブマッピング同様 OpenGL 1.3 で標準機能に取り入れられたので,Visual C++ 6.0 の gl.h には必要な宣言や定義が含まれていません.そこで,今回も OpenGL® Sample Implementation にある glext.h を #include します.また,マルチテクスチャで用いる glActiveTexture() も用意されていないので,この関数ポインタ変数の定義も追加しておきます.
#include <stdio.h> #include <stdlib.h> #include <math.h> #if defined(WIN32) //# pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") # include "glut.h" # include "glext.h" PFNGLACTIVETEXTUREPROC glActiveTexture; #elif defined(__APPLE__) || defined(MACOSX) # include <GLUT/glut.h> #else # include <GL/glut.h> #endif ・・・
テクスチャユニット
テクスチャマッピングの処理を行うハードウェアのことを,テクスチャユニットと呼びます.マルチテクスチャは,複数のテクスチャユニットの処理結果を合成することで実現されます.このテクスチャユニットの数はグラフィックスハードウェアごとに異なっており,これが重ね合わせることのできるテクスチャの枚数になります.使用できるテクスチャユニットの数は,glGetIntegerv(GL_MAX_TEXTURE_UNITS, &n) によって,GLint 型の変数 n に得ることができます.
テクスチャユニットのうち,0番目のテクスチャユニット(テクスチャユニット0)は最初から利用可能になっており,これがベースのテクスチャマッピングとして使用されます.テクスチャユニット1以上を使用する場合は,glActiveTexture() によって指定します.glActiveTexture() の呼び出しによって,それ以降のテクスチャ画像やテクスチャ座標の自動生成,テクスチャ環境,テクスチャ変換行列に関連した命令を,指定したテクスチャユニットが実行するようになります.
テクスチャユニットを指定したテクスチャの設定
それでは,雛形のプログラムの初期化の関数 init() の中にある,ベースのテクスチャを設定している部分に続けて,キューブマッピングの設定を追加します.この設定はテクスチャユニット1に対して行うので,glActiveTexture() を使って,テクスチャユニット1を指定します.なお Windows の場合は,これに先立って関数ポインタ変数 glActiveTexture に glActiveTexture() の実体のエントリポイントを代入しておきます.
・・・ /* ** 初期化 */ static void init(void) { /* テクスチャの読み込みに使う配列 */ GLubyte texture[TEXHEIGHT * TEXWIDTH * 4]; FILE *fp; /* テクスチャ画像はワード単位に詰め込まれている */ glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* テクスチャ画像の読み込み */ if ((fp = fopen(texture1, "rb")) != NULL) { fread(texture, sizeof texture, 1, fp); fclose(fp); } /* テクスチャの割り当て */ 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_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* テクスチャの繰り返し方法の指定 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); /* テクスチャ環境 */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); /* テクスチャユニット1用のテクスチャオブジェクトを作成する */ GLuint texname[1]; glGenTextures(1, texname); #if defined(WIN32) glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture"); #endif /* テクスチャユニット1に切り替える */ glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_CUBE_MAP, texname[0]);
そしてこの後にキューブマッピングの設定を行います.この設定はテクスチャユニット1に対して行われます.テクスチャオブジェクトの場合と異なり,テクスチャ環境はテクスチャユニットごとに設定されます.
for (int i = 0; i < 6; ++i) { /* テクスチャファイル名 */ static const char *textures[] = { "room2ny.raw", /* 下 */ "room2nz.raw", /* 裏 */ "room2px.raw", /* 右 */ "room2pz.raw", /* 前 */ "room2nx.raw", /* 左 */ "room2py.raw", /* 上 */ }; /* テクスチャのターゲット名 */ static const int target[] = { GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, }; if ((fp = fopen(textures[i], "rb")) != NULL) { /* テクスチャ画像の読み込み */ fread(texture, 128 * 128 * 4, 1, fp); fclose(fp); /* キューブマッピングのテクスチャの割り当て */ glTexImage2D(target[i], 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture); } } /* テクスチャを拡大・縮小する方法の指定 */ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* テクスチャの繰り返し方法の指定 */ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP); /* テクスチャユニット1のテクスチャ環境 */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); /* キューブマッピング用のテクスチャ座標を生成する */ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
テクスチャユニット1に対する設定が終わったら,現在のテクスチャユニットをテクスチャユニット0に戻しておきましょう.
/* テクスチャユニット0に戻す */ glActiveTexture(GL_TEXTURE0); /* 初期設定 */ glClearColor(0.3, 0.3, 1.0, 0.0); glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); /* 光源の初期設定 */ glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, lightcol); glLightfv(GL_LIGHT0, GL_SPECULAR, lightcol); glLightfv(GL_LIGHT0, GL_AMBIENT, lightamb); } ・・・
テクスチャを合成してマッピングする
テクスチャの設定が完了したら,今度は図形の表示の際に複数のテクスチャユニットを有効にして,合成したテクスチャがマッピングされるようにします.テクスチャユニット0はもとから有効になっているので,テクスチャユニット1を有効にする処理を追加します.
・・・ /* ** シーンの描画 */ 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_TEXTURE_2D); /* テクスチャユニット1に切り替える */ glActiveTexture(GL_TEXTURE1); /* キューブマッピング開始 */ glEnable(GL_TEXTURE_CUBE_MAP); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); /* トラックボール処理による回転 */ glMultMatrixd(trackballRotation()); /* 箱を描く */ 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_CUBE_MAP); /* テクスチャユニット0に戻す */ glActiveTexture(GL_TEXTURE0); /* テクスチャマッピング終了 */ glDisable(GL_TEXTURE_2D); } ・・・
環境をマッピングする際のテクスチャ環境に GL_MODULATE を使っているので,模様の付いたメタリックな表面に環境が映り込んでいるような結果になります.非金属のつややかな表面への映り込みの場合は,もとのテクスチャに環境のテクスチャを加算する必要がありますが,これに関しては次回をお楽しみに.