WebGL中深度碰撞方法總結

木的樹發表於2019-07-26

z-fighting問題是三維渲染中常見的問題,本文根據實際工作中遇到的一些場景,進行了系統的總結

一個實際工作中的問題

當兩個面離得太近就會發生深度碰撞問題,比如:

遇到深度檢測問題,最重要的是先搞明白是哪兩個面離得太近導致的問題。比如上面這個問題,一直以來我都以為是柱子的面跟底圖基礎底面的問題。所以嘗試了各種解決深度檢測的問題都沒起作用。

 

直到後面一次偶然的嘗試,開啟了CULL_FACE後,這個深度碰撞正常了。思考了很久才想到原來它發生深度碰撞的原因不是跟地圖底面,而是柱子的上頂面跟下頂面離得太近發生的碰撞。恍然大悟!

這張圖的表現很有欺騙性,底面是黑色的,而剛好碰撞部分也是一部分藍,一部分發暗,所以很讓人想當然的認為是底圖跟柱子之間的問題。這個原因是因為預設沒有面剔除,導致底面也被繪製了,而底面的法線方向與光線方向夾角很大,導致最後計算的顏色發暗。所以碰撞部分一部分明亮,一部分發暗。

換個了底圖樣式,仍然是這種結果,可以證明上述原因。

 

最終這個問題的解決方式是,開啟CULL_FACE,剔除背面三角形,同時在著色中為頂點增加一點偏移

let parameters = {
            [GL.DEPTH_TEST]: true,
            [GL.CULL_FACE]: true,
            [GL.CULL_FACE_MODE]: GL.FRONT
        };
// 計算cube該頂點的位置, cube的X座標範圍是-1~1,(rotatedPosition.x * coverage + 1.0) / 2.0座標範圍在0~1之間
  // cube的Z座標範圍是-1~1,(rotatedPosition.z * coverage - 1.0) / 2.0座標範圍在-1~0之間
  // cubeTopLeftPosition在cube區域性座標系的(-1, 0, -1)位置
  vec4 vertexPosition = cubeTopLeftPosition + vec4(
    vec2(
      (rotatedPosition.x * coverage + 1.0) / 2.0 * useRadius,
      (rotatedPosition.z * coverage - 1.0) / 2.0 * useRadius
    ),
    1.0, 1.0
  );

深度檢測根本原因

由於z-buffer的精度並不是線性相關的,而是在靠近near平面是精度非常大,但是靠近遠平面時精度非常低,所如果平面離著相機非常遠,那麼就很可能出現深度檢測問題。

解決方法

1.      首先搞明白是哪兩個面發生的深度碰撞

2.      資料層面永遠不要把兩個物體靠的太近,最好在使用者不太注意的地方稍微加一點偏移

3.      將near設定的大一些,這樣使得場景中的物體都在高精度範圍內,但這種方式也是需要調整,near設定的太大,會導致一些應當在視野範圍內的物體被裁切掉

4.      在著色器中適當增加一個小的偏移

5.      利用depthRange來調整深度緩衝範圍

6.      修改投影矩陣的第十位,增加一個小的偏移(http://note.youdao.com/noteshare?id=43a15cadb1afebb1b4ad24a4c159d1e0&sub=37ECF8DF031440D99B69D9CE60850F8A

 

相關文章