光源と物体の照光処理
Last update: <2004/03/13 17:30:45 +0900>
物体を立体的に表現し,より本物らしく見せる技法として,物体に陰影をつける方法があります.物体に陰影をつけることを照光(lighting)と言います.
現実世界で人間が物体を見る場合には,何らかの光線が物体に反射,拡散し,その光が網膜を刺激することによって物体を認識します.物体によっては自ら光を発する(放射)ものもありますが,基本的には物体が反射する光線を見ていて,物体そのものを見ているわけではありません.
物体の陰影とは,物体が反射拡散する光線の量に依存しています.図は光源の光が物体にあたり,視点に入るまでを表したものです.この場合,視点物体が発する光線の量は,視点と物体と光源の位置関係や,物体面の法線方向に依存することがわかります.
当然のことながら,視点や光源が物体から離れたり,光源の光の量が少ないと全体が暗くなったりします.また光がスポットライトのような指向性をもつものであれば,光の向きが重要になってきたりします.光に色がついている場合は,物体の色と光の色が混ざることもあります.
コンピュータでこれらの照光処理を実現するためには,光の属性や物体の属性をモデル化し,シミュレーションすることになります.ここでは照光処理に必要不可欠な光の属性,物体の属性について説明します.
光源には,光の色,位置,方向など属性があります.OpenGLでもたくさんの特性を指定することができますが,代表的なものを列挙します.
- 光源の色
光にはさまざまな波長の成分が含まれています.光の色は,主に三原色であるRGBで指定します.光は物体に反射して人間の目に入射しますが,物体から反射してくる光を分類すると,3つに分けることができます.
- 環境光(ambient,アンビエント)
光は光源から発します.しかし,日常生活では光源の位置を意識しないこともあります.これは光源から発した光が反射拡散を繰り返し,光源の位置や方向を特定できないぐらい光線が入り乱れているからです.このように周囲から均等にふりそそぐ光のことを環境光といいます.
環境光では,一つの物体表面上に均一な光を当てるので,物体の明暗や影はできません.曇りの日の光だと考えて下さい.
- 拡散光(diffuse,ディフューズ)
光が光沢を持たない物体とぶつかるとき,光は面に対して全方向に等しく拡散します.
光源から物体に一回ぶつかり,拡散をしたときの光を拡散光といいます.
拡散光は特定の位置や方向から来る光が,物体に1回だけ拡散する光です.光は物体表面で全方向に等しく拡散されますので,視点の位置は無関係です.ただし物体面が光線に対して薄くあたると光量が減りますので暗く見えます.
例えば光源の下にボールがあったとしましょう.ボールの光源側では光線と垂直にあたるため,受ける光量はもっとも多くなります.ボール面が光線と薄くあたるほど,光量が減っていきます.この時,視点の位置に関わらず,光源の位置と法線ベクトルだけで明暗は決定されます.
- 鏡面光(specular,スペキュラー)
光が光沢を持つ物体とぶつかるとき,光はある特定方向に乱反射します.鏡のような完全反射の場合には乱反射せず,スネルの法則にしたがって反射しますが,普通のつや有り物体の場合には,完全反射に準じた方向に乱反射します.このような光沢をもつ物体の反射光を鏡面光と呼びます.ハイライトともいいます.
鏡面光は特定方向にのみに乱反射するため,光源の位置,法線ベクトル,そして視点の位置に依存して計算されます.
環境光,拡散光,鏡面光のそれぞれの例
光源ではそれぞれに色を指定することができます.環境光になりやすい色,拡散されやすい色,反射されやすい色と考えるとよいでしょう.照光処理はこれらの光の組み合わせによっておこなわれます.上の図は環境光,拡散光,鏡面光の表示結果で,下の図がそれぞれを組み合わせたときの結果です.
環境光,拡散光,鏡面光の重ねあわせ
これらの光源の色設定は,物体の質感設定がなければ意味がありません.例えば赤い色の光源を持っていても,物体が赤い色を吸収されるのであれば,見えないことを意味します.
- 光源の位置,方向
光源の位置は3次元空間で一意に指定することができます.物体より近いところにある光源は,位置が物体の照光に及ぼす影響が大きくなります.また,太陽のように極めて遠い光源の場合は光線が平行になるため,光線の方向が重要になってきます.位置が重要な意味を持つ光源をpositionalな光源,方向が重要な光源をdirectionalな光源といいます.
- 光源の減衰
現実の光は光源から遠ざかると光量が減少します.減衰係数によって減少量を指定します.ただし,directionalな光源は無限遠に存在するので,減衰係数の指定は意味をなしません.
- スポットライト
スポットライトとは,光線を放射する範囲が円錐のような形状になるものです.スポットの大きさを角度で指定します.なおスポットライトを使用する場合は,円錐の軸方向を指定して,光線の向きを指定する必要があります.
物体にはさまざまな属性がありますが,特に照光や見えに関係する代表的なものを列挙します.
- 物体の色
物体が色を持っているとは,物体が反射拡散しやすい波長の光を見ていることを意味します.したがって,物体の色の属性には光の属性が大きく関わってきます.白い光源の下で赤く見えるボールとは,赤い光だけを反射し緑と青の光を吸収していることになります.
OpenGLでは環境光や拡散光,鏡面光などが物体に反射拡散したときの色,物体自身が輝く色などを属性として持たせることができます.
- 環境光の拡散
環境光に対してその物体がどんな色成分を拡散するかを表します.例えば赤色を指定すると,たとえ白色光源でも物体は全体的に赤色を帯びることになります.
- 拡散光の拡散色
拡散光に対してその物体がどんな色成分を拡散するかを表します.例えば緑を指定すると,たとえ白色光源でも物体の明暗は緑のグラデーションで表現されます.
白色光源での物体色設定例:
左が環境光に対して赤色を指定したもの.
中央が拡散光に対して緑色を設定したもの.
右が環境光に赤,拡散光に緑を設定したもの.
- 鏡面光の反射色
鏡面光に対してその物体がどんな色成分を拡散するかを表します.例えば青色を指定すると,たとえ白色光源でも物体は青色のハイライトを出すことになります.
白色光源での物体色設定例:
左が鏡面光に対して青色を指定したもの.
右が環境光に赤,拡散光に緑,鏡面光に青を設定したもの
鏡面光では物体にハイライトが出ますが,物体の質感によってはハイライトが小さくなったり大きくなったりします.光沢が高いほど,ハイライトが小さく明るい状態(焦点が合っている)になります.この光沢度も物体側特有のパラメータです.
ハイライトが大きい(光沢が低い)もの.
ハイライトが小さい(光沢が高い)もの.
- 放射色
物体が自ら発している色です.この色成分は光源には無関係になるため,光源のある状況下で光源の影響を受けない物体(シーン内のランプ等)に使われたりします.
- 物体の位置
光源が指向性や減衰する性質を持つ場合,物体面と光源の位置関係によって受ける光の量が異なります.こうした照光時には物体位置も参照されます.
- 物体面の法線(Normal)
物体と光線のあたり具合をシミュレートするためには,面がどの方向を向いているかを定義しておかなければなりません.物体面が向いている方向を表すための3次元パラメータとして法線ベクトルが用いられます.
物体は通常,面ポリゴンから構成されていますが,この面ごとに法線を定義させることによって,正しく照光処理をおこなうことができます.
なお,OpenGLでは法線ベクトルの長さを1に正規化しておかないと,パフォーマンスが低下します.
- 照光処理のながれ
照光処理を有効にするためには,
- 物体に法線ベクトルを持たせる.
- 光源を作成,配置する.
- 物体の質感を設定する.
という手順が必要となります.第3回で触れた照光を例に演習をすすめます.
-
第3回の演習問題を参考に,思い出しながら太陽の周りを地球が周回するプログラムを作成して下さい.
- 第3回演習では質感を持たせるために一部,照光処理をやりました.光源の設定は質感の設定は通常,init関数のような初期化関数の中でやるのが流儀です.物体によって質感が異なる場合には,物体を描画する毎に変更します.
照光処理を有効にするためには,次のようなコマンドをinit関数に追加しました.
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
最初の行は光源を有効にする設定,2行目が光源0(光源は8個まで設定可能)を有効にする設定,3行目が物体の質感を有効にする(今はそう考えて下さい)設定です.
これらの設定だけでとりあえず照光処理が実行されます.光源や質感の詳細パラメータは初期値(デフォルト値)が使用されます.また,法線ベクトルの設定はglutSolidSphere()に含まれています.
-
光源の位置を変更してみましょう.デフォルトでの光源位置は(0.0, 0.0, 1.0)になっています.頭上(0.0, 10000.0, 0.0)に変更する場合は,初期化関数の中で
float light0_position[4]={0.0, 10000.0, 0.0, 1.0};
と冒頭で変数を宣言し,
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
を入れます.光源の位置が変更された見えになったでしょうか.
-
glLightfv()関数は位置だけではなく,光源のさまざまな属性を設定できます.一つ目の引数が設定したい光源で,GL_LIGHT0〜GL_LIGHT7まで指定できます.二つ目の引数は属性で,三つ目が指定した属性のパラメータ(ベクトル)です.代表的な属性と第2引数,第3引数の形を列挙します.
属性 | 第2引数 | 第3引数の値 |
環境光 | GL_AMBIENT | float[4]=(R,G,B,A) |
拡散光 | GL_DIFFUSE | float[4]=(R,G,B,A) |
鏡面光 | GL_SPECULAR | float[4]=(R,G,B,A) |
位置 | GL_POSITION | float[4]=(x,y,z,w) |
RGBは3原色,Aはアルファ値をあらわしています.アルファ値は透明度を表すパラメータですが,次回の混合処理までは無視してかまいません.
3次元位置をあらわす(x,y,z,w)ですが,wがゼロの場合には指向性の光源になり,x,y,zが示す位置から原点に向かって光が進むことを表すことになります.
この他にもスポットライトを設定するパラメータや減衰係数などを指定できます.必要に応じて赤本で調べてみて下さい.
-
光源を上に持ってくると,玉の下の部分が少し暗いですね.例えば環境光を強くすると次の画像のようになります.
パラメータを変更して,見え方を変えてみて下さい.
- 光源を動かしてみましょう.光源の位置変更は先ほど説明した通りです.描画ループ(display関数)で第3パラメータを適宜変更して,glLightfv()を再宣言すれば光源の位置が更新されます.
光源の位置は,数値による指定だけでなく,座標変換して移動することも可能です.例えば太陽を光源としたい場合には,display関数の冒頭で,
float light0_position[3]={0.0, 0.0, 0.0, 1.0};
と変数を宣言します.指向性を示すパラメータが1である(指向性なし)ことに注意して下さい.で,太陽の描画(glutSolidSphere())の位置に
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
を入れます.
光源が太陽の中心にきてしまうので,太陽はのっぺりしてしまうことがわかります.これができたら,光源の位置を地球や月に変更してみて下さい.
- スポットライトについて
- c:\glut-3.7\progs\examples\spots.exeを実行してみて,3色のスポットライトを見てみましょう.
- c:\glut-3.7\progs\examples\spots.cを見て,スポットライトの設定箇所を探してみましょう.サンプルプログラムを読んで,自分の糧にすることは重要です.
- 可能であれば,自分でコンパイルして動作確認してみましょう.c:\glut-3.7\progs\examples\spots.cを自分のプロジェクトにコピーし,プロジェクトに追加して,動作確認します.うまくコンパイルできたら,スポットライトのパラメータを変更してみましょう.
- 質感の設定:ティーポットに質感をあたえてみましょう.
- 第4回のおまけプログラムを使用して,ティーポットの質感を設定します.第4回のプログラムを実行可能にして下さい.
- 光源の属性を準備しておきます.たとえば位置は(0.0, 100.0, 0.0)で位置的光源,環境光は(0.2,0.2,0.2),拡散光,鏡面光はいずれも(1.0, 1.0, 1.0)にしてください.他の色でもかまいません.
- 質感の設定をおこないます.物体が複数あり,物体ごとに質感を設定する場合は,物体の描画の直前で設定しますが,ここではinitの中でも構わないでしょう.
質感の設定は光源と同様に,配列にパラメータを入れておいて,関数で指定します.
試しに金の材質を持つようなティーポットに変更してみましょう.init関数の中に
float material_ambient[4]={0.24725, 0.1995, 0.0745, 1.0};
float material_diffuse[4]={0.75164, 0.60648, 0.22648, 1.0};
float material_specular[4]={0.628281, 0.555802, 0.366065, 1.0};
float material_shinness=48;
という配列を宣言し,
glMaterialfv(GL_FRONT, GL_AMBIENT, material_ambient);
glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular);
glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse);
glMaterialf(GL_FRONT, GL_SHININESS, material_shinness);
を挿入して下さい.
また,glEnable(GL_COLOR_MATERIAL);をコメントアウトして下さい.この関数は,glColor3fというカラーの設定で,質感を持たせることのできる便利な関数で,今回の質感設定では使いません.
画面のように金(っぽい)表示になったでしょうか.
-
質感を指定する関数はglMaterialfv()で,第1引数には指定したい材質を適用したい面を指定します.通常は表なので,GL_FRONTとなります.両面に持たせたい場合はGL_FRONT_AND_BACK,裏面だけに持たせたい場合はGL_BACKです.第2引数と第3引数は以下の通りです.
属性 | 第2引数 | 第3引数の値 |
環境光 | GL_AMBIENT | float[4]=(R,G,B,A) |
拡散光 | GL_DIFFUSE | float[4]=(R,G,B,A) |
鏡面光 | GL_SPECULAR | float[4]=(R,G,B,A) |
放射光 | GL_EMISSION | float[4]=(R,G,B,A) |
また,鏡面反射のハイライトの大きさを指定するには,類似の関数で
glMaterialf()があります.第1引数は上に同じ,第2引数がGL_SHINNESS,第3引数が0から128までの数字です.大きいほど表面の光沢が強く,ハイライトが小さくなります.
-
真珠のような質感を出してみましょう.
参考まで,環境光={0.25, 0.20725, 0.20725, 1.0},拡散光={1, 0.829, 0.829, 1.0},鏡面光={0.296648, 0.296648, 0.296648, 1.0},SHINNESS=5という数字を挙げておきます.各自で微調整してみて下さい.
- ここで勉強したOpenGLの関数は,
glLightv()
glMaterialf()
glMaterialfv()
です.赤本やGLUTのマニュアルを利用して,復習しておきましょう.
戻る