床井研究室

下地の色

前回のプログラムでは、ポリゴンにテクスチャを貼る場合にも glMaterialfv() を使って材質を設定していました。では、この材質によって決まるポリゴンの色は、そのポリゴンにテクスチャを貼ったときに、どのように扱われるのでしょうか。今回はポリゴンの色とテクスチャの色との合成について説明します。

アルファ値

これまで画像データの形式は、光の三原色(赤、緑、青)の3つの成分、すなわち RGB 形式で表していました。しかし、OpenGL が内部で色について処理する時は、RGBA という4つの成分を取り扱っています。この4つ目の成分 “A” は「アルファ値」と呼ばれ、一般に「不透明度」として扱われます。アルファ値は画像の合成の際に利用されます。

では、今まで使ってきたデータをもとに、RGBA 形式の画像を作成してみましょう。実は Photoshop Elements では、RGBA 形式の画像データのアルファ値(アルファチャンネル)を扱う(編集する)ことができません (GIMPPictBear は可能)。しかし、アルファ値を持った画像データを作ることは可能です。

まず、元の画像を開きます。そして、その画像の必要な部分だけを選択した状態にします。下図ではタイヤの周囲とホイールの穴の部分以外を選択しています。

必要な部分を選択

つぎに、この選択範囲を保存します。

画像の必要な領域を選択

すると選択範囲に付ける名前を聞かれますから、適当に決めてください。

画像の必要な領域を選択

あとはこの第1回でやったように、この画像を RAW 画像として保存します。

RGBA 画像ファイルの読み込み

次に、画像の読み込みプログラムを、RGBA 画像を読み込むように変更します。1 画素あたり 3 要素だったものが、4 要素になるだけです。この場合は、画素のデータはワード境界に配置されます。したがって、glPixelStorei( GL_UNPACK_ALIGNMENT, 4) を実行して、そのことを OpenGL 側に知らせることにより、テクスチャの画像データのテクスチャメモリへの読み込みが最適化されます。

/*
** 初期化
*/
static void init()
{
  /* テクスチャ画像はワード単位に詰め込まれている */
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

/* テクスチャの読み込みに使う配列 */
  GLubyte texture[TEXHEIGHT][TEXWIDTH][4];

  /* テクスチャ画像の読み込み */
  FILE* fp = fopen(texture_file, "rb");
  if (fp != NULL) {
    fread(texture, sizeof texture, 1, fp);
    fclose(fp);
  }
  else {
    perror(texture_file);
  }

  ...
}

そして、テクスチャを割り当てる際に、RGBA 画像として割り当てるようにします。

  /* テクスチャの割り当て */
  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);
  ...

ここまでできたら一度プログラムをコンパイルして実行し、今までと同じようにポリゴンにテクスチャが貼られることを確認してください。画像が RGBA 形式になっていなかったり、読み込みプログラムやテクスチャの割当の部分が画像の形式と一致していなかったりすると、つぶれたテクスチャが貼られてしまいます。

void glPixelStorei(GLenum pname, GLint param)
glPixelStorei() は、コンピュータ側のメモリと OpenGL 側のメモリとの間でデータをやり取りする際に、画像(この場合はテクスチャ)がメモリにどのように格納されているかを OpenGL に伝えるために用います。引数 pnameGL_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 のデータ型が違うだけ)。

ポリゴンの陰影

雛形のプログラムでポリゴンをまわすと、テクスチャを貼っても、テクスチャを貼らないのと同じように、ポリゴンに陰影が付いていることがわかります。テクスチャを貼ったポリゴンの陰影付けは、まずテクスチャを貼り付けていないポリゴンに対して陰影付けを行い、その結果をテクスチャの色で「変調 (modulate)」することによって実現しています。 したがって、もとのポリゴンの材質を変更すれば、それはテクスチャを貼ったポリゴンにも反映されます。ためしに、もとのポリゴンの拡散反射係数を指定している変数 color に、赤色を設定してみてください。

/*
** シーンの描画
*/
static void scene()
{
  static const GLfloat color[] = { 1.0f, 0.0f, 0.0f, 1.0f };  /* 材質 (色) */
  
  /* 材質の設定 */
  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);

  /* テクスチャマッピング開始 */
  glEnable(GL_TEXTURE_2D);
  
  ...

  /* テクスチャマッピング終了 */
  glDisable(GL_TEXTURE_2D);
}

すると、こういう具合に下地の色、すなわちポリゴンの色がテクスチャに反映されます。

下地の色のテクスチャの色による変調

テクスチャ環境

下地の色とテクスチャの色の組み合わせ方は、glTexEnvi() という関数で指定します。初期状態では、glTexEnvi()GL_TEXTURE_ENV_MODEGL_MODULATE が指定されている状態になっています。そこで初期化の関数 init() 内で、glTexEnvi() を使って GL_TEXTURE_ENV_MODEGL_REPLACE を指定してみてください。すると今度はテクスチャの色が下地の色を完全に置き換えてしまいます。プログラムに太字の部分を追加してください。 なお、雛形のプログラムでは、下地の色はポリゴンの材質で決定されるものしかありませんが、複数のテクスチャを重ね合わせて使うマルチテクスチャでは、このテクスチャ環境が大きな意味を持ちます。

/*
** 初期化
*/
static void init()
{
  /* テクスチャ画像はバイト単位に詰め込まれている */
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

  ...

  /* テクスチャの割り当て */
  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);
  
  /* テクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  
  ...
} 
void glTexEnvi(GLenum target, Genum pname, GLint param)
テクスチャの合成環境を設定します。引数 targetGL_TEXTURE_ENV である必要があります。引数 pnameGL_TEXTURE_ENV_MODE である必要があります。引数 は GL_TEXTURE_ENV_MODE である必要があります。引数 param には GL_MODULATE, GL_DECAL, GL_BLEND, および GL_REPLACE が指定できます。初期値は GL_MODULATE です。

陰影は下地の色についていますから、この場合は陰影付けも反映されなくなります。ポリゴンを回して、色を確認してみてください。なお、プログラムの実行中にテクスチャ環境を変更しないなら、glTexEnvi() はテクスチャを割り当てた直後に実行すれば十分です。

`GL_REPLACE`

それでは、次に GL_REPLACEGL_DECAL に書き換えてみてください。

  /* テクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

GL_DECAL では下地の色をすべてテクスチャで置き換えるのではなく、テクスチャ画像の選択範囲だけを置き換えます。したがって、選択範囲外には下地の色(ポリゴンの色)が現れます。

`GL_DECAL`

GL_BLEND を指定したときは画像のアルファ値は用いられず、かわりに画像の RGB 値が別に設定した色と下地の色との混合比として用いられます。この色は glTexEnvf() の pname に GL_TEXTURE_ENV_COLOR を指定して定義します。

  /* テクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
  
  /* 混合する色の設定 */
  static const GLfloat blend[] = { 0.0, 1.0, 0.0, 1.0 };
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend);
void glTexEnvfv(GLenum target, Genum pname, GLint param)
テクスチャの合成環境を設定します。引数 targetGL_TEXTURE_ENV である必要があります。引数 pnameGL_TEXTURE_ENV_MODE あるいは GL_TEXTURE_ENV_COLOR が指定できます。pnameGL_TEXTURE_ENV_MODE を指定したときは、引数 para には GL_MODULATE, GL_DECAL, GL_BLEND, あるいは GL_REPLACE を格納した単一の実数値 (GLfloat 型) へのポインタを指定します。pnameGL_TEXTURE_ENV_COLOR を指定したときは、引数 param には RGBA 値を格納した4要素の GLfloat 型の配列が指定できます。

この出力画像では、テクスチャの暗い部分に下地の色(color: 赤)が現れ、明るい部分に別に定義した色 (blend: 緑) が現れています。

これは 2004 年の記事です。OpenGL (3.1以降 Core Profile) および OpenGL ES 2.0/3.0 以降では、glTexEnv*() 関数は廃止されています。

`GL_BLEND`