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