webgl centroid質心插值的一點理解

木的樹發表於2023-10-04

質心插值說的是什麼

2023.10.04再次review這個細節點:
基本上把這個問題看明白了;centroid代表質心插值;問題來自於在對普通的varying變數進行插值時,預設都是採用片元中心點來進行計算獲取最終插值,但是有些情況中心點並不在圖形的覆蓋範圍內,這時候中心點插值就會超出當初設定的一個值域範圍,導致在某些邊界存在異常情況;比如上面連結中的程式碼,在藍色區域竟然出現了黃色就有問題。
 
產生這個問題的原因在於,針對離散的片元在進行光柵化差值時候,有些片元雖然只有少部分被幾何區域覆蓋,但這些片元也參與光柵化並被作為圖形的一部分;這就導致使用片元中心點參與插值計算時,得到的計算結果會不在頂點的值域範圍內。
而質心插值採用的是,幾何邊界所覆蓋片元部分的一個質心點來參與插值計算。這樣就能夠保證最終光柵化的每個片元獲取的數值都在頂點的值域範圍內。
下面這個示例就是在表達這種情況:
左邊沒有使用質心插值,在下面程式碼構造的場景中就會出現超出值域範圍(0-1)的問題;右邊採用了質心插值,則沒有出現這個問題。
<!-- WebGL 2 shaders -->
    <script id="vs-render" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 0
        #define ATTRIBUTE_DATA_LOCATION 6
        
        precision highp float;
        precision highp int;

        uniform mat4 MVP;

        layout(location = POSITION_LOCATION) in vec2 position;
        layout(location = ATTRIBUTE_DATA_LOCATION) in float data;
        
        out float v_attribute;

        void main()
        {
            gl_Position = MVP * vec4(position, 0.0, 1.0);
            v_attribute = data;
        }
    </script>

    <script id="fs-render" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;

        in float v_attribute;
        out vec4 color;

        void main()
        {
            const vec4 blue   = vec4( 0.0, 0.0, 1.0, 1.0 );
            const vec4 yellow = vec4( 1.0, 1.0, 0.0, 1.0 );
            color = v_attribute >= 0.0 ? mix(blue, yellow, sqrt(v_attribute)) : yellow;
        }
    </script>
    
    <script id="vs-render-centroid" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 0
        #define ATTRIBUTE_DATA_LOCATION 6
        
        precision highp float;
        precision highp int;

        uniform mat4 MVP;

        layout(location = POSITION_LOCATION) in vec2 position;
        layout(location = ATTRIBUTE_DATA_LOCATION) in float data;
        
        centroid out float v_attribute;

        void main()
        {
            gl_Position = MVP * vec4(position, 0.0, 1.0);
            v_attribute = data;
        }
    </script>
    
    <script id="fs-render-centroid" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;

        centroid in float v_attribute;
        out vec4 color;

        void main()
        {
            const vec4 blue   = vec4( 0.0, 0.0, 1.0, 1.0 );
            const vec4 yellow = vec4( 1.0, 1.0, 0.0, 1.0 );
            color = v_attribute >= 0.0 ? mix(blue, yellow, sqrt(v_attribute)) : yellow;
        }
    </script>
那麼質心怎麼計算?
OpenGL allows implementers to choose the ideal centroid, or any location that is inside the intersection of the pixel square and the primitive, such as a sample point or a pixel center.
意思就是OpenGL規範允許具體實現者自己來決定,可以使用幾何覆蓋區域的中心點,或者直接使用片元的中心點也可以(當然這時候會有問題)。目前看起來當宣告瞭使用質心插值時,大多數顯示卡的實現方式是,如果覆蓋了片元中心點那麼就是用片元中心點就是用中心點計算插值,當沒有覆蓋中心點就是用幾何邊界與片元覆蓋區域(是一個三角形)計算出三角形質心(有專門的計算公式。
 

到底該不該用

質心插值也不是什麼時候都適用,大部分時候影響不大(如果重要webgl1不會捨棄)。如果你的程式碼裡有一些內建函式依賴插值結果,並且可能出現異常情況,比如對一個插值求開平方,如果插值出來結果是負數,對負數求開平方就有問題。

其次當著色器中程式碼邏輯,因為這個超出值域範圍的數字影響特別大時候需要處理,比如對一個數求高次的冪邏輯。
 
也有一些情況不適用質心插值。比如程式碼中使用了一些導數計算,如dfx、dfy,因為他們的步長和方向都已經變了(delta的取值不再是兩個片元的中心點差值了);另外如果非質心插值對效果影響不大可以不用管他,因為它的開銷還是比較大。

 

 

 

相關文章