床井研究室

非金属の物体への映り込み

コンピュータグラフィックスでは、照明された物体表面上での反射光を、拡散反射光と鏡面反射光の二つに分けて考えます(二色性反射モデル)。拡散反射光は入射光のうち屈折して物体内に進入し、散乱・吸収を経て再び外部に放出された成分であり、物体の色の影響を受けます。これに対して鏡面反射光は入射光の物体表面における正反射光であり、物体内部に侵入しないために、物体が非金属の場合は物体の色の影響を受けません。したがって、非金属の物体の表面に環境が映り込んでいる場合、物体表面の色は物体の本来の色に映り込みの色が加算されたものになります。

二色性反射

加算してみる

前回の最後の結果は、テクスチャの合成に GL_MODULATE、すなわち「積」を用いていました。これは金属製の物体表面に着色された半透明のフィルムが貼り付けられていて、鏡面反射光自体に色が付いているような状態です。そこで、前回作ったプログラムでテクスチャユニット1のテクスチャ環境として使っている GL_MODULATE を、試しに GL_ADD に変えてみましょう。

/*
** 初期化
*/
static void init()
{
  ...

  /* テクスチャユニット1のテクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);

プログラムをコンパイルして実行すると、こういう結果が得られます。

`GL_ADD` を使ってテクスチャを合成

なんだか明るくなりすぎてしまいました。二色性反射モデルでは入射光が拡散反射光と鏡面反射光に分かれるので、拡散反射光強度と鏡面反射光強度の和が入射光強度を超えることはありません。しかし GL_ADD を使うと、拡散反射光に対して環境のテクスチャがそのまま加算されてしまいますから、物体表面の色が入射光強度より大きくなってしまう場合があります。環境マッピングの環境は光源そのもの

これを防ぐには、光源の明るさ (lightcol, lightamb) を下げたり、下地のポリゴンの拡散反射係数 (color) を下げたり、物体表面のテクスチャや環境のテクスチャの明度を下げたりする必要があります。これらの調整は、鏡面反射光の強さと拡散反射光の強さの和が入射光の強さを超えないこと(エネルギー保存の法則)を考慮して決めなければなりません。

GL_COMBINE

OpenGL 1.3 ではテクスチャ環境が拡張され、線形補間によるテクスチャの合成機能などが追加されました。この機能はテクスチャコンバイナと言います。この線形補間によるテクスチャの合成機能を使うと、上記のような明度の比例配分をレンダリング時に行うことが可能になります。すなわち、(現在のテクスチャユニットで処理している)映り込みの色の a 倍し、下地の色の 1 - a 倍 (0 ≦ a ≦ 1) した後に、それらを加算した結果を得ることができます。

この機能を使用するには、まず glTexEnvi() 関数で GL_TEXTURE_ENV_MODEGL_COMBINE を指定します。そして glTexEnvi()GL_COMBINE_RGB に実行するテクスチャの演算を指定します。線形補間を行うには、ここに GL_INTERPOLATE を指定します。GL_INTERPOLATE では補間のパラメータ a に何を使うか指定する必要がありますが、デフォルトではこれに glTexEnvfv() を使って GL_TEXTURE_ENV_COLOR に指定した変数に格納された色が用いられます。ただし、ここで使用されるのは、色の4つの成分 RGBA のうちの A、すなわちアルファ値のみです。

/*
** 初期化
*/
static void init()
{
  ...

  /* テクスチャユニット1のテクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
  static const GLfloat blend[] = { 1.0f, 1.0f, 1.0f, 0.5f };
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend);

この配列変数 blend の4つ目の要素 blend[3] の値を 0.0, 0.33, 0.67, および 1.0 に設定し、プログラムをコンパイルして実行したら、それぞれ以下のような結果が得られました。

GL_INTERPOLATE で a=0.0 の時 GL_INTERPOLATE で a=0.33 の時
`GL_INTERPOLATE` で a=0.0 の時 `GL_INTERPOLATE` で a=0.33 の時
GL_INTERPOLATE で a=0.67 の時 GL_INTERPOLATE で a=1.0 の時
`GL_INTERPOLATE` で a=0.67 の時 `GL_INTERPOLATE` で a=1.0 の時

グロスマッピング

GL_COMBINE では、補間のパラメータ a に定数ではなくテクスチャの値を用いることもできます。つまり、鏡面反射光強度をテクスチャで制御することが可能になります。これにより物体表面上の場所によって輝き方(グロス)を変更することができます。このような手法をグロスマッピングといいます。ここでは物体に貼り付けているテクスチャに合わせて、次のようなテクスチャをグロスマッピングに用います。

グロスマッピング用のテクスチャ

このテクスチャは、物体に貼り付けるテクスチャのアルファチャンネルに格納することにします。このような画像ファイルは、Photoshop (Elements を含む) において元の画像の白い部分を選択した後、選択範囲を保存して作成することができます。詳しくは第3回を参照してください。

このテクスチャのアルファチャネルを補間のパラメータ a に使用するために、glTexEnvi() を使って GL_SOURCE2_RGBGL_PREVIOUS を指定します。GL_SOURCE2_RGB はこの演算の a に用いるデータを表し、GL_PREVIOUS は現在のテクスチャユニットの前のテクスチャユニットの出力を示します。

現在のテクスチャユニット(テクスチャユニット1)はキューブマッピングの処理を行っており、前のテクスチャユニットは下地のテクスチャの処理を行っています。したがって、GL_SOURCE2_RGBGL_PREVIOUS を指定して前のテクスチャユニットの出力を参照すれば、a に物体に張り付けた方のテクスチャを利用できます。

そして glTexEnvi() を使って GL_OPERAND2_RGB に参照した値の取り扱い方を設定します。これに GL_SRC_ALPHA を指定することで、a に下地のアルファチャンネルを割り当てることができます。

/*
** 初期化
*/
static void init()
{
  ...

  /* テクスチャユニット1のテクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
#if 0
  static const GLfloat blend[] = { 1.0f, 1.0f, 1.0f, 0.5f };
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend);
#else
  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
#endif

a にテクスチャを使っているので、配列変数 blend や、blend を指定している glTexEnvfv() は不要になります。これをコンパイルして実行すると、こういう結果が得られます。マウスで物体をぐるぐる回してみてください。右側は関数 box()glutSolidTeapot() に変えた場合です(なぜかこれは手元の Windows マシンでは表示されなかった…)。

グロスマッピング ティポットにグロスマッピング

ちなみに、GL_SRC_ALPHAGL_ONE_MINUS_SRC_ALPHA に変えると、次のような結果が得られます。

/*
** 初期化
*/
static void init()
{
  ...

  /* テクスチャユニット1のテクスチャ環境 */
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
#if 0
  static const GLfloat blend[] = { 1.0f, 1.0f, 1.0f, 0.5f };
  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend);
#else
  glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS);
  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_ALPHA);
#endif

`GL_ONE_MINUS_SRC_ALPHA` に変更した場合

glTexEnvi() の引数

テクスチャの合成方法と glTexEnvi() の引数の関係は、次1のようになります(抜粋)。

テクスチャの結合方法

マルチテクスチャのレイヤーの配分比を変化させながらアニメーション

  1. この機能、この頃に自分で使ってて気が狂いそうになったから、これがこの後にプログラマブルシェーダが導入されたきっかけになったんじゃないかって密かに思ってる。(2024 年 5 月 9 日追記)