本文不是使用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 的主要部分,我的修改也是基於原工程,可以對比着看。
點贊評論,不要私信,謝謝!