WebGL簡易教程 十四 陰影

2022-07-04 01:51:13 字數 4068 閱讀 4910

目錄2.2. 繪製部分

3. 結果

4. 參考

所謂陰影,就是物體在光照下向背光處投下影子的現象,使用陰影技術能提公升圖形渲染的真實感。實現陰影的思路很簡單:

找出陰影的位置。

將陰影位置的圖元調暗。

很明顯,關鍵還是在於如何去判斷陰影的位置。陰影檢測的演算法當然可以自己去實現,但其實opengl/webgl已經隱含了這種演算法:假設攝像機在光源點,視線方向與光線一致,那麼這個時候檢視中看不到的地方肯定就是存在陰影的地方。這實際上是由光源與物體之間的距離(也就是光源座標系下的深度z值)決定的,深度較大的點為陰影點。如下圖所示,同一條光線上的兩個點p1和p2,p2的深度較大,所以p2為陰影點:

圖1-1:通過深度來判斷陰影

同樣的定義了兩組著色器,一組繪製在幀快取,一組繪製在顏色快取。在需要的時候對兩者進行切換。

繪製幀快取的著色器如下:

// 頂點著色器程式-繪製到幀快取

var frame_vshader_source =

'attribute vec4 a_position;\n' + //位置

'attribute vec4 a_color;\n' + //顏色

'uniform mat4 u_mvpmatrix;\n' +

'varying vec4 v_color;\n' +

'void main() \n';

// 片元著色器程式-繪製到幀快取

var frame_fshader_source =

'precision mediump float;\n' +

'varying vec4 v_color;\n' +

'void main() \n';

其中,頂點著色器部分沒有變化,主要是根據mvp矩陣算出合適的頂點座標;在片元著色器中,將渲染的深度值儲存為片元顏色。這個渲染的結果將作為紋理物件傳遞給顏色快取的著色器。

這裡片元著色器中的深度rgbadepth還經過一段複雜的計算。這其實是乙個編碼操作,將16位的深度值gl_fragcoord.z編碼為4個8位的gl_fragcolor,從而進一步提公升精度,避免有的地方因為精度不夠而產生馬赫帶現象。

在顏色快取中繪製的著色器**如下:

// 頂點著色器程式

var vshader_source =

'attribute vec4 a_position;\n' + //位置

'attribute vec4 a_color;\n' + //顏色

'attribute vec4 a_normal;\n' + //法向量

'uniform mat4 u_mvpmatrix;\n' + //介面繪製操作的mvp矩陣

'uniform mat4 u_mvpmatrixfromlight;\n' + //光線方向的mvp矩陣

'varying vec4 v_positionfromlight;\n' +

'varying vec4 v_color;\n' +

'varying vec4 v_normal;\n' +

'void main() \n';

// 片元著色器程式

var fshader_source =

'#ifdef gl_es\n' +

'precision mediump float;\n' +

'#endif\n' +

'uniform sampler2d u_sampler;\n' + //陰影貼圖

'uniform vec3 u_diffuselight;\n' + // 漫反射光顏色

'uniform vec3 u_lightdirection;\n' + // 漫反射光的方向

'uniform vec3 u_ambientlight;\n' + // 環境光顏色

'varying vec4 v_color;\n' +

'varying vec4 v_normal;\n' +

'varying vec4 v_positionfromlight;\n' +

'float unpackdepth(const in vec4 rgbadepth) \n' +

'void main() \n';

這段著色器繪製**在教程《webgl簡易教程(十):光照》繪製顏色和光照的基礎之上加入可陰影的繪製。頂點著色器中新加入了乙個uniform變數u_mvpmatrixfromlight,這是在幀快取中繪製的從光源處觀察的mvp矩陣,傳入到頂點著色器中,計算頂點在光源處觀察的位置v_positionfromlight。

v_positionfromlight又傳入到片元著色器,變為該片元在光源座標系下的座標。這個座標每個分量都是-1到1之間的值,將其歸一化到0到1之間,賦值給變數shadowcoord,其z分量shadowcoord.z就是從光源處觀察時的深度了。與此同時,片元著色器接受了從幀緩衝物件傳入的渲染結果u_sampler,裡面儲存著幀緩衝物件的深度紋理。從深度紋理從取出深度值為rgbadepth,這是之前介紹過的編碼值,通過相應的解碼函式unpackdepth(),解碼成真正的深度depth,也就是在光源處觀察的片元的深度。比較該片元從光源處觀察的深度shadowcoord.z與從光源處觀察得到的同一片元位置的渲染深度depth,如果shadowcoord.z較大,就說明為陰影位置。

注意這裡比較時有個0.0015的容差,因為編碼解碼的操作仍然有精度的限制。

主要的繪製**如下:

//繪製

function drawdem(gl, canvas, fbo, frameprogram, drawprogram, terrain)

//獲取光線:平行光

var lightdirection = getlight();

//預先給著色器傳遞一些不變的量

//開始繪製

var tick = function () ;

tick();

}

利用幀快取繪製陰影的關鍵就在於繪製了兩遍地形,乙個是關於當前檢視觀察下的繪製,另乙個是在光源處觀察的繪製,一定要確保兩者的繪製都是正確的,注意兩者繪製時的mvp矩陣。

2.2.2.1. 獲取平行光

這個例項模擬的是在太陽光也就是平行光下產生的陰影,因此需要先獲取平行光方向。這裡描述的是太陽高度角30度,太陽方位角315度下的平行光方向:

//獲取光線

function getlight()

2.2.2.2. 設定幀快取的mvp矩陣

對於點光源光對物體產生陰影,就像在點光源處用透視投影觀察物體一樣;與此對應,平行光對物體產生陰影就需要使用正射投影。雖然平行光在設定mvp矩陣的時候沒有具體的光源位置,但其實只要確定其中一條光線就可以了。在幀快取中繪製的mvp矩陣如下:

//設定mvp矩陣

function setframemvpmatrix(gl, sphere, lightdirection, frameprogram)

這個mvp矩陣通過地形的包圍球來設定,確定一條對準包圍球中心得平行光方向,設定正射投影即可。在教程《webgl簡易教程(十二):包圍球與投影》中論述了這個問題。

2.2.2.3. 設定顏色快取的mvp矩陣

設定實際繪製的mvp矩陣就恢復成使用透視投影了,與之前的設定是一樣的,同樣在教程《webgl簡易教程(十二):包圍球與投影》中有論述:

//設定mvp矩陣

function setmvpmatrix(gl, canvas, sphere, lightdirection, drawprogram)

最後在瀏覽器執行的結果如下所示,陰影存在於一些光照強度較暗的地方:

圖3-1:地形的陰影

通過shadowmap生成陰影並不是要自己去實現陰影檢查演算法,更像是對圖形變換、幀緩衝物件、著色器切換的基礎知識的綜合運用。

WebGL簡易教程 目錄

目錄最近研究webgl,看了 webgl程式設計指南 這本書,結合自己的專業知識寫的一系列教程。之前在看opengl webgl的時候總是感覺opengl webgl看的時候懂,實際用起來卻挺難,感覺中間總是隔著很多東西。現在一路邊學邊寫,才明白這中間缺少的其實就是總結,是實踐 把這個過程寫出來,既...

webgl 平面陰影效果

在特定的3d場景中,陰影效果有時還是顯得十分重要的,在一般的3d引擎當中設定陰影可以直接通過對物體設定屬性來實現,十分的方便,這裡我們就用webgl來實現一下平面效果。平面陰影是通過燈光將物體的陰影投射在乙個平面內,但是物體之間沒有陰影的疊加,也就是說a物體的陰影不會投射到b物體上,在本案例中我們主...

WebGL 陰影的實現

原理 根據光源與物體之間的距離 也就是物體在光源座標系下的深度z值 來決定物體是否可見。如下圖,同一條光線上有兩個點p1 p2,由於p2的z值大於p1,所以p2在陰影中。需要使用兩對著色器 1 一對著色器用來計算光源到物體的距離 2 另一對根據 1 中計算出的距離繪製場景。2 如何使用 1 中的距離...