«第1回 画像の読み込み 最新 第3回 テクスチャの合成»

床井研究室

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

■ 2004年09月14日 [OpenGL][テクスチャ] 第2回 テクスチャの割り当て

2014年02月26日 14:15更新

テクスチャを割り当てる

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

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

・・・
 
/*
** 初期化
*/
static void init(void)
{
  /* テクスチャの読み込みに使う配列 */
  GLubyte texture[TEXHEIGHT][TEXWIDTH][3];
  FILE *fp;
  
  /* テクスチャ画像の読み込み */
  if ((fp = fopen(texture1, "rb")) != NULL) {
    fread(texture, sizeof texture, 1, fp);
    fclose(fp);
  }
  else {
    perror(texture1);
  }
  
  /* テクスチャ画像はバイト単位に詰め込まれている */
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  
  /* テクスチャの割り当て */
  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 glPixelStorei(GLenum pname, GLint param)
glPixelStorei() は,コンピュータ側のメモリと OpenGL 側のメモリとの間でデータをやり取りする際に,画像(この場合はテクスチャ)がメモリにどのように格納されているかを OpenGL に伝えるために用います.引数 pname に GL_UNPACK_ALIGNMENT を指定した場合,param にはメモリを参照するときの「アドレス境界」を指定します.param には 1, 2, 4, 8 のいずれかが指定できます.画素の色が各色 1 バイトで表されているとき,1 画素が RGBA で表現されていれば,メモリ上の 4 バイトごとに 1 画素が配置されていることになります(4 バイト境界=ワード境界).このときは param に 4 が指定できます.一方,1 画素が RGB で表現されていれば,メモリ上の 3 バイトごとに 1 画素が配置されていることになります."3" というアドレス境界は許されませんから(param に 3 は指定できない),この場合は param に 1 を指定する必要があります.一般に,この数が多いほど効率的なメモリアクセスが可能になります.pname に指定できるその他の値については,マニュアル等を参照してください.なお,同じことをする関数に glPixelStoref(GLenum pname, GLfloat param) があります(param のデータ型が違うだけ).
void glTexParameteri(GLenum target, GLenum pname, GLint param)
glTexParameteri() はテクスチャマッピングを行う際の種々のパラメータの設定を行います.2次元のテクスチャを扱うときは,引数 target に GL_TEXTURE_2D を指定します.pname に GL_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次元画像をテクスチャに割り当てます.引数 target は GL_TEXTURE_2D でないといけません.level には MIPMAP を行う場合のテクスチャの解像度レベルを指定します.MIPMAP を行わない場合は 0 にしておいてください.internalFormat は,テクスチャの OpenGL 内部でどのように保持するかを指定します.GL_RGB は RGB 形式で保持します.このほかに GL_RGBA, GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_INTENSITY など,全部で 38 種類のものが指定できます.width と height は,それぞれテクスチャの幅と高さです.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 は *pixels が GLubyte(unsigned char と等価)であることを示します.他に GL_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, GL_BITMAP が指定できます.pixels にはテクスチャの画像を格納したメモリ(配列)のポインタを指定します.

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

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

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

テクスチャ空間上の画像

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

ポリゴンの頂点にテクスチャ座標を割り当てるには,glVertex3d() などで頂点の位置を指定する前に,その頂点のテクスチャ座標を glTexCoord2d() を使って指定します.またテクスチャを使う前に glEnable(GL_TEXTURE_2D) を呼び出して,テクスチャマッピングを有効にしておきます.プログラムに下の太字の部分を追加してください.

・・・
 
/*
** シーンの描画
*/
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枚の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 が使いたければ,テクスチャを割り当てている部分を下のように変更してみてください.マッピングされたテクスチャのギザギザが,多少滑らかになります.

・・・
 
  /* テクスチャ画像はバイト単位に詰め込まれている */
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  
  /* テクスチャの割り当て */
  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() が自動的に調節してくれます.

コメント(5) [コメントを投稿する]
加藤美樹 2013年02月20日 11:15

10bit表示(10bit対応ビデオカード、10bit対応LCDディスプレイ)用に、10bit(カラー30bit)のRawデータは、テクスチャマッピングできるものなのでしょうか?<br><br> 10bitRawデータはRGBが各16bit(2BYTE)で格納されるので、 <br><br> /* テクスチャ画像は2バイト単位に詰め込まれている */<br> glPixelStorei(GL_UNPACK_ALIGNMENT, 2);<br> <br> /* テクスチャの割り当て */<br> gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, TEXWIDTH, TEXHEIGHT,<br> GL_RGB, GL_UNSIGNED_SHORT, texture);<br>※GL_UNSIGNED_BYTE→GL_UNSIGNED_SHORTに変更。<br><br>とサンプルコードを変更して、Windowを作成するとき、下記のコードを追加し、10bit表示用の描画ウィンドウを作成しました。<br> glutInitDisplayString("red=10 green=10 blue=10 alpha=2");<br><br> 以下の変更後、10bitのRawデータを表示させましたが、8bit表示と表示が変わりません。<br><br> 10bit Rawデータの2Dテクスチャマッピングを行う設定手順があるなら、ご教授いただければ幸いです。<br><br> 以上よろしくお願いいたします。

とこ 2013年02月25日 23:18

加藤さま,コメントありがとうございます.<br> ソースを確認していませんが,gluBuild2DMipmaps() の第2引数の internalFormat に RGB32F や RGB16F を指定してみてはどうでしょうか.gluBuild2DMipmaps() が対応していない可能性はありますが…<br> もしうまく行かなければ,試しに gluBuild2DMipmaps() を使わずに glTexImage2D() でお試しいただけますでしょうか.よろしくお願いいたします.

加藤美樹 2013年02月27日 09:15

gluBuild2DMipmaps()ではどうやらうまくいかないようです。もし、同様の検討をされている方でうまくいった方がいらしたら、教えていただければ幸いです。<br>今、glTexImage2D()を使用しようと使い方を勉強しつつ、コード作成を行なっています。

EKISUKE 2014年02月23日 23:33

MipMap作るのにglGenerateMipmapが必要だと聞いたことがあるのですが、どうでしょうか?<br>関係なかったらすみません。

とこ 2014年02月26日 14:15

はい、今の OpenGL では、glGenerateMipmap() 使います。gluBuild2DMipmaps() や GL_GENERATE_MIPMAP のことは忘れてしまっていいと思います。GLU は gluLookAt() も gluPerspective() も忘れるべきなんでしょうね。。


編集 «第1回 画像の読み込み 最新 第3回 テクスチャの合成»