Cesium 實現幾何圖形貼地主要采用Stencil Buffer Test技術實現
大致繪制思路:(繪制一直貼地幾何圖形即有三個drawcommand)
第一步:幾何陰影體模板測試
第二步:幾何陰影體模板測試+深度測試
第三步:融合+幾何陰影體模板測試
模板緩沖中的模板值(Stencil Value)通常是8位的,因此每個片段/像素共有256種不同的模板值。下圖是模板測試示例。
下面根據實際使用場景具體分析
第一步:
function getStencilPreloadRenderState(enableStencil, mask3DTiles) {
var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS;
return {
colorMask : {
red : false,
green : false,
blue : false,
alpha : false
},
stencilTest : {
enabled : enableStencil,
frontFunction : stencilFunction,
frontOperation : {
fail : StencilOperation.KEEP,
zFail : StencilOperation.DECREMENT_WRAP,
zPass : StencilOperation.DECREMENT_WRAP
},
backFunction : stencilFunction,
backOperation : {
fail : StencilOperation.KEEP,
zFail : StencilOperation.INCREMENT_WRAP,
zPass : StencilOperation.INCREMENT_WRAP
},
reference : StencilConstants.CESIUM_3D_TILE_MASK,
mask : StencilConstants.CESIUM_3D_TILE_MASK
},
stencilMask : StencilConstants.CLASSIFICATION_MASK,
depthTest : {
enabled : false
},
depthMask : false
};
}
如上圖和代碼所示,繪制模板體,模板測試失敗的保持不變,模板測試通過的+1,注:WebGL規范的6.8節要求引用和掩碼必須相同,正面和背面測試防止調用時出現無效操作錯誤。可以得到如下模板測試結果。其中灰色比較模糊的1是地下的模板體,這里為了示意更清楚,特地區分。
圖3 模板測試圖
代碼跟蹤調試部分代碼如下:
圖4 正面和背面模板狀態更新圖
結合上面貼出的關鍵代碼,正面-1(DECREMENT_WRAP),背面+1(INCREMENT_WRAP),得到如上圖四的區分結果,黃色部分(圖四中黃色應該是在地下)模板值為255,中等淺藍色部分和最上方深藍色部分模板值為0(渲染正面時-1,背面時+1,最后保持不變)。
第二步:
function getStencilDepthRenderState(enableStencil, mask3DTiles) {
var stencilFunction = mask3DTiles ? StencilFunction.EQUAL : StencilFunction.ALWAYS;
return {
colorMask : {
red : false,
green : false,
blue : false,
alpha : false
},
stencilTest : {
enabled : enableStencil,
frontFunction : stencilFunction,
frontOperation : {
fail : StencilOperation.KEEP,
zFail : StencilOperation.KEEP,
zPass : StencilOperation.INCREMENT_WRAP
},
backFunction : stencilFunction,
backOperation : {
fail : StencilOperation.KEEP,
zFail : StencilOperation.KEEP,
zPass : StencilOperation.DECREMENT_WRAP
},
reference : StencilConstants.CESIUM_3D_TILE_MASK,
mask : StencilConstants.CESIUM_3D_TILE_MASK
},
stencilMask : StencilConstants.CLASSIFICATION_MASK,
depthTest : {
enabled : true,
func : DepthFunction.LESS_OR_EQUAL
},
depthMask : false
};
}
第二步在第一步的基礎上繼續模板測試+深度檢測,深度檢測小於等於時通過深度檢測。該檢測即可得到貼地的幾何圖形的模板測試結果。如下圖淺藍色區域即為最終模板檢測結果。
第三步:融合,這里不做解釋。只要模板測試值不為0就渲染。
function getColorRenderState(enableStencil) {
return {
stencilTest : {
enabled : enableStencil,
frontFunction : StencilFunction.NOT_EQUAL,
frontOperation : {
fail : StencilOperation.KEEP,
zFail : StencilOperation.KEEP,
zPass : StencilOperation.DECREMENT_WRAP
},
backFunction : StencilFunction.NOT_EQUAL,
backOperation : {
fail : StencilOperation.KEEP,
zFail : StencilOperation.KEEP,
zPass : StencilOperation.DECREMENT_WRAP
},
reference : 0,
mask : StencilConstants.CLASSIFICATION_MASK
},
stencilMask : StencilConstants.CLASSIFICATION_MASK,
depthTest : {
enabled : false
},
depthMask : false,
blending : BlendingState.ALPHA_BLEND
};
}
GL_FRONT和GL_BACK的分析
三維渲染一般用表面來表示物體,一個物體就是一組平面。光線照射在平面上會產生反射,入射線與反射線的角平分線就是法線,它垂直於平面。面法線有兩種可能的方向,我們稱面有兩側。當描述封閉物體的外表面時,法線應該從內部指向外部;而表示在物體內部時,法線應該從外部指向內部。
用glNormal*(N_Vector);指定當前法向矢量。此后的頂點都會使用該法向,直到再次改變。
面的法向矢量是怎么確定的呢?右手手指沿頂點順序握拳,拇指豎立所指方向即為一個面的法向,如果面對該平面,以逆時針序指定頂點,則法向指向你。當你用這樣的順序指定平面的頂點時,WebGL能夠正確的計算出法向矢量。法矢與頂點坐標一樣要經過模型視矩陣變換,這樣物體坐標系內的法向矢量就可以正確的變成世界坐標系內的法向矢量,保證最終生成的圖像是正確的。
當一個頂點屬於多個平面時,該頂點的法向應該取所在所有平面法向的平均值,以使表面棱角部份平滑自然的,否則會有一道黑線。
我們把平面的法向所指一側稱為前面,另一側稱為后面,可以為兩側分別指定不同的屬性。