■ 2005年06月16日 [OpenGL][テクスチャ] 第20回 グロスマッピング
非金属の物体への映り込み
コンピュータグラフィックスでは,照明された物体表面上での反射光を,拡散反射光と鏡面反射光の二つに分けて考えます(二色性反射モデル).拡散反射光は入射光のうち屈折して物体内に進入し,散乱・吸収を経て再び外部に放出された成分であり,物体の色の影響を受けます.これに対して鏡面反射光は入射光の物体表面における正反射光であり,物体内部に侵入しないために,物体が非金属の場合は物体の色の影響を受けません.したがって,非金属の物体の表面に環境が映り込んでいる場合,物体表面の色は物体の本来の色に映り込みの色が加算されたものになります.
加算してみる
前回の最後の結果は,テクスチャの合成に GL_MODULATE,すなわち「積」を用いていました.これは金属製の物体表面に着色された半透明の膜が貼り付けられていて,鏡面反射光自体に色が付いているような状態です.そこで,前回作ったプログラムでテクスチャユニット1のテクスチャ環境として使っている GL_MODULATE を,試しに GL_ADD に変えてみましょう.
・・・ /* ** 初期化 */ static void init(void) { ・・・ /* テクスチャユニット1に切り替える */ glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_CUBE_MAP, texname[0]); ・・・ /* テクスチャユニット1のテクスチャ環境 */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); /* キューブマッピング用のテクスチャ座標を生成する */ 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); /* テクスチャユニット0に戻す */ glActiveTexture(GL_TEXTURE0); ・・・
プログラムをコンパイルして実行すると,こういう結果が得られます.
なんだか明るくなりすぎてしまいました.二色性反射モデルでは入射光が拡散反射光と鏡面反射光に分かれるので,拡散反射光強度と鏡面反射光強度の和が入射光強度を超えることはありません.しかし GL_ADD を使うと,拡散反射光に対して環境のテクスチャがそのまま加算されてしまいますから,物体表面の色が入射光強度より大きくなってしまう場合があります.
これを防ぐには,変数 color で指定してる下地のポリゴンの拡散反射係数を下げたり,物体表面のテクスチャや環境のテクスチャの明度を下げたりする必要があります.これらの調整は,入射光に対して鏡面反射光と物体内に進入する光の割合が a 対 1-a (0≦a≦1) となることを考慮して決めなければなりません.
GL_COMBINE
OpenGL 1.3 ではテクスチャ環境が拡張され,線形補間によるテクスチャの合成機能などが追加されました.この線形補間によるテクスチャの合成機能を使うと,上記のような明度の比例配分をレンダリング時に行うことが可能になります.すなわち,(現在のテクスチャユニットで処理している)映り込みの色の a 倍し,下地の色の 1-a 倍した後に,それらを加算した結果を得ることができます.
この機能を使用するには,まず glTexEnvi() 関数で GL_TEXTURE_ENV_MODE に GL_COMBINE を指定します.次に glTexEnvi() で GL_COMBINE_RGB に実行するテクスチャの演算を指定します.線形補間を行うには,ここに GL_INTERPOLATE を指定します.GL_INTERPOLATE では補間のパラメータ a に何を使うか指定する必要がありますが,デフォルトではこれに glTexEnvfv() を使って GL_TEXTURE_ENV_COLOR に指定した変数に格納された色が用いられます.ただし,ここで使用されるのは,色の4つの成分 RGBA のうちの A,すなわちアルファ値のみです.
・・・ /* ** 初期化 */ static void init(void) { ・・・ /* テクスチャユニット1に切り替える */ glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_CUBE_MAP, texname[0]); ・・・ /* テクスチャユニット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.0, 1.0, 1.0, 0.5 }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blend); /* キューブマッピング用のテクスチャ座標を生成する */ 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); /* テクスチャユニット0に戻す */ glActiveTexture(GL_TEXTURE0); ・・・
この配列変数 blend の4つ目の要素 blend[3] の値を 0.0, 0.33, 0.67, および 1.0 に設定し,プログラムをコンパイルして実行したら,それぞれ以下のような結果が得られました.
グロスマッピング
GL_COMBINE では,補間のパラメータ a に定数ではなくテクスチャの値を用いることもできます.つまり,鏡面反射光強度をテクスチャで制御することが可能になります.これにより物体表面上の場所によって輝き方(グロス)を変更することができます.このような手法をグロスマッピングといいます.ここでは物体に貼り付けているテクスチャに合わせて,次のようなテクスチャをグロスマッピングに用います.
このテクスチャは,物体に貼り付けるテクスチャのアルファチャンネルに格納することにします.このような画像ファイルは,Photoshop (Elements を含む) において元の画像の白い部分を選択した後,選択範囲を保存して作成することができます.詳しくは第3回を参照してください.
このテクスチャのアルファチャネルを補間のパラメータ a に使用するために,glTexEnvi() を使って GL_SOURCE2_RGB に GL_PREVIOUS を指定します.GL_SOURCE2_RGB はこの演算の a に用いるデータを表し,GL_PREVIOUS は現在のテクスチャユニットの前のテクスチャユニットの出力を示します.
現在のテクスチャユニット(テクスチャユニット1)はキューブマッピングの処理を行っており,前のテクスチャユニットは下地のテクスチャの処理を行っています.したがって,GL_SOURCE2_RGB に GL_PREVIOUS を指定して前のテクスチャユニットの出力を参照すれば,a に物体に張り付けた方のテクスチャを利用できます.
そして glTexEnvi() を使って GL_OPERAND2_RGB に参照した値の取り扱い方を設定します.これに GL_SRC_ALPHA を指定することで,a に下地のアルファチャンネルを割り当てることができます.
・・・ /* ** 初期化 */ static void init(void) { ・・・ /* テクスチャユニット1に切り替える */ glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_CUBE_MAP, texname[0]); ・・・ /* テクスチャユニット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.0, 1.0, 1.0, 0.5 }; 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 /* キューブマッピング用のテクスチャ座標を生成する */ 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); /* テクスチャユニット0に戻す */ glActiveTexture(GL_TEXTURE0); ・・・
a にテクスチャを使っているので,配列変数 blend や,blend を指定している glTexEnvfv() は不要になります.これをコンパイルして実行すると,こういう結果が得られます.マウスで物体をぐるぐる回してみてください.右側は関数 box() を glutSolidTeapot() に変えた場合です(なぜかこれは手元の Windows マシンでは表示されなかった…).
ちなみに,GL_SRC_ALPHA を GL_ONE_MINUS_SRC_ALPHA に変えると,次のような結果が得られます.
・・・ /* テクスチャユニット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.0, 1.0, 1.0, 0.5 }; 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 ・・・
glTexEnvi() の引数
テクスチャの合成方法と glTexEnvi() の引数の関係は,次のようになります(抜粋).