0
\$\begingroup\$

I am trying to write a shader that highlight the edge between each pair of vertices, much like the UCLA Wireframe Shader.

enter image description here

All implementations I came across require geometry shader support. I am wondering if this is possible with just vertex and fragment shaders?

(Of course, I can use texture to workaround it; but I am trying to avoid create custom textures for each model that I need this effect. On the other hand, I could just use geometry shader but it presents quite a problem when porting to iOS, which lacks support.)

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Yes, there are several great wireframe shaders that you can use, for free:

https://forum.unity.com/threads/is-wireframe-effect-possible-without-geometry-shader.442625/#post-9546460

Here is the best one from it (by Unity), in my opinion. It's called SpatialMappingWireframe and I modified it to include texture and to have constant color. I also made it work with single (non-stereo camera)

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
// from https://github.com/TwoTailsGames/Unity-Built-in-Shaders/blob/master/DefaultResourcesExtra/VR/Shaders/SpatialMappingWireframe.shader
// This is adjusted version, allowing to tweak color of Wireframe, make it barely visible (alpha of color).
// Allows for a texture.
Shader "Custom/VRWireframe (Unlit Texture)"
{
    Properties
    {
        _WireThickness ("Wire Thickness", RANGE(0, 800)) = 100
        _MainTex("MainTexture", 2D) = "white"{}
        _WireColor("Wireframe Color", Color) = (1,1,1,1)
    }
 
    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"
 
            float _WireThickness;
            sampler2D _MainTex;
            fixed4 _WireColor;
 
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
 
            struct v2g
            {
                float4 projectionSpaceVertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 worldSpacePosition : TEXCOORD1;
            };
 
            struct g2f
            {
                float4 projectionSpaceVertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 worldSpacePosition : TEXCOORD1;
                float4 dist : TEXCOORD2;
            };
 
            v2g vert (appdata v)
            {
                v2g o;
                o.projectionSpaceVertex = UnityObjectToClipPos(v.vertex);
                o.worldSpacePosition = mul(unity_ObjectToWorld, v.vertex);
                o.uv = v.uv;
                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 wireThickness = 800 - _WireThickness;
 
                g2f o;
                o.worldSpacePosition = i[0].worldSpacePosition;
                o.projectionSpaceVertex = i[0].projectionSpaceVertex;
                o.dist.xyz = float3( (area / length(edge0)), 0.0, 0.0) * o.projectionSpaceVertex.w * wireThickness;
                o.dist.w = 1.0 / o.projectionSpaceVertex.w;
                o.uv = i[0].uv;
                triangleStream.Append(o);
 
                o.worldSpacePosition = i[1].worldSpacePosition;
                o.projectionSpaceVertex = i[1].projectionSpaceVertex;
                o.dist.xyz = float3(0.0, (area / length(edge1)), 0.0) * o.projectionSpaceVertex.w * wireThickness;
                o.dist.w = 1.0 / o.projectionSpaceVertex.w;
                o.uv = i[1].uv;
                triangleStream.Append(o);
 
                o.worldSpacePosition = i[2].worldSpacePosition;
                o.projectionSpaceVertex = i[2].projectionSpaceVertex;
                o.dist.xyz = float3(0.0, 0.0, (area / length(edge2))) * o.projectionSpaceVertex.w * wireThickness;
                o.dist.w = 1.0 / o.projectionSpaceVertex.w;
                o.uv = i[2].uv;
                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];
 
                // Early out if we know we are not on a line segment.
                float4 texCol = tex2D(_MainTex, i.uv);
 
                // Smooth our line out
                float t = exp2(-2 * minDistanceToEdge * minDistanceToEdge);
                      t *= _WireColor.a;
 
                fixed4 finalColor = lerp(texCol, _WireColor, t);
                finalColor.a = 1;
 
                return finalColor;
            }
            ENDCG
        }
    }
}
\$\endgroup\$
0
1
\$\begingroup\$

Have you considered https://docs.unity3d.com/ScriptReference/GL-wireframe.html ?

Alternatively, you could generate the lines by yourself and add them to a mesh with a line topology.

\$\endgroup\$
2
  • \$\begingroup\$ This is currently a link-only answer. If the Unity docs links ever rot or become inaccessible from certain locations, future readers won't get any insight from this answer. Please consider editing your answer to include at least the high-level steps of a solution, so that the answer remains useful even stand-alone. \$\endgroup\$
    – DMGregory
    Dec 25, 2023 at 23:17
  • \$\begingroup\$ From the linked docs, Note that some platforms, for example mobile (OpenGL ES) does not support wireframe rendering. \$\endgroup\$
    – Basic
    Dec 26, 2023 at 11:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .