- Home /

**Question**by Nyhtian · Apr 07, 2020 at 07:09 AM · shadershadersshader programmingshader writingwireframe

# Wireframe shader with constant width and no diagonals

Hello, I'm very new to shader code so forgive me for my ignorance.

I'm wanting to create a wireframe shader that has lines of constant width (in world or screenspace), as well as no diagonal lines, such that it generates quads instead of tris.

I tried to start basing my shader off of t$$anonymous$$s unlit wireframe shader, w$$anonymous$$ch is a solid wireframe shader, w$$anonymous$$ch I read up on.

Here's the code:

```
Shader "SuperSystems/Wireframe-Shaded-Unlit"
{
Properties
{
_MainTex ("MainTex", 2D) = "w$$anonymous$$te" {}
_WireT$$anonymous$$ckness ("Wire T$$anonymous$$ckness", RANGE(0, 800)) = 100
_WireSmoothness ("Wire Smoothness", RANGE(0, 20)) = 3
_WireColor ("Wire Color", Color) = (0.0, 1.0, 0.0, 1.0)
_BaseColor ("Base Color", Color) = (0.0, 0.0, 0.0, 1.0)
}
SubShader
{
Tags {
"RenderType"="Opaque"
}
Pass
{
// Wireframe shader based on the the following
// http://developer.download.nvidia.com/SDK/10/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform float _WireT$$anonymous$$ckness;
uniform float _WireSmoothness;
uniform float4 _WireColor;
uniform float4 _BaseColor;
struct appdata
{
float4 vertex : POSITION;
float2 texcoord0 : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2g
{
float4 projectionSpaceVertex : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 worldSpacePosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct g2f
{
float4 projectionSpaceVertex : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 worldSpacePosition : TEXCOORD1;
float4 dist : TEXCOORD2;
UNITY_VERTEX_OUTPUT_STEREO
};
v2g vert (appdata v)
{
v2g o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.projectionSpaceVertex = UnityObjectToClipPos(v.vertex);
o.worldSpacePosition = mul(unity_ObjectToWorld, v.vertex);
o.uv0 = TRANSFORM_TEX(v.texcoord0, _MainTex);
return o;
}
[maxvertexcount(3)]
void geom(triangle v2g i[3], inout TriangleStream<g2f> triangleStream)
{
float2 p0 = i[0].projectionSpaceVertex.xy / i[0].projectionSpaceVertex.w;
float2 p1 = i[1].projectionSpaceVertex.xy / i[1].projectionSpaceVertex.w;
float2 p2 = i[2].projectionSpaceVertex.xy / i[2].projectionSpaceVertex.w;
float2 edge0 = p2 - p1;
float2 edge1 = p2 - p0;
float2 edge2 = p1 - p0;
// To find the distance to the opposite edge, we take the
// formula for finding the area of a triangle Area = Base/2 * Height,
// and solve for the Height = (Area * 2)/Base.
// We can get the area of a triangle by taking its cross product
// divided by 2. However we can avoid dividing our area/base by 2
// since our cross product will already be double our area.
float area = abs(edge1.x * edge2.y - edge1.y * edge2.x);
float wireT$$anonymous$$ckness = 800 - _WireT$$anonymous$$ckness;
g2f o;
o.uv0 = i[0].uv0;
o.worldSpacePosition = i[0].worldSpacePosition;
o.projectionSpaceVertex = i[0].projectionSpaceVertex;
o.dist.xyz = float3( (area / length(edge0)), 0.0, 0.0) * o.projectionSpaceVertex.w * wireT$$anonymous$$ckness;
o.dist.w = 1.0 / o.projectionSpaceVertex.w;
UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(i[0], o);
triangleStream.Append(o);
o.uv0 = i[1].uv0;
o.worldSpacePosition = i[1].worldSpacePosition;
o.projectionSpaceVertex = i[1].projectionSpaceVertex;
o.dist.xyz = float3(0.0, (area / length(edge1)), 0.0) * o.projectionSpaceVertex.w * wireT$$anonymous$$ckness;
o.dist.w = 1.0 / o.projectionSpaceVertex.w;
UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(i[1], o);
triangleStream.Append(o);
o.uv0 = i[2].uv0;
o.worldSpacePosition = i[2].worldSpacePosition;
o.projectionSpaceVertex = i[2].projectionSpaceVertex;
o.dist.xyz = float3(0.0, 0.0, (area / length(edge2))) * o.projectionSpaceVertex.w * wireT$$anonymous$$ckness;
o.dist.w = 1.0 / o.projectionSpaceVertex.w;
UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(i[2], o);
triangleStream.Append(o);
}
fixed4 frag (g2f i) : SV_Target
{
float minDistanceToEdge = min(i.dist[0], min(i.dist[1], i.dist[2])) * i.dist[3];
float4 baseColor = _BaseColor * tex2D(_MainTex, i.uv0);
// Early out if we know we are not on a line segment.
if(minDistanceToEdge > 0.9)
{
return fixed4(baseColor.rgb,0);
}
// Smooth our line out
float t = exp2(_WireSmoothness * -1.0 * minDistanceToEdge * minDistanceToEdge);
fixed4 finalColor = lerp(baseColor, _WireColor, t);
finalColor.a = t;
return finalColor;
}
ENDCG
}
}
}
```

I figured to get a constant width, I should multiply the minDistanceToEdge by the full length (in world space) of the barycentric axis that the minDistanceToEdge is derived from, to sort of 'un-normalise' it (if that makes any sense whatsoever). I'm not sure if that is correct, and *if* it is, then how to implement that.

In regard to removing the diagonals (apologies for the double question, I just didn't want to ask two seperate questions and then inevitably fail to consolidate the two scripts), I have seen solutions that do not display an edge based on its length (I am dealing with right-angles, so the hypotenuse ((w$$anonymous$$ch I do not want to display)) will always be the longest), however, in the nVidia paper, they describe not displaying an edge based on its index in the primitive. I am using t$$anonymous$$s shader for a custom mesh that I define in a script, so I could also use t$$anonymous$$s solution, however, again, I'm not too sure how to implement either of these options.

If anyone could help me out as a newbie in t$$anonymous$$s field, that would be greatly appreciated!

**Answer** by Bunny83
·
Apr 09, 2020 at 11:20 AM

Well, I recommend to have a look at t$$anonymous$$s tutorial of Catlike coding. About removing the diagonal since we use barycentric coordinates we can simply ignore one of them. For example the implicit calculated "z". Now all we have to do is ensure the right "order" when we create our new triangle in the geometry shader. So make sure you calculate the barycentric coordinates the right way. The two vertices that are at each end of the longest line has to get the "1". The vertex opposite to the hypotenuse gets the (0, 0) coordinate.

When calculating the minBary value we just ignore z. However instead of t$$anonymous$$s:

```
float minBary = min(barys.x, min(barys.y, barys.z));
```

we do t$$anonymous$$s:

```
float minBary = min(barys.x, barys.y);
```

That should get rid of the longest line (if you ordered your vertices correctly in the geometry shader).

### Your answer

### Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.