Unity移動端、WebGL 四邊形線框Shader 實現

本文不是使用GeometryShader!

移動端和WebGL由於一些限制不能使用GeometryShader,因此不好實現線框Shader。

一、手動繪製

有的人可能會選擇用GL畫線來實現繪製線框,具體就是讀取mesh信息然後繪製線條,但是GL繪製不能調整線條粗細。有的人可能有回使用LineRender或者直接用圓柱體。這樣確實可以調整粗細,但是性能堪憂。。。

手動繪製,基本大一點的Mesh就會嚴重影響性能,不是很可取。

二、通過Mesh傳參數

線框用shader實現的難度,就是很難找到三角的邊

我的靈感來自Github上的一個工程, UnityWireframeRenderer ,感興趣的可以去看看,感覺真的很厲害。

主要通過修改Mesh的UV,然後通過uv插值,計算出點到邊的距離。

因爲要創建新的Mesh,所以其實還是有內存消耗的,但是除了內存問題,運行性能完美。我的做法是在這個原理上進行了修改。

Normal傳參

原文通過UV傳遞信息有一個問題,就是需要自己計算第三條邊的距離,因爲uv變化只能保存兩條邊的距離。然而normal具有三個值,可以完美保存三條邊的插值距離,就不用在shader中額外計算了。

還有一個優點,可以通過normal的值,實現繪製4邊形,而不是繪製三角面的線。因爲Mesh提前計算的,所以可以提前計算出同義平面上的相交邊,然後修改Normal值,然後在Shader中插值時,就不會顯示相交邊了。

 

建議Github上搜索原工程看看,上面有註釋,很容易理解。

fixed4 _LineColor;
float _LineSize;
float4 _EmissionColor;
float _Intencity;


struct appdata
{
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float2 uv : TEXCOORD0;
};

struct v2f
{
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    float3 normal : TEXCOORD1;
};

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    //If we are rendering in shaded mode (showing the original mesh renderer)
    //we want to ensure that the wireframe-processed mesh appears "on top" of
    //the original mesh. We achieve this by slightly decreasing the z component
    //(making the vertex closer to the camera) without actually changing its screen space position
    //since the w component remains the same, and thus, after w division, the x and y components
    //won't be affected by our "trick".
    //So, in essence, this just changes the value that gets written to the Z-Buffer
    o.vertex.z -= 0.001;
    o.uv = v.uv;
    o.normal = v.normal;
    return o;
}


fixed4 frag (v2f i) : SV_Target
{
    float lineWidthInPixels = _LineSize;
    float lineAntiaAliasWidthInPixels = 1;


    float2 normalxVector = float2(ddx(i.normal.x),ddy(i.normal.x)); 
    float2 normalyVector = float2(ddx(i.normal.y),ddy(i.normal.y)); 
    float2 normalzVector = float2(ddx(i.normal.z),ddy(i.normal.z)); 

    float normalxLength = length(normalxVector); 
    float normalyLength = length(normalyVector);
    float normalzLength = length(normalzVector); 


    float maximumXDistance = lineWidthInPixels * normalxLength;
    float maximumYDistance = lineWidthInPixels * normalyLength;
    float maximumZDistance = lineWidthInPixels * normalzLength;

    float minimumXDistance = i.normal.x;
    float minimumYDistance = i.normal.y;
    float minimumZDistance = i.normal.z;



    float normalizedXDistance = minimumXDistance / maximumXDistance;
    float normalizedYDistance = minimumYDistance / maximumYDistance;
    float normalizedZDistance = minimumZDistance / maximumZDistance;



    float closestNormalizedDistance = min(normalizedXDistance,normalizedYDistance);
    closestNormalizedDistance = min(closestNormalizedDistance,normalizedZDistance);


    float lineAlpha = 1.0 - smoothstep(1.0,1.0 + (lineAntiaAliasWidthInPixels/lineWidthInPixels),closestNormalizedDistance);

    lineAlpha *= _LineColor.a;

    
    return fixed4(_LineColor.rgb + _EmissionColor.rgb * _Intencity,lineAlpha);

}

這是Shader 的主要部分,我的修改也是基於原工程,可以對比着看。

點贊評論,不要私信,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章