環境光によるライティング
平行光源の弱点
前回は平行光源によるライティングに挑戦しました。
平行光源では、光の向きは常に一定です。
それをシミュレートするために
モデル変換行列の逆行列を生成する必要があったり、モ
デルデータに法線情報を追加したりする必要がありましたね。
平行光源は比較的計算の負荷も小さく、ある程度それらしいライティングが可能なため、3D プログラミングの世界ではよく使われています。
弱点
光が当たっていない部分をうまく演出できないことです。
たとえば前回のサンプルの頂点シェーダをよく見てみると、ライトベクトルと法線との内積を取っている部分で、実はちょっとしたズルをしています。
前回のサンプルの一部分を抜粋
float diffuse = clamp(dot(normal, invLight), 0.1, 1.0);
さて、ここでは GLSL の組み込み関数である clamp を使っていますね。この関数は引数として与えられた数値を一定の範囲に収めてくれます。先ほどのコードのように指定すれば、結果は必ず 0.1 ~ 1.0 の範囲に収まることになります。
最小が0.1なので真っ暗にならない
https://gyazo.com/1588321cf708a82b59de278c51bd58da
ゼロクランプバージョン
前回のサンプルのように、丸めの範囲をある程度広く設けることで今回の問題には対処できます。しかし、環境光という新しい光の概念を用いれば、この問題にすんなりと対応することが可能になります。
環境光を定義する一例
// 環境光の色
var ambientColor = [0.1, 0.1, 0.1, 1.0];
環境光を用いる際には、その色の強度に注意しましょう。
環境光は先ほども書いたようにありとあらゆるものを照らすことになるので、
たとえば上記のコードで 0.1 を指定している部分を全部 1.0 などにしてしまうと描画されるモデルは全て真っ白になってしまいます。
全体の色に加算するため
平行光源による陰影付けがどうなっていようと関係なく、全てを真っ白になるまで照らしてしまいますので気をつけましょう。
環境光の色は、せいぜい 0.2 くらいまでの範囲に収めておいたほうが無難です。
ちなみに今回のサンプルは 0.1 で設定しています。
頂点シェーダと javascript を修正
さて、それでは各種ソースの修正箇所を見ていきます。まずは頂点シェーダから。
頂点シェーダのソース
code:glsl
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
uniform mat4 invMatrix;
uniform vec3 lightDirection;
uniform vec4 ambientColor;
varying vec4 vColor;
void main(void){
vec3 invLight = normalize(invMatrix * vec4(lightDirection, 0.0)).xyz;
float diffuse = clamp(dot(normal, invLight), 0.0, 1.0);
vColor = color * vec4(vec3(diffuse), 1.0) + ambientColor;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
前回から追加されている uniform 変数が一つありますね。それが vec4 として宣言されている ambientColor です。環境光は、平行光源による一連の計算が終わり、最終的に出力される色を決める段階で加算します。
ここで仮に足し合わせではなく掛け合わせで処理してしまうと、逆にシーン全体が暗くなってしまうので注意しましょう。
続いてはメインプログラムを修正します。
とは言っても、頂点シェーダに環境光のパラメータを渡すだけですので、追加でやることはほんの少しです。
まずは環境光のパラメータをプログラムの中で定義しておきます。
環境光のパラメータを追加
code:js
// 環境光の色
さらに、これを正しく頂点シェーダに渡すために、シェーダの uniformLocation を取得する部分を追加します。
code:js
// uniformLocationを配列に取得
var uniLocation = new Array();
uniLocation0 = gl.getUniformLocation(prg, 'mvpMatrix'); uniLocation1 = gl.getUniformLocation(prg, 'invMatrix'); uniLocation2 = gl.getUniformLocation(prg, 'lightDirection'); uniLocation3 = gl.getUniformLocation(prg, 'ambientColor'); さらに、これを uniform 変数として恒常ループの中でシェーダに送れば OK ですね。
シェーダへ環境のパラメータを送る
code:js
// uniform変数の登録
gl.uniformMatrix4fv(uniLocation0, false, mvpMatrix); gl.uniformMatrix4fv(uniLocation1, false, invMatrix); gl.uniform3fv(uniLocation2, lightDirection); gl.uniform4fv(uniLocation3, ambientColor); これで、頂点シェーダと javascript プログラムの両方を修正できましたね。
ちなみに、今回のサンプルでは環境光を導入しましたので、平行光源の計算を行なっている部分ではクランプの範囲を 0.0 ~ 1.0 の範囲に変更しています。平行光源によってシミュレートされる光が当たらない部分は、純粋に環境光によって照らされることになります。
まとめ
環境光は、自然界の光の乱反射をシミュレートする概念で、平行光源の弱点をうまくカバーしてくれます。普通、これらの二つの光は同時に利用されます。環境光だけでライティングするとモデルの凹凸が表現されませんし、平行光源だけでライティングすると陰影がきつくなりすぎてしまうからですね。
3D プログラミングにおける代表的なライティングである環境光と平行光源による拡散光。今回のサンプルでそこまで実現できました。次回は、反射光と呼ばれるライティングについて解説します。