床井研究室

テクスチャを割り当てる

今回は配列に読み込んだ画像をポリゴンに貼り付けます。画像を読み込んだ配列は、コンピュータのメインメモリ上にあります。テクスチャマッピングを行うには、これを OpenGL が管理するテクスチャメモリ(グラフィックスカード上のメモリあるいはグラフィックスカードのドライバ確保したメモリ)に転送して、グラフィックスコントローラから参照できるようにします。

2次元画像をテクスチャとして使う場合、これには glTexImage2D() という関数を使います。他に1次元テクスチャを扱う glTexImage1D() や3次元テクスチャを扱う glTexImage3D() というのもあります。ここでは、ごく普通に2次元画像をテクスチャとして用いますから、glTexImage2D() を使います。

/*
** 初期化
*/
static void init()
{
  /* テクスチャの読み込みに使う配列 */
  GLubyte texture[TEXHEIGHT][TEXWIDTH][3];

  /* テクスチャ画像の読み込み */
  FILE* fp = fopen(texture_file, "rb");
  if (fp != NULL) {
    fread(texture, sizeof texture, 1, fp);
    fclose(fp);
  }
  else {
    perror(texture_file);
  }
  
  /* テクスチャの割り当て */
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXWIDTH, TEXHEIGHT, 0,
    GL_RGB, GL_UNSIGNED_BYTE, texture);

  /* テクスチャを拡大・縮小する方法の指定 */
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

  /* 初期設定 */
  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);
}
void glTexParameteri(GLenum target, GLenum pname, GLint param)
glTexParameteri() はテクスチャマッピングを行う際の種々のパラメータの設定を行います。2次元のテクスチャを扱うときは、引数 targetGL_TEXTURE_2D を指定します。引数 pnameGL_TEXTURE_MAG_FILTER あるいは GL_TEXTURE_MIN_FILTER を指定したときは、param にテクスチャマッピングの際の画像の補間方法を指定します。画像をオブジェクト(物体)にテクスチャマッピングすると、その画像はオブジェクトの見かけの大きさにあわせて拡大あるいは縮小されます。GL_TEXTURE_MAG_FILTER はテクスチャが拡大されるときの補間方法の指定を行い、param には GL_NEAREST(最近傍法)あるいは GL_LINEAR(双線形補間)が指定できます。また GL_TEXTURE_MIN_FILTER はテクスチャが縮小されるときの補間方法を指定を行い、param には GL_NEAREST, GL_LINEAR, および GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR が指定できます。MIPMAP は画像の縮小サンプリングを行う際のエリアシングの発生を抑える手法ですが、ここでは説明を割愛します。詳しくは CG の本なりマニュアルなりを読んでください。
void glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
メモリ上の2次元画像をテクスチャに割り当てます。引数 targetGL_TEXTURE_2D でないといけません。level には MIPMAP を行う場合のテクスチャの解像度レベルを指定します。MIPMAP を行わない場合は 0 にしておいてください。internalFormat は、テクスチャの OpenGL 内部でどのように保持するかを指定します。GL_RGB は RGB 形式で保持します。このほかに GL_RGBA, GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_INTENSITY など、全部で 38 種類のものが指定できます。widthheight は、それぞれテクスチャの幅と高さです。border は境界線の太さです。0 を指定すれば、テクスチャに境界線は付きません。format は引数 pixels に指定したメモリ上の画像の形式です。GL_RGB のほか、GL_RGBA, GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA が指定できます。type には引数 pixel の要素のデータ型を指定します。GL_UNSIGNED_BYTE*pixelsGLubyteunsigned char と等価)であることを示します。他に GL_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, GL_BITMAP が指定できます。pixels にはテクスチャの画像を格納したメモリ(配列)のポインタを指定します。

ふぅ〜、こうなるからテクスチャマッピングに関係する関数の説明ってヤなんだけど、これらの関数の全部の機能を押さえるのは後回しにして、とりあえず上のサンプルプログラムの太字の部分が何をしているのかだけを理解?しておいてください。

これは 2004 年の記事です。OpenGL (3.1以降 Core Profile) および OpenGL ES 2.0/3.0 以降では、GL_ALPHAGL_LUMINANCEGL_LUMINANCE_ALPHAGL_INTENSITYGL_COLOR_INDEXGL_BITMAP は廃止されました。また border も意味を持たなくなりましたので、常に 0 を指定してください。詳しくは OpenGL 4.0 以降の glTexImage2D() のマニュアルを読んでください。

テクスチャ座標と頂点の対応付け

読み込んだ画像は、サイズ(すなわち glTeXImage2D() の引数 widthheight に指定した値)に関係なく、テクスチャ空間 (s, t) 上の 2 点 (0, 0), (1, 1) を対角線とする正方形上に配置されます(これは初期設定であり、画像を配置するテクスチャ空間上の領域は自由に設定できます)。テクスチャマッピングを行うには、ポリゴンの頂点に対して、このテクスチャ空間中の対応する位置を指定します。

テクスチャ空間上の画像

ただし、画像の原点(画像ファイルの最初の画素)の位置は通常画像の左上隅ですが、テクスチャ空間上では原点はテクスチャの左下にあるので、このプログラムの方法で画像を読み込んだ場合、画像は上下反転した形で読み込まれています。ポリゴンの頂点にテクスチャ座標を割り当てるときは、このことを考慮に入れる必要があります。たとえば、上図のように四角形ポリゴンの全面にテクスチャを貼り付けようとしたとき、ポリゴンの左下の頂点 (-1, -1, 0) に対応するテクスチャ座標は、テクスチャの左上 (0, 1) になります。

ポリゴンの頂点にテクスチャ座標を割り当てるには、glVertex3d() などで頂点の位置を指定する前に、その頂点のテクスチャ座標を glTexCoord2d() を使って指定します。またテクスチャを使う前に glEnable(GL_TEXTURE_2D) を呼び出して、テクスチャマッピングを有効にしておきます。

/*
** シーンの描画
*/
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_TEXTURE_2D);

  /* 1枚の4角形を描く */
  glNormal3d(0.0, 0.0, 1.0);
  glBegin(GL_QUADS);
  glTexCoord2d(0.0, 1.0);
  glVertex3d(-1.0, -1.0,  0.0);
  glTexCoord2d(1.0, 1.0);
  glVertex3d( 1.0, -1.0,  0.0);
  glTexCoord2d(1.0, 0.0);
  glVertex3d( 1.0,  1.0,  0.0);
  glTexCoord2d(0.0, 0.0);
  glVertex3d(-1.0,  1.0,  0.0);
  glEnd();
  
  /* テクスチャマッピング終了 */
  glDisable(GL_TEXTURE_2D);
}
 

これでプログラムをコンパイルして実行すれば、テクスチャを貼ったポリゴンが表示されるはずです。

テクスチャを貼った四角形

MIPMAP について

ちなみに MIPMAP が使いたければ、次のようにテクスチャの拡大フィルタ (GL_TEXTURE_MAG_FILTER) や縮小フィルタ (GL_TEXTURE_MIN_FILTER) に線形補間 (GL_LINEARGL_LINEAR_MIPMAP_LINEAR など) を使ってみてください。マッピングされたテクスチャのギザギザが、多少滑らかになります。

  /* テクスチャの割り当て */
  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, TEXWIDTH, TEXHEIGHT,
    GL_RGB, GL_UNSIGNED_BYTE, texture);
 
  /* テクスチャを拡大・縮小する方法の指定 */
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

こんな具合です。

MIPMAP

なお、gluBuild2DMipmaps() を使う場合は、画像のサイズを 2n にあわせる必要はありません。gluBuild2DMipmaps() が自動的に調節してくれます。