[WebGL入門]二十二,從環境光線源發出的光,webgl光源
註:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。
本次的demo的運行結果
平行光源的弱點
上次挑戰了一下從平行光源發出的光。平行光源的光的方向是固定的。而且,為了類比這些,需要用到模型變換矩陣的逆矩陣,以及需要向模型資料中加入法線情報等等。
平行光源的計算負擔比較小,在一定程度上類比了光照效果,在3D類比世界中經常被用到。但是,平行光源也有弱點,陰面的部分,就是沒有被光照到的部分是無法完美的類比。
比如上次的demo中的定點著色器,仔細觀察的話,擷取光向量和法線的內積的部分,實際上是取巧的。
>上次的demo中的一部分代碼
float diffuse = clamp(dot(normal, invLight), 0.1, 1.0);
這裡使用了GLSL的內建函數clamp,這個函數是將參數的數值限制到指定的範圍之內,上面的代碼,結果會限制在0.1 ~ 1.0之間。
但是,擷取光向量和法線的內積,根據使用場合的不同,可能會出現負數,而使用了clamp函數,即使有負數,也會被指定的最小值0.1代替,假設把clamp的範圍設定為0.0 ~ 1.0之間會是什麼效果呢,試著運行一下的話,出現效果。
這樣,完全沒有和光發生碰撞的地方會變成完全的黑色。這樣會導致無法分清楚模型的輪廓,這就是平行光源的缺點。
就像上次的demo那樣,將光照係數的範圍設定的大一些,在一定程度上可以解決上述問題。但是使用環境光線源的話,可以徹底解決這個問題。
什麼是環境光線源
環境光線是類比現實世界中的自然光的漫反射。現實世界中,從太陽或是照明器械等發射出的光,遇到物體或者大氣中的灰塵等遮擋而發生反射,將世界照亮。比如說,在一個漆黑的屋子裡面,只需要一個燈泡,背對著燈泡的話,就會看到自己的影子映射到床或者牆壁上,而自己的身體雖然沒有直接被光照到,但是也應該能看得見吧。
由於牆壁和屋頂,以及床和大氣中的灰塵等對燈泡發出的光的反射,即使是沒有直接被光照到的部分也會受到光的影響。這樣,呈現光的漫反射的就是環境光線源了。
環境光線源用於照亮三維空間中的所有部分。就是說,不是根據頂點的不同處理attribute變數,而是向著色器中傳遞uniform變數。進一步說,環境光線最終影響的是在context中輸出的顏色,處理包含四個元素的顏色情報。
>定義環境光線的例子
var ambientColor = [0.1,0.1,0.1,1.0];
使用環境光線源的時候,需要注意顏色的亮度。環境光線照的是全部,比如上面的代碼中指定的0.1,如果全都換成1.0的話,模型就會變成全白了。和平行光源不一樣,所以要注意。
環境光線的顏色,最好是限制在0.2左右以下,這次的demo使用的是0.1。
頂點著色器和javascript的修改
接著,看一下各個代碼部分的修改。先從頂點著色器開始看。
>頂點著色器代碼
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。環境光線是在平行光源等一連串的計算結束之後,最後輸出顏色的階段開始添加的。
這裡,如果不使用加法而使用乘法的話,整個畫面都會變暗,所以要特別注意。
接下來,修改主程式。
說起來,只是把環境光線作為參數傳給頂點著色器,追加的東西還是挺少的。
首先在程式中定義環境光線的參數。
>增加環境光線參數
var ambientColor = [0.1,0.1,0.1,1.0];
接著,為了正確的傳給頂點著色器,追加擷取著色器的uniformLocation的部分。
// 將uniformLocation儲存到數組中var uniLocation = new Array();uniLocation[0] = gl.getUniformLocation(prg, 'mvpMatrix');uniLocation[1] = gl.getUniformLocation(prg, 'invMatrix');uniLocation[2] = gl.getUniformLocation(prg, 'lightDirection');uniLocation[3] = gl.getUniformLocation(prg, 'ambientColor');
接著,作為uniform變數在持續迴圈的時候傳給著色器就行了。
>向著色器中傳送環境光線的參數
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);gl.uniform3fv(uniLocation[2], lightDirection);gl.uniform4fv(uniLocation[3], ambientColor);
這樣,就完成了頂點著色器和javascript程式的修改了。
其實,因為這次引入了環境光線源,所以平行光源部分的光照係數設定成了0.0 ~ 1.0,沒有被平行光源照到的部分,就會使用純粹的環境光線來照射。
總結
環境光線,類比了自然界的光的漫反射,彌補了平行光源的缺點。一般,這兩種光會同時使用。只使用環境光線的話,無法表現出模型的凹凸,只使用平行光源的話,陰影過於嚴重無法分清模型的輪廓。
3D類比中的擴散光的代表就是環境光線和平行光。這次的demo也實現到了這一步。下次會介紹一下反射光。
點擊下面的串連,可以確認一下今天的內容。
同時使用平行光源和環境光線源照射的圓環體
http://wgld.org/s/sample_010/
轉載請註明: