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 的主要部分,我的修改也是基于原工程,可以对比着看。

点赞评论,不要私信,谢谢!

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