WebGL光照陰影對映

2021-09-29 03:35:46 字數 3108 閱讀 1782

經過前面的學習,webgl的基本功能都已經掌握了,我們不僅掌握了著色器的編寫,圖形的繪製,矩陣的變換,新增光照,還通過對webgl的基礎api封裝,編寫出了便利的工具庫. 是時候進一步深入學習webgl的高階功能了,我認為要做逼真的3d特效,陰影絕對是乙個必不可少的環節.現在我們就在之前光照的基礎上新增陰影效果吧.

首先看一下陰影效果的例項:

陰影綜合(多物體高精度pcf)

點光源聚光燈陰影

我們以陰影綜合(多物體高精度pcf)為例, 開始學習陰影相關知識.

幀緩衝提高陰影精度

抗鋸齒(pcf)

我們實現陰影效果使用的是叫陰影對映的技術, 在此之前要提到以下兩個物件:幀緩衝區物件和渲染緩衝區物件.

幀緩衝區物件(framebuffer object)可以用來代替顏色緩衝區或深度緩衝區.繪製在幀緩衝區中的物件並不會直接顯示canvas上,可以先對幀緩衝區中的內容進行一些處理再顯示,或者直接用其中的內容作為紋理影象.在幀緩衝區中進行繪製的過程又稱為離屏繪製(offscreen drawing).

而渲染緩衝區物件就是我們繪圖使用的緩衝區, 它會直接把內容渲染到canvas中.

而我們現在先有這個概念,來看看幀緩衝區的建立和配置:

1. 建立幀緩衝區物件 gl.createframebffer().

2. 建立文理物件並設定其尺寸和引數 gl.createtexture()、gl.bindtexture()、gl.teximage2d()、gl.parameteri().

3. 建立渲染緩衝區物件 gl.createrenderbuffer().

4. 繫結渲染緩衝區物件並設定其尺寸 gl.bindrenderbuffer()、gl.renderbufferstorage().

5. 將幀緩衝區的顏色關聯物件指定為乙個文理物件 gl.frambuffertexture2d().

6. 將幀緩衝區的深度關聯物件指定為乙個渲染緩衝區物件 gl.framebufferrenderbuffer().

7. 檢查幀緩衝區是否正確配置 gl.checkframebufferstatus().

8. 在幀緩衝區中進行繪製 gl.bindframebuffer().

它的建立和配置是乙個非常繁瑣的過程,我們先熟悉了怎麼使用,再慢慢研究它內部的原理,所以先把上面的步驟封裝成乙個黑盒子,我這裡就是initframebufferobject這個函式.

#ifdef gl_es 

precision mediump float;

#endif

void main()

深度值後面加了0.005,稍微大於1/256,即8位的表示範圍(因為乙個分量就是8位),這個是消除馬赫帶用的,不加這個值,畫面會產生難看的條紋,具體的原理可查詢馬赫帶,在此不細講.

#ifdef gl_es

precision mediump float;

#endif

uniform sampler2d u_shadowmap;

varying vec4 v_positionfromlight;

varying vec4 v_color;

void main()

完成了最簡單的陰影效果,但是當你把光源與物體的距離拉遠,問題出來了,怎麼看不到陰影了?這是距離超過了8位的儲存範圍,溢位的緣故.之前我們只使用了乙個分量來儲存,現在我們把其他的分量也利用起來吧,rgba一共32位.

這中間進行複雜的分解運算,並同時去除異常值,請看如下**

// ...

/*** 分解儲存深度值

*/vec4 pack (float depth)

void main()

這裡對應就要解碼出深度值

// ...

/*** 發布深度值z

*/float unpack(const in vec4 rgbadepth)

// ...

解決了精度的問題,接著繼續優化. 執行起來吧,陰影很粗糙有木有? 你看看下面左圖,很嚴重的鋸齒, 抗鋸齒有很多種解決方案,我這裡使用pcf, 也就是百分比漸近式過濾演算法,因為它基於**實現的,所以也叫軟陰影.

pcf的原理也很簡單, 採集當前點周圍畫素的陰影值,並將其深度與所有採集的樣本進行比較,最後對結果進行平均,這樣就得到光線和陰影之間更平滑的過渡效果.下面右圖是經過pcf處理之後的陰影,效果要自然得多了.

我們看正常著色器的實現**

// ...

vec3 shadowcoord = (v_positionfromlight.xyz/v_positionfromlight.w)/2.0 0.5;

float shadows =0.0;

float opacity=0.6;// 陰影alpha值, 值越小暗度越深

float texelsize=1.0/1024.0; // 陰影畫素尺寸,值越小陰影越逼真

vec4 rgbadepth;

// 消除陰影邊緣的鋸齒

for(float y=-1.5; y <= 1.5; y = 1.0)

}shadows/=16.0; // 4*4的樣本

float visibility=min(opacity (1.0-shadows),1.0);

specular=visibility < 1.0 ? vec3(0.0,0.0,0.0): specular; // 陰影處沒有高光

gl_fragcolor = vec4((diffuse ambient specular) * visibility, v_color.a);

webgl的陰影部分,涉及到了很多openggl的底層,計算機圖形學,演算法. 為了深入理解它,可真是花費了很多腦力,是到目前為止學習webgl的第一道坎,它裡面的水很深.比如光是反鋸齒部分就涉及到很多低層細節,演算法的實現,顯示卡的效能問題等都是需要考慮的, 陰影部分後續還要慢慢查資料繼續優化.

越是深入學習webgl,就越覺得它相關的資料真是少,必須看opengl es相關的東西才能解決,傷不起啊.

更多專業前端知識,請上

【猿2048】www.mk2048.com

WebGL光照陰影對映

經過前面的學習,webgl的基本功能都已經掌握了,我們不僅掌握了著色器的編寫,圖形的繪製,矩陣的變換,新增光照,還通過對webgl的基礎api封裝,編寫出了便利的工具庫.是時候進一步深入學習webgl的高階功能了,我認為要做逼真的3d特效,陰影絕對是乙個必不可少的環節.現在我們就在之前光照的基礎上新...

webgl 平面陰影效果

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

WebGL 陰影的實現

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