/**/

チュートリアル 35:
ライティングとフォグ

ライティング(現実の世界での対象物の照明)はとても複雑な課題です。私たちが対象物を見るとき、私たちの目は焦点を合わせ、可視光と呼ばれるエネルギーの範囲の全体に渡って光子を検出します。光子は、太陽、照明、蛍、その他の光源と私たちの目の間で、様々な素材(マテリアル)による反射や屈折をしたり、大気によって散乱したりすることにより、多くの複雑な経路を通ってくる可能性をもっています。私たちのコンピュータでは、まだまだ、すぐにはこのレベルの複雑性を扱うことはできないでしょう。そのため、OpenGLではこれを非常に単純化しています。

OpenGLライティングモデル

OpenGLでのライティングは、現実世界における現象の非常にラフなモデルに基づいています。自然の繊細さに比べればとても大雑把なものですが、現在の技術では、リアリズムへの欲求と複雑さによるコストの間での適当な妥協点と言えるでしょう。

これまでに私たちは、OpenGLでの色がRGB(赤、緑、青)値として表現される方法を見てきました。OpenGLのライティングモデルではこの考え方を拡張し、それぞれが RGB の3種類の値によって表される、独立した様々な「コンポーネント(要素)」によって光を指定できるようになっています。それぞれのコンポーネントは、特定の方法で散乱させられた光がどの程度の色になるかを表しています。現実世界で可能な一連の光の経路は4つの要素に単純化されます。ここでは、それらを、方向性が最大なものから方向性が最小なものまで順に挙げていきます。

鏡面反射光(specular light)というコンポーネントは、特定の方向から来て、表面で主として特定の方向に反射する光です。艶のあるマテリアルは、顕著な鏡面反射要素を持っています。鏡で反射する集中した光線では鏡面反射要素が支配的な状況になります。

拡散反射光(diffuse light)では、光は一方向から来ますが、表面での反射はすべての方向に等しく散乱します。表面が光源の方向に向いている場合には、光源から受ける光の放射は大きなものになり、表面から反射される拡散反射要素は最も明るくなります。表面が別の方向に向いている場合には、光源に対する断面積は小さくなり、拡散反射要素は小さくなります。

環境光(ambient light)には方向性がありません。非常に散乱した光であるため、その光源の方向は定まりません。そのため、すべての方向から等しく光っているように見えます。白い壁の部屋は、私たちの目に届く前に、非常に多くの光子が壁から壁へ反射して散乱するため、高い環境光要素を持つ環境になります。

最後に、放射光(emissive light)があります。これは方向にはまったく影響されません。そのオブジェクトを直接見た場合にだけ目に届く光です。これは、モデリングするオブジェクト自体が光源となる場合に使います。

これらのコンポーネントは、マテリアルや光源を表すために使用されます。鏡面反射光、拡散反射光、環境光、放射光というコンポーネントによって作られるマテリアルはポリゴンに適用され、ポリゴンがどのような色になるかを決定します。マテリアルを適用されたポリゴンは、光源やカメラに対する位置や回転角、光源の鏡面反射や拡散反射のプロパティ、さらに、そのシーンの環境光のコンポーネントに基づいて照らされます。

最初に

・Jitter Tutorial フォルダにある 35jLightingAndFog.pat というチュートリアルパッチを開いて下さい。Start Rendering と表示された toggle(今回は左下にあります)をクリックして下さい。

jit.pwindow オブジェクトの上にある name lt, depthbuffer 1と書かれたメッセージボックスをクリックして下さい。これにより、デプスバッファが生成され、隠面消去が可能になります。

jit.pwindow オブジェクトの中には、平らなグレイのトーラスと小さな白い円が見えます。このチュートリアルパッチの中央にある jit.gl.gridshape オブジェクトがトーラスを描いています。パッチの右側にあるもう一つの jit.gl.gridshape オブジェクトは、白い円を描いています。このシーンではまだライティングが有効になっていないため、すべて平らな影で表されています。

・上部にある lighting_enable $1smooth_shading $1auto_material $1と書かれた3つのメッセージボックスの上にある toggle ボックスをクリックして、トーラスを描いている jit.gl.gridshape オブジェクトのそれぞれのアトリビュートの値を1に設定して下さい。

デフォルトでは、それぞれのオブジェクトに対するライティングはオフになっているため、これを有効にしなければなりません。スムースシェーディングについても同じことが言えます。この2つのアトリビュートを設定すると、(このチュートリアルを順に読み進んできたとすれば)すでに何回も見てきたように、トーラスが平らな影から立体的な表面を持ったものへ、さらに立体的で滑らかな表面を持ったものへと進行していくようすをみることができます。 auto_material アトリビュートを1に設定しても何の変化も見られないでしょう。この値は、デフォルトですでに1に設定されています。

auto_material $1 と表示されたメッセージボックスの上にある toggle オブジェクトをもう一度クリックし、jit.gl.gridshape オブジェクトの auto_material アトリビュートを0に設定して下さい。

今度はトーラスのライティングに変化が見られるはずです。今までの光沢のないグレイの表現に代わって、次のような光沢のあるグレイによる表現になります。


auto_material アトリビュートをオフにして照らされたトーラス

Jitterでは auto_material アトリビュートが提供されているため、単にオブジェクトのライティングを見るだけであれば、必ずしもマテリアルのすべての要素を指定する必要はありません。GLグループのオブジェクトの auto_material アトリビュートがオンで、オブジェクトへのライティングが有効な場合、オブジェクトに対するマテリアルの拡散反射と環境光の要素がオブジェクトの色に設定され、鏡面反射と放射光の要素は無効になります。この結果は、最初に見たような平らなグレイのトーラスになります。しかし、このチュートリアルでは、すべてのライティング要素が明示的に指定された場合にどのようになるかを見たいため、auto_material アトリビュートをオフに変更しました。

結果としてイメージは輝いて見えます。これはトーラスに適用されているマテリアルが鏡面反射要素を持つためです。

ライトの移動

白い円を描く jit.gl.gridshape オブジェクトには、jit.gl.handle オブジェクトが接続されています。これまで見てきたように、このオブジェクトは、コマンドキーを押し下げたままクリック、ドラッグすることによって、円を移動させることができるようにしてくれます。オプションキーを押し下げたたままドラッグした場合には、シーンの中で、円をカメラに近づけたり、離したりすることができます。

同時に、この jit.gl.handle オブジェクトからの x、y、z 位置の値は unpack オブジェクトを経由してナンバーボックスオブジェクトへ送られ、目で確認することもできるようになっています。pak オブジェクトは、シンボル light_position をメッセージの先頭に加え、リストの最後にもう1つの値を追加します。このメッセージは jit.gl.render オブジェクトへ送られ、シーンのライトの位置を設定します。ライト自身は見ることができず、他のオブジェクトに与える効果だけを見ることができるという点に注意して下さい。白い円は、ライトの位置を確認するために使うことができるマーカーです。

・コマンドキーを押し下げたまま白い円をクリックし、そのままドラッグしてシーンの左下隅へ移動させて下さい。その後、オプションキーを押し下げたままドラッグし、カメラから離して下さい。

光源は白い円とともに移動します。正しい位置に移動している場合、次のようなイメージを生成することができます。


同じシーンで光源を移動したもの

ライトの位置と、カメラの位置に対して反射する光の角度によって、ライトの拡散反射と鏡面反射のコンポーネントは、各頂点でのマテリアルの拡散反射と鏡面反射のコンポーネントと組み合わせられます。ライトを移動させた場合、これらの関連位置が変化するため、各頂点は様々な色になります。

法線:ライティングを行なうためには、モデルの各頂点には対応する法線がなければなりません。法線は、その頂点でオブジェクトの表面に対して垂直に定義されるベクトルで、イメージの鏡面反射や拡散反射コンポーネントの影響を決定するために使われる値です。複雑なオブジェクトに対する適切な法線の生成は、時間のかかる処理になってしまいます。

Jitterは、この不安を2通りの方法によってできる限り取り除こうとしています。第1に、GLグループのほとんどのオブジェクトはそれらに対応した法線を持っています。これは描画を行うオブジェクトによって計算されます。jit.gl.gridshape オブジェクトはその一例です。シェイプとして球が設定された場合、球の中心から外側に向かった法線が、各頂点で生成されます。それぞれのシェイプには法線を計算する異なった方法があるため、シェイプの表面は、曲線を描く部分では滑らかですが、立方体のようなシェイプのエッジ部分はくっきりとして、滑らかでないままになります。

第2に、jit.gl.render オブジェクトに対して、マトリックスとして直接にジオメトリを送った場合、jit.gl.render オブジェクトはその機能を最大限に使って自動的に法線を生成します。tri_gridquad_grid プリミティブを持つマトリックスを送って、結合されたグリッドのジオメトリを描画する場合、生成された法線はグリッドの表面を横切って滑らかにされます。三角形のような他のプリミティブを使ったマトリックスを送る場合、 jit.gl.render はその頂点を滑らかにせずに、ジオメトリにおけるそれぞれ別個のポリゴンに対して法線を生成します。

自分自身の法線を作りたい場合には、jit.gl.render オブジェクトの auto_materials アトリビュートを0に設定することによって法線の自動生成をオフにできます。

チュートリアル37の「覆いの下のジオメトリ(Geometry Under the Hood)」では、Jitterマトリックスによって、頂点、色、法線が jit.gl.render オブジェクトへ渡される方法について解説しています。マトリックスの中でジオメトリに対する法線を指定する方法の詳細については、このマニュアルの 「補遺B:OpenGLマトリックスフォーマット」 を参照して下さい。

鏡面反射光

ライティングの鏡面反射光コンポーネントの値を変更して、より感覚をつかみやすいものにしてみましょう。。

prepend mat_specular オブジェクトの上で swatch オブジェクトを探して下さい。その色見本の中の円を左端に寄せて下さい。これにより、jit.gl.gridshape オブジェクトに、純粋な赤を表すRGB値を持った mat_specular メッセージが送られます。

swatch オブジェクトは、0から255の範囲の3つの整数のリストを出力します。vexpr オブジェクトは、リストの個々の整数値を255.で割り、JitterのGLグループオブジェクトが色を指定するために使う0.〜1.の範囲の浮動小数点値を生成します。


鏡面反射のマテリアル要素への赤の設定


赤いハイライトを生じたトーラス

いま、イメージのハイライト色は赤い色になっています。このとき、ハイライト色を生じさせるために、白い光源の鏡面反射要素にマテリアルの鏡面反射要素が掛け合せられます。

鏡面反射光源の赤、緑、青の値は、それぞれマテリアルの鏡面反射要素の赤、青、緑の値を掛け合わされられます。このため、光源の要素とマテリアルの要素が違う色である場合、結果がゼロになり、ハイライトが生成されない可能性もあります。これは、多かれ少なかれ、現実世界での光とマテリアルとの作用をモデルにしています。赤い照明の部屋で緑の物体を見ると黒く見えます。

prepend light_specular オブジェクトの上の swatch オブジェクトで、円を緑色のところに移動させて下さい。ハイライトが見えなくなります。含まれる赤の量が異なる色の場合、それぞれ異なった赤の輝きが生じます。確認できたら、swatchオブジェクトの上部へ円を移動させ、ハイライトを赤に戻して下さい。

GLグループのオブジェクトの shininess アトリビュートは、マテリアル定義の重要な部分を占めています。これは、光がオブジェクトに反射した場合に、どのくらいの範囲に拡散したり、広がったりするのかを指定するものです。鏡をモデル化する場合、非常に高い shininess の値を使います。現実的なオブジェクトを作成するためには、およそ2から50の値が使用されます。

prepend shininess オブジェクトの上のナンバーボックスを50に設定して下さい。

これは、鏡面反射光のコンポーネントが影響する領域を非常に小さくします。そのため、グレーのままの拡散反射光から非常に際立った赤の鏡面反射光を見ることができます。


非常に輝きのあるトーラス

拡散反射光

もう少し色を操作して、拡散反射光のコンポーネントの影響を調べてみましょう。

prepend mat_diffuse オブジェクトの上にあるswatchオブジェクトを探して下さい。swatchの中の円をおよそ下の図の位置にになるように移動させ、マテリアルの拡散反射光のコンポーネントを深い青にして下さい。


マテリアルの拡散反射を青に設定

トーラスからの拡散反射は青になり、ハイライトはマゼンタになります。これは、オブジェクトのマテリアルが持つカラーコンポーネントが、位置によってライティングの要素と掛け合わされた後に合計され、オブジェクトのそれぞれの頂点の最終的な色が生成されているためです。


青い拡散反射光に赤いハイライトが加えられました

環境光

私たちは、オブジェクトのマテリアルが持つ環境光コンポーネントをまだ変えていません。現在、これはデフォルトのミディアムグレイに設定されていて、これにグローバルな環境光コンポーネントの暗いグレーが掛け合わせられ、上の図のトーラスの暗いグレー領域を作り出しています。グローバルな環境光コンポーネントは、シーンにおけるすべてのマテリアルの環境光コンポーネントに掛け合わせられ、各オブジェクトのそれぞれの頂点に加えられます。 jit.gl.render オブジェクトの light_global_ambient アトリビュートを使うと、グローバルな環境光コンポーネントを設定できます。

prepend mat_ambient の上の swatch オブジェクトの中にある円を緑色に設定し、緑色のマテリアル環境光コンポーネントを生成して下さい。


緑の環境光

マテリアルの環境光コンポーネントは、グローバルな環境照明コンポーネントと掛け合わせられ、グレーの領域を暗い緑色の領域に置き換えます。

シーンの中で移動が可能なライトは、自分自身に環境光コンポーネントを持っていて、これがグローバルな環境光コンポーネントに加えられます。prepend light_ambient オブジェクトの上にある swatch オブジェクトの中の円を移動させると、この色を変えることができます。明るい色に変化させた場合、環境光コンポーネントの輝度が拡散反射光コンポーネントよりも高くなるため、オブジェクト全体が明るく見えるようになります。

これは見苦しい!

おそらく、青い拡散反射光とマゼンタのハイライトを持つ緑色のトーラスはずっと見たいと思っていたようなものではないはずです。すでに色々と試してみたのでなければ、色見本を操作し、より調和のとれた組み合わせを考えつくための良い機会になるでしょう。

このパッチではスペースの関係で彩度のコントロールを行っていない点に注意して下さい。すべてのライティングとマテリアルの要素に対して彩度を減少させた色を指定することはもちろん可能です。

方向によるライティング vs. 位置によるライティング

シーンの中で動かすことができるライトは、方向によるもの(ディレクショナル)あるいは位置によるもの(ポジショナル)のどちらかに設定することができます。jit.gl.render に送られる light_position [x] [y] [z] [w] というメッセージの中で、[w]の値は、方向を持ったライティング、あるいは位置によるライティングのどちらが使用されるかを決定します。 [w]0 の場合、ライトはディレクショナルライトになり、[x][y][z] の値によって、光が来る方向を表す方向ベクトルを指定することになります。[w] が 0でない場合には、ライトはポジショナルライトになり、シーンにおける特定の位置に基づいてオブジェクトを照らします。ライトの位置は、同次座標 [x]/ [w][y] /[w][z]/ [w] によって指定されます。

ポジショナルライトは、シーンの中での人工的な光源をシミュレートするのに適しています。ディレクショナルライトは、典型的なものとしては太陽の代わりになります。これは、シーンの中のオブジェクトの位置を変えても、光の角度の変化を知覚することができないくらい遠方にあるものです。

p moverサブパッチの上にある toggle をオンにして、トーラスがカメラに近付いたり遠ざかったりする動作をスタートさせて下さい。

トーラスがライトのほぼ近くを通過する際に、トーラスの動きにつれて、トーラスの表面でライティングがどのように移動するかに注目して下さい。これがよくわかるように、ライトの位置を移動させておく必要があるかもしれません。

・方向性をもったライティングの効果を見るため、pak light_position 0. 0. 0. 1.オブジェクトの上にある最後のナンバーボックスを1に変更し、その後、0に戻して下さい。

方向性のあるライティングがオンになったため、オブジェクトが位置を変えてもライティングが移動しなくなっている点に注意して下さい。

フォグ

ライティングの場合と同様に、OpenGLでのフォグ(霧)のシミュレーションは、現実の世界の現象と比べると大雑把なものです。しかし、フォグは、イメージに対して豊かな特徴を加えるための便利な方法を提供してくれます。OpenGLフォグは、ライティングの計算が終わった後に、各頂点の色にフォグの色を単純に混ぜ合わせるものですが、このフォグの色は、カメラからのオブジェクトの距離によって量が増えるようになっています。そのため、遠くのオブジェクトはフォグの中で見えなくなってしまいます。

Jitterでは、フォグはGLグループの各オブジェクトに対して fog アトリビュートを使うことにより、オンとオフを切り替えることができます。シーンの中でのいくつかのオブジェクトにはフォグを適用し、他のオブジェクトには適用しないということも可能です。

fog $1というメッセージボックスの上にある toggle をオンにして下さい。これは、トーラスを描いている jit.gl.gridshape オブジェクトのフォグをオンにします。

pak fog_params… オブジェクトの上の右端のナンバーボックスの値を10に設定して下さい。これにより、同じ jit.gl.gridshape オブジェクトに、すべての fog パラメータのリストが送信されます。

p mover サブパッチがまだ動作している場合、カメラから遠ざかるにつれてトーラスが霧(フォグ)の中へ消えていくようすを見ることができるはずです。

pak fog_params オブジェクトの上にある赤いナンバーボックスを1に設定して下さい。これにより、フォグの色は1.、0.2、0.2に指定されます。

この場合、トーラスが遠ざかると消えてしまうのではなく、かえって明るい赤になります。フォグは、遠くのオブジェクトに対してフォグの色をより多く反映させますが、その色が背景色と同じ場合も、そうでない場合もあります。フォグの色と背景色がほとんど同じ場合にのみ、リアルなフォグの効果が得られます。


フォグの中に消えていくトーラス

実装への依存:チュートリアル33で紹介したアンチエイリアシングと同様に、フォグパラメータの効果はシステムによって様々であり、使用されているOpenGLレンダーに依存します。これまで述べてきたフォグの基本的な特徴は、原則的には同じであるべきですが、デンシティ(濃度)パラメータがフォグにどのくらい影響を及ぼすかというような詳細な点については異なるかもしれません。

まとめ

OpenGLのライティングモデルや、Jitterでのその実装についてやや詳しく説明してきました。OpenGLライティングモデルの鏡面反射光、拡散反射光、環境光のコンポーネント(要素)について、また、それらを現実の世界のシーンが持っている様々な側面にどのようにして近づけるのか、そして、イメージを作成するためにそれらがどう組み合わされるのかということについて検討しました。位置によるライティングと方向性を持つライティングの違いについて紹介しました。最後に、それぞれのオブジェクト単位でシーンにフォグを追加する方法について見ました。