Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question Quad wireframe shader maintained after vertex editing

Discussion in 'Shaders' started by AlmostMikhail, May 13, 2024.

  1. AlmostMikhail

    AlmostMikhail

    Joined:
    Apr 5, 2022
    Posts:
    4
    Hi, I've been trying to get a shader online that maintains a quad wireframe even if vertices are moved. I'm creating a low poly 3d modeling tool for mobile but I've been stuck trying to get quad wireframe but all of them don't maintain quad when vertices are moved. They somehow revert to triangles when vertices are moved too far from each other.

    I don't want to dive deep into shaders but if I can't get an answer here I feel like I might have to because I can figure out other stuff with this app like edge loops, Vertex editing and extruding but wireframes requires knowledge of shaders and I don't want to have to learn all of that.

    Here's the script I'm working with
    Code (CSharp):
    1. Shader "Custom/Geometry/Wireframe"
    2. {
    3.     Properties
    4.     {
    5.         [PowerSlider(3.0)]
    6.         _WireframeVal ("Wireframe width", Range(0., 0.5)) = 0.05
    7.         _FrontColor ("Front color", color) = (1., 1., 1., 1.)
    8.         _BackColor ("Back color", color) = (1., 1., 1., 1.)
    9.         [Toggle] _RemoveDiag("Remove diagonals?", Float) = 0.
    10.     }
    11.     SubShader
    12.     {
    13.         Tags { "Queue"="Geometry" "RenderType"="Opaque" }
    14.        
    15.         Pass
    16.         {
    17.             Cull Front
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #pragma geometry geom
    22.  
    23.             // Change "shader_feature" with "pragma_compile" if you want set this keyword from c# code
    24.             #pragma shader_feature __ _REMOVEDIAG_ON
    25.  
    26.             #include "UnityCG.cginc"
    27.  
    28.             struct v2g {
    29.                 float4 worldPos : SV_POSITION;
    30.             };
    31.  
    32.             struct g2f {
    33.                 float4 pos : SV_POSITION;
    34.                 float3 bary : TEXCOORD0;
    35.             };
    36.  
    37.             v2g vert(appdata_base v) {
    38.                 v2g o;
    39.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    40.                 return o;
    41.             }
    42.  
    43.             [maxvertexcount(3)]
    44.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
    45.                 float3 param = float3(0., 0., 0.);
    46.  
    47.                 #if _REMOVEDIAG_ON
    48.                 float EdgeA = length(IN[0].worldPos - IN[1].worldPos);
    49.                 float EdgeB = length(IN[1].worldPos - IN[2].worldPos);
    50.                 float EdgeC = length(IN[2].worldPos - IN[0].worldPos);
    51.                
    52.                 if(EdgeA > EdgeB && EdgeA > EdgeC)
    53.                     param.y = 1.;
    54.                 else if (EdgeB > EdgeC && EdgeB > EdgeA)
    55.                     param.x = 1.;
    56.                 else
    57.                     param.z = 1.;
    58.                 #endif
    59.  
    60.                 g2f o;
    61.                 o.pos = mul(UNITY_MATRIX_VP, IN[0].worldPos);
    62.                 o.bary = float3(1., 0., 0.) + param;
    63.                 triStream.Append(o);
    64.                 o.pos = mul(UNITY_MATRIX_VP, IN[1].worldPos);
    65.                 o.bary = float3(0., 0., 1.) + param;
    66.                 triStream.Append(o);
    67.                 o.pos = mul(UNITY_MATRIX_VP, IN[2].worldPos);
    68.                 o.bary = float3(0., 1., 0.) + param;
    69.                 triStream.Append(o);
    70.             }
    71.  
    72.             float _WireframeVal;
    73.             fixed4 _BackColor;
    74.  
    75.             fixed4 frag(g2f i) : SV_Target {
    76.             if(!any(bool3(i.bary.x < _WireframeVal, i.bary.y < _WireframeVal, i.bary.z < _WireframeVal)))
    77.                  discard;
    78.  
    79.                 return _BackColor;
    80.             }
    81.  
    82.             ENDCG
    83.         }
    84.  
    85.         Pass
    86.         {
    87.             Cull Back
    88.             CGPROGRAM
    89.             #pragma vertex vert
    90.             #pragma fragment frag
    91.             #pragma geometry geom
    92.  
    93.             // Change "shader_feature" with "pragma_compile" if you want set this keyword from c# code
    94.             #pragma shader_feature __ _REMOVEDIAG_ON
    95.  
    96.             #include "UnityCG.cginc"
    97.  
    98.             struct v2g {
    99.                 float4 worldPos : SV_POSITION;
    100.             };
    101.  
    102.             struct g2f {
    103.                 float4 pos : SV_POSITION;
    104.                 float3 bary : TEXCOORD0;
    105.             };
    106.  
    107.             v2g vert(appdata_base v) {
    108.                 v2g o;
    109.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    110.                 return o;
    111.             }
    112.  
    113.             [maxvertexcount(3)]
    114.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
    115.                 float3 param = float3(0., 0., 0.);
    116.  
    117.                 #if _REMOVEDIAG_ON
    118.                 float EdgeA = length(IN[0].worldPos - IN[1].worldPos);
    119.                 float EdgeB = length(IN[1].worldPos - IN[2].worldPos);
    120.                 float EdgeC = length(IN[2].worldPos - IN[0].worldPos);
    121.                
    122.                 if(EdgeA > EdgeB && EdgeA > EdgeC)
    123.                     param.y = 1.;
    124.                 else if (EdgeB > EdgeC && EdgeB > EdgeA)
    125.                     param.x = 1.;
    126.                 else
    127.                     param.z = 1.;
    128.                 #endif
    129.  
    130.                 g2f o;
    131.                 o.pos = mul(UNITY_MATRIX_VP, IN[0].worldPos);
    132.                 o.bary = float3(1., 0., 0.) + param;
    133.                 triStream.Append(o);
    134.                 o.pos = mul(UNITY_MATRIX_VP, IN[1].worldPos);
    135.                 o.bary = float3(0., 0., 1.) + param;
    136.                 triStream.Append(o);
    137.                 o.pos = mul(UNITY_MATRIX_VP, IN[2].worldPos);
    138.                 o.bary = float3(0., 1., 0.) + param;
    139.                 triStream.Append(o);
    140.             }
    141.  
    142.             float _WireframeVal;
    143.             fixed4 _FrontColor;
    144.  
    145.             fixed4 frag(g2f i) : SV_Target {
    146.             if(!any(bool3(i.bary.x <= _WireframeVal, i.bary.y <= _WireframeVal, i.bary.z <= _WireframeVal)))
    147.                  discard;
    148.  
    149.                 return _FrontColor;
    150.             }
    151.  
    152.             ENDCG
    153.         }
    154.     }
    155. }
     
  2. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    2,011
    You're using a geometry shader that takes triangles and generates barycentric coordinates for them to be interpolated in a fragment shader, which then uses the barycentric coordinate to determine how far away the fragment is from the edges of the triangle and discard it if it's too far away.

    Problem is that your code tells apart the diagonal edge in each quad assuming it will be the longest edge of each triangle, which does not always hold. In particular, you'll need your quads to have close to zero shear - that is, all 4 corners having an angle close to 90º- for this to work at all, which is why it's reverting to triangles when you move your vertices far apart from each other.

    You will need to come up with a better way of doing this, which invariably requires becoming familiar with shaders. :oops:
     
    AlmostMikhail likes this.