WebGL半透明物體的繪製

WebGL 中當透明和半透明物體共存時,相關設置不正確的話,物體表面會出現破碎雜亂的斷面,很是影響效果,咱們接着就來解決這個問題。 完成的展現Demo請看: 半透明物體和透明物體共存 javascript

alpha混合

α 混合

讓物體實現半透明效果須要用到顏色的α份量。該功能被稱爲a混合(alpha blending) 或 混合 blending,WebGL已經內置該功能,但須要開啓,若是隻設置了顏色的第四個份量 α 是看不到透明效果的,這第四份量α其實和 css 樣式的 rgba / hsla 顏色模式 中的 α 是同樣的,或者相似 opacity 屬性。必需要執行下面兩個步驟才能看到透明效果:css

  1. 開啓混合功能:gl.enable(gl.BLEND)。
  2. 指定混合函數:gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)。

gl.blendFunc(src_factor, dst_factor)

a混合函數,指定如如何混合二者的顏色,權重因子的類型多種多樣,參數:html

  • src_factor: 指定源顏色在混合顏色的權重因子,以下表所示
  • dst_factor: 指定目標顏色在混合後顏色的權重因子,以下表所示
// 混合顏色計算公式: 
<混合後的顏色> = <源顏色> * src_factor + <目標顏色> * dst_factor

// 通常半透明效果經常使用以下形式
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
複製代碼

權重因子列表

(Rs,Gs,Bs,As) 表示源顏色各份量, (Rd,Gd,Bd,Ad) 表示目標顏色的各份量java

常量 R份量的係數 G份量的係數 B份量的係數
gl.ZERO 0.0 0.0 0.0
gl.ONE 1.0 1.0 1.0
gl.SRC_COLOR Rs Gs Bs
gl.ONE_MINUS_SRC_COLOR 1-Rs 1-Gs 1-Bs
gl.DST_COLOR Rd Gd Bd
gl.ONE_MINUS_DST_COLOR 1-Rd 1-Gd 1-Bd
gl.SRC_ALPHA As As As
gl.ONE_MINUS_SRC_ALPHA 1-As 1-As 1-As
gl.DST_ALPHA Ad Ad Ad
gl.ONE_MINUS_DST_ALPHA 1-Ad 1-Ad 1-Ad
gl.SRC_ALPHA_SATUREATE min(As,Ad) min(As,Ad) min(As,Ad)

透明和不透明物體共存

實現 a 混合最簡單的方式是屏蔽掉隱藏面消除功能,即去掉 gl.enable(gl.DEPTH_TEST),但關閉隱藏面消除功能是一個粗暴的解決方案,並不能知足實際需求。其實可經過某些機制,同時實現隱藏面消除和半透明效果,步驟以下:git

//1.開啓隱藏面消除功能:
gl.enable(gl.DEPTH_TEST)。

//2.繪製全部不透明的物體(a == 1.0)

//3.鎖定深度緩衝區的寫入操做,使之只讀 (深度緩衝區用於隱藏面消除):
gl.depthMask(false);

//4.繪製全部半透明的物體 a < 1.0,注意將物體按深度排序,a 最小最早繪製

//5.釋放深度緩衝區,使之可讀可寫: 
gl.depthMask(true)
複製代碼

gl.depthMask(mask)

鎖定或釋放深度緩衝區的寫入操做github

mask: 鎖定深度緩衝區的寫入操做 false,釋放 trueweb

實現效果

咱們寫個Demo來實際演示效果,好比我要繪製8個物體,其中前面4個是非透明的物體,即 α 份量值則爲1,剩餘物體的 α 份量分別從 0.1至0.4不等。dom

for (var i = 0; i < 8; i++) {
    let color = randomColor();
    color[3] = i > 3 ? (i - 3)/10 :1;// 透明物體 α 份量小於1,非透明物體則等於1
    
    Polygons.push({
        x: random(-9,9),
        y: random(0, 6),
        z: random(-5,5),
        color: color
    });
}
複製代碼

若是是繪製的物體隊列是無序的,則必須手動排序。但我這裏創建圖形時已經排好序,前4個爲不透明物體,剩餘是透明物體,因此可直接按順序繪製,針對是否爲透明物體,分別設置緩衝區寫入和隱藏面刪除功能。函數

if(i < 4){ // 非透明物體
    gl.depthMask(true);
    gl.disable(gl.BLEND);
} else { //透明物體
    gl.depthMask(false);
    gl.enable(gl.BLEND);
    gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
}
drawBufferInfo(gl, vao);
複製代碼

最終完成效果請看:半透明物體和透明物體共存webgl

相關文章
相關標籤/搜索