Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Issues creating fixed-width lines using Geometry shader in view space

Discussion in 'Shaders' started by Iron-Warrior, Nov 21, 2016.

  1. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Hi, I'm attempting to create lines of a fixed width. I am creating a mesh using the Lines topology, and then using a geometry shader to create a quad from each line. My goal is to create lines that are of a constant width, ignoring perspective. Here's what I've got so far:

    Code (csharp):
    1.  
    2. Shader "Unlit/ThickLine"
    3. {
    4.     Properties
    5.     {
    6.         _Color("Main Color", Color) = (1,1,1,1)
    7.     }
    8.     SubShader
    9.     {
    10.         Pass
    11.         {
    12.             CGPROGRAM
    13.             #pragma target 5.0
    14.             #pragma vertex vert
    15.             #pragma fragment frag
    16.             #pragma geometry geo
    17.            
    18.             #include "UnityCG.cginc"
    19.  
    20.             struct v2g
    21.             {
    22.                 float4 vertex : SV_POSITION;
    23.             };
    24.  
    25.             struct g2f
    26.             {
    27.                 float4 pos: POSITION;
    28.             };
    29.  
    30.             fixed4 _Color;
    31.            
    32.             v2g vert (float4 position : POSITION)
    33.             {
    34.                 v2g o;
    35.                 o.vertex = position;
    36.                 return o;
    37.             }
    38.  
    39.             [maxvertexcount(4)]
    40.             void geo(line v2g v[2], inout TriangleStream<g2f> ts)
    41.             {
    42.                 float weight = 1;
    43.  
    44.                 float4 p1 = mul(UNITY_MATRIX_MVP, v[0].vertex);
    45.                 float4 p2 = mul(UNITY_MATRIX_MVP, v[1].vertex);
    46.  
    47.                 float4 dir = normalize(p2 - p1);
    48.                 float4 perp = float4(dir.y, -dir.x, 0, 0);
    49.  
    50.                 float4 v1_top = p1 + perp * weight;
    51.                 float4 v1_bot = p1 - perp * weight;
    52.  
    53.                 float4 v2_top = p2 + perp * weight;
    54.                 float4 v2_bot = p2 - perp * weight;
    55.  
    56.                 g2f o1;
    57.                 o1.pos = v1_top;
    58.                 ts.Append(o1);
    59.  
    60.                 g2f o2;
    61.                 o2.pos = v1_bot;
    62.                 ts.Append(o2);
    63.  
    64.                 g2f o3;
    65.                 o3.pos = v2_top;
    66.                 ts.Append(o3);
    67.  
    68.                 g2f o4;
    69.                 o4.pos = v2_bot;
    70.                 ts.Append(o4);
    71.             }
    72.            
    73.             fixed4 frag (g2f i) : SV_Target
    74.             {
    75.                 return _Color;
    76.             }
    77.             ENDCG
    78.         }
    79.     }
    80. }
    81.  
    Here's what I think I'm doing: I'm converting both the line points into view space. From there I get the direction from one point to another, then the direction perpendicular to this. Now I can build the four points of the quad to represent this line. Since I'm doing the offsets in view space, this should create a line of constant width. Here's what I'm getting:



    (With the camera right beside the starting point).

    I'm not sure what I'm doing wrong, but it looks like the offsets are being applied in world space...but I'm not sure how since I clearly transform the vertices beforehand.

    Thanks for any help,
    Erik
     
  2. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    So it turns out I was a bit off on how view space works—I was assuming perspective distortion had already been applied and I could operate on the vertices as if they were already on a 2D plane (which doesn't seem to be the case).

    My current resource is this article about drawing lines in OpenGL. I'm trying to implement the Screen-Space Projected Lines section (except as a geometry shader), which should be pretty easy, but in some simple tests I've been doing it looks like the w coordinate is always 1, making it difficult to correct for the perspective divide...based on what I've read here in the OpenGL manual the w coordinate should be the perspective divide at the end of the vertex shader, but this doesn't seem to be the case. Does anyone have any ideas?
     
    andrew-lukasik likes this.
  3. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    836
    Was able to figure it out! Trick was to multiply the offset by each individual vertex's w coordinate. Here's the geometry shader, pretty simple:

    Code (csharp):
    1.  
    2.             [maxvertexcount(4)]
    3.             void geo(line v2g v[2], inout TriangleStream<g2f> ts)
    4.             {
    5.                 g2f g[4];
    6.  
    7.                 float4 p1 = mul(UNITY_MATRIX_MVP, v[0].vertex);
    8.                 float4 p2 = mul(UNITY_MATRIX_MVP, v[1].vertex);
    9.  
    10.                 float2 dir = normalize(p2.xy - p1.xy);
    11.                 float2 normal = float2(-dir.y, dir.x);
    12.  
    13.                 float4 offset1 = float4(normal * p1.w * _Thickness / 2.0f, 0, 0);
    14.                 float4 offset2 = float4(normal * p2.w * _Thickness / 2.0f, 0, 0);
    15.  
    16.                 float4 o1 = p1 + offset1;
    17.                 float4 o2 = p1 - offset1;
    18.                 float4 o3 = p2 + offset2;
    19.                 float4 o4 = p2 - offset2;
    20.  
    21.                 g[0].pos = o1;
    22.                 g[1].pos = o2;
    23.                 g[2].pos = o3;
    24.                 g[3].pos = o4;
    25.  
    26.                 ts.Append(g[0]);
    27.                 ts.Append(g[1]);
    28.                 ts.Append(g[2]);
    29.                 ts.Append(g[3]);
    30.             }
     
    fuser and andrew-lukasik like this.
  4. darger

    darger

    Joined:
    Aug 30, 2012
    Posts:
    17
    Hello.
    I was able to do what I wanted to do with your shader! Thank you very much.

    However, I noticed that some line displays became thin and disappeared depending on the camera viewpoint.
    Fixing the 10th line of the shader this way, it worked for me.

    Code (CSharp):
    1. float2 dir = normalize(p2.xy /p2.w - p1.xy /p1.w);
     
    andrew-lukasik likes this.
  5. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    239
    Vertical lines are wider because screen aspect ratio is stretching clip space as well. Fix:
    offset.x *= _ScreenParams.y/_ScreenParams.x;