# How can I create an outline shader for a plane?

All these toon shaders I have been seeing render an duplicated mesh behind it and extrude the normals of the vectors to make it look like it has a border, but it feels a bit hacky to me.

Basically I am trying to create an outline shader for a plane so these toon shaders do not really work.

It's probably due to my lack of deep knowledge about shaders in particular but I am really confused and would like someone to explain. I'm also a bit worried that rendering an object twice would hit the performance.

Edit: Added picture for reference. (Thanks for the edit Alexandre!)

• When you say "outline shader for a plane," do you mean you just need to draw a stroked rectangle? If so, an outlining shader might not be the simplest route to this effect. Try showing us a mock-up of how this should look in your game, and we can suggest methods to achieve it. Mar 27, 2018 at 16:58
• do you need a simple fragment shader like this. Mar 27, 2018 at 20:36
• @DMGregory: Added a mock-up for reference. Thanks! Mar 28, 2018 at 7:59

If all we need is an outline around a rectangle, we can compute this analytically with the math of signed distance fields. :)

v2f vert (appdata v)
{
v2f o;
// Slightly enlarge our quad, so we have a margin around it to draw the outline.
float expand = 1.1f;
v.vertex.xyz *= expand;
o.vertex = UnityObjectToClipPos(v.vertex);
// If we want to get fancy, we could compute the expansion
// dynamically based on line thickness & view angle, but I'm lazy)

// Expand the texture coordinate space by the same margin, symmetrically.
o.uv = (v.uv - 0.5f) * expand + 0.5f;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
// Texcoord distance from the center of the quad.
float2 fromCenter = abs(i.uv - 0.5f);
// Signed distance from the horizontal & vertical edges.
float2 fromEdge = fromCenter - 0.5f;

// Use screenspace derivatives to convert to pixel distances.
fromEdge.x /= length(float2(ddx(i.uv.x), ddy(i.uv.x)));
fromEdge.y /= length(float2(ddx(i.uv.y), ddy(i.uv.y)));

// Compute a nicely rounded distance from the edge.
float distance = abs(min(max(fromEdge.x,fromEdge.y), 0.0f) + length(max(fromEdge, 0.0f)));

// Sample our texture for the interior.
fixed4 col = tex2D(_MainTex, i.uv);
// Clip out the part of the texture outside our original 0...1 UV space.
col.a *= step(max(fromCenter.x, fromCenter.y), 0.5f);

// Blend in our outline within a controllable thickness of the edge.
col = lerp(col, _Color, saturate(_Thickness - distance));

return col;
}


This gives us an outline of constant pixel thickness with nicely rounded corners and antialiased edges (the green box above - the orange is Unity's selection outline, showing how we've expanded the quad relative to its otherwise identical twin below)

• Awesome, thank you very much. This helps me a lot! If I'd want to compute the expansion, what would be needed? You don't have to program it, just pointing me to some resources would be an immensive help! :) Mar 28, 2018 at 13:27
• Honestly, I'm not sure how I'd approach that without diving in to actually work on it. There are a few funny cases to handle, like if your camera is allowed to get very close to the plane of the quad — then the outline needs to protrude out of plane. You might need a little skirt of geometry to do this well. Mar 28, 2018 at 14:06