[OpenGL] COLOR MATERIALの利点

OpenGLのcolor materialとは、material parameterの一部をcurrent colorに同期させるモードである。例えば、

glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); /* (1) */
glEnable(GL_COLOR_MATERIAL);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
(some drawing)
glDisable(GL_COLOR_MATERIAL);
とすると、
GLfloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
(some drawing)
とするのと同じ効果が得られる。((1)のColorMaterialの設定は、この引数だとOpenGLのデフォルトと同じ設定なので、省略できる。OpenGL ESだと、そもそもこの関数は存在しない=ColorMaterialは常にFRONT_AND_BACK, AMBIENT_AND_DIFFUSEに働く)

これが何の役に立つのだろうか?と、ふと思った時にわからず、調べても見つからなかったので、自分なりにCOLOR_MATERIALのメリットを整理してみた。

1. 軽量("less expensive")である

Webを検索すると、いくつかのページに、color materialは"less expensive"と書かれてある。例えば、Avoiding 16 Common OpenGL Pitfallsに、

OpenGL's color material feature provides a less expensive way to change material parameters.
と書かれている。実は、筆者には"less expensive"の意味がよくわからないが、まさか
GLfloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
より
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
の方がコードが短くて済む、という意味ではないと思う(前者が1行で書けないのはC言語だからである)ので、ハードウェアの処理負荷が少ないという意味だと思いたい。

2. LightingがOFFの場合と同じコードになる

LightingがOFFだとcurrent colorが描画に反映され、ONだとmaterial colorが描画に反映される。Material colorをglMaterialfv()で設定すると、例えば

glBegin(GL_TRIANGLES);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
glVertex3f(0, 1, 0);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green);
glVertex3f(-0.866f, -0.5f, 0);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, blue);
glVertex3f( 0.866f, -0.5f, 0);
glEnd();
このようになるのに対し、color materialを使うと
glBegin(GL_TRIANGLES);
glColor4f(1,0,0,1);
glVertex3f(0, 1, 0);
glColor4f(0,1,0,1);
glVertex3f(-0.866f, -0.5f, 0);
glColor4f(0,0,1,1);
glVertex3f( 0.866f, -0.5f, 0);
glEnd();
のように、lightingがOFFでもそのまま使えるコードにすることができる。

3. Color arrayの色をmaterialに反映できる

glBegin()〜glEnd()とするのでなく、頂点配列を用意してglDrawArrays()やElements()を使って描画する場合、頂点毎にmaterial colorを設定するには、color material+color arrayを用いるしかない。

一応、GLUTのサンプルコードを作ってみた。
color_material_test.c
display()のcase 1〜7(左クリックで出るメニューのTest1〜7)は、それぞれ、以下の内容である。

  1. Material colorをglMaterialfv()で設定する例(glBegin()〜glEnd()使用、case 3まで同様)
  2. Material colorをcolor materialで設定する例
  3. 同じコードがlighting OFFでも使えることを示す例
  4. Material colorをglMaterialfv()で設定する例(glDrawArrays()使用、以下同様)
    全ての頂点が同じ色になる。
  5. Material colorをcolor materialで設定する例(color array不使用)
    case 4と同じ表示になる。
  6. Material colorをcolor materialで設定する例(color array使用)
    Color materialを使用すれば、頂点毎にmaterialを変えられる。
  7. 同じコードがlighting OFFでも使えることを示す例(color array使用)
case 0(初期状態)は、Color materialを使用しないと、current colorもcolor arrayも反映されないことを示す例である。(灰色の四面体が表示される。)

実行例
case 1-3 case 4-5 case 6
左から、case 1-3、case 4-5、case 6の画面


なお、Avoiding 16 Common OpenGL Pitfallsの14.に書かれているが、color materialを有効にすると即座にcurrent colorがmaterialに反映され、その後でcolor materialを無効にしてもmaterialは元に戻らない。従って、glEnable(GL_COLOR_MATERIAL)とglDisable(GL_COLOR_MATERIAL)を現在のmaterialを一時的に退避させる用途では使えない。また、glDisable(GL_COLOR_MATERIAL)した後にmaterialを元に戻したいなら、glEnable(GL_COLOR_MATERIAL)する前に現在のmaterialの値を取り出しておく必要がある。

なお、筆者の環境(Mac OS X 10.7.5)では、上記のサンプルコードで、glEnable(GL_CULL_FACE)しないと、四面体が色々おかしな表示になる。全体的に暗くなり、回転中に、ポリゴン単位でなく頂点単位で色が変わったりする。ポリゴンの表裏が逆なのかと思ったが、CULL_FACEして消える訳じゃないので、表裏は間違っていないと思う。法線の向きもしかりである。なんとなく、ライトが当たっているポリゴンの表面よりも、ライトが当たっていない裏面が優先されて描画されているように思えるのだが、そんなことがあり得るのだろうか。
半日くらい原因を調べたが、さっぱりわからない。
こういう、細かな所でよく躓き、しかも躓くと原因の特定が困難な辺り、OpenGLの道は険しいと思う。

一年振りくらいにOpenGLの勉強を再開しているが、なかなか思うように進まない。概念は理解しているつもりで、大体、大まかにはすぐに動くのだが、できた、と思ったら細かい所が何か変で、原因がわからず、試行錯誤してたまたま回避できる方法を探すことの連続である。OpenGLの内部で何が起こってるのかがわからず、何か思い通りに動かない時に、原因を探る手段が見当たらないのが辛い。どうしたものか。