實施vertex compression所遇到的各類問題和解決辦法

關於頂點壓縮,好處是能夠減小帶寬,必定程度提升加載速度,能夠提升約5-10%的fps,特別是mobile上,簡單描述就是:算法

壓縮以前(32字節)安全

position float3 12
normal float3 12
texcoord0 float2 8函數

壓縮以後(16字節)優化

position short4 8
normal ubyte4 4
texcoord0 short2 4spa

 

壓縮的方法,其實就是在bounding box內分65536份,用"-32767.5"到"32767.5"描述。3d

參考文章:「Vertex Decompression using Vertex Shaders Part 2」 by Dean Calve @ShaderX Programmingcode

示例代碼以下:orm

    // 計算position的範圍
    oiram::vec3 posCenter(    (mMesh.boundingBox.pmax.x + mMesh.boundingBox.pmin.x) * 0.5f, 
                            (mMesh.boundingBox.pmax.y + mMesh.boundingBox.pmin.y) * 0.5f, 
                            (mMesh.boundingBox.pmax.z + mMesh.boundingBox.pmin.z) * 0.5f),

                posExtent(    (mMesh.boundingBox.pmax.x - mMesh.boundingBox.pmin.x) * 0.5f,
                            (mMesh.boundingBox.pmax.y - mMesh.boundingBox.pmin.y) * 0.5f,
                            (mMesh.boundingBox.pmax.z - mMesh.boundingBox.pmin.z) * 0.5f);
                if (vertexDeclaration & oiram::Ves_Position)
                {
                    oiram::vec4 norm((vertex.vec3Position.x - posCenter.x) / posExtent.x,
                                     (vertex.vec3Position.y - posCenter.y) / posExtent.y,
                                     (vertex.vec3Position.z - posCenter.z) / posExtent.z,
                                     1.0f);
                    vertex.short4Position = oiram::short4(norm);
                }
    inline short packF32ToS16(float f)
    {
        return static_cast<short>(f * 32767.5f);;
    }

    struct short4
    {
        short s[4];

        short4() {}
        short4(const oiram::vec4& v)
        {
            s[0] = packF32ToS16(v.x);
            s[1] = packF32ToS16(v.y);
            s[2] = packF32ToS16(v.z);
            s[3] = packF32ToS16(v.w);
        }
    };

這樣,position的xyz就從float壓縮到short中了。接下來,要在vs中進行解壓,那麼一樣須要傳入center和extent:blog

float4 position = float4(iPosition.xyz / 32767.5 * positionExtent + positionCenter, 1);

固然,這裏能夠直接將positionExtent除以32767.5以後再傳入,減小一次沒必要要的除法操做,這屬於自行研發優化的範疇以內,不累述。ip

接下來,同理能夠將uv也進行壓縮,由於uv是2個值的緣故,因此center和extent能夠合併在一塊兒,只佔用一個float4便可,同上理不累述。

float4 texcoord0 = float4(iTexCoord0.xyzw * uvExtentCenter.xyxy + uvExtentCenter.zwzw);

 

須要注意的是,由於必須依賴vs進行解壓,並且center和extent的值必須正確。有意思的是,若是美術曾經將一個展分過uv的大模型,摘取其中某一塊,而後merge到一個新的模型中,那麼就容易出現混亂的uv值,好比u = 1.234567e+28, v = 1.234567e-44#DEN之類的。若是打開3dsmax裏的UV map觀察,整個face的3個vertex都在uv上的同一個"點"上。無奈的是,獲取這些數據的函數都正確返回了,並且uv數值也是正常的float,沒法經過isNAN神碼的來判斷是否有效。惟一能想到的辦法就是,檢查uv的絕對值,若是小於0.00001,或者大於10000,就將其重置爲0。

還有一個在頂點壓縮以前不容易察覺的狀況,由於某種緣由,部分faces的material爲空,即沒有附上材質。這一些頂點以前可能安全地藏在模型的體內,肉眼沒法察覺。但如今通過壓縮以後,若是vs沒有照顧到它們,將其正確得解壓的話,由於是以short形式記錄的,因而你會發現場景中出現一些奇異的巨形的模型,附着奇怪的貼圖。

既然選擇了programmable pipeline代替Fixed Function,那麼意味着,你的shader在解壓數據之餘,渲染效果必須保持與以前FF的一致。能夠想象的是,這並非一件簡單的事情,好比一個模型中,一部分submesh是用diffuse map渲染,另外一部分submesh只用到了diffuse color。那麼好吧,你的shader可要準備好了才行。

 

結論:一篇paper,一個算法,一種優化,每每看上去很簡單很美好。當你往具體項目中加入時,一般不會像demo中執行得那麼順利,尤爲是趕上大量的數據,甚至是各類奇葩數據,從而出現各類詭異現象的時候,那時候估計你就會跟我同樣,很難笑得出來了。

相關文章
相關標籤/搜索