Search Unity

Interested in writing my own wireframe shader

Discussion in 'Shaders' started by miloshcavitch, Apr 3, 2017.

  1. miloshcavitch

    miloshcavitch

    Joined:
    Jan 31, 2017
    Posts:
    13
    I am interested in writing my own or modifying an existing wireframe shader. The difference would be that I could choose which edges of each triangle would have lines on them. I've been reading the scripts for a couple i got from the asset store and it seems like this math happens within the geom function that takes a trianglestream inout input. I know this is a broad question but I would like to know how i could omit some of the edges from this calculation. In general I would like to learn how to make unlit shaders similar to this so all information is welcome!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    All wireframe shaders use barycentric coordinates, some calculate them in the geometry shader, and some by storing data in the vertices (by using a uv set or vertex colors).

    The basic way to store barycentrics with something like vertex colors is to color each corner of a triangle a single primary color, so vertex 0 is red (1,0,0), vertex 1 is green (0,1,0), and vertex 2 is blue (0,0,1). Then the interpolated color can be used to determine how far from each edge of the triange you are. If any value is 0 it is on the edge of the triangle!

    However a nice property of barycentrics coordinates is you only really need two "colors", as any point on the surface of the triangle the sum of the interpolated elements is 1. In the vertex color example 0.33, 0.33, 0.33 is the exact center of the triangle, so you only need two values to be interpolated and you can reconstruct the third by doing 1 - a + b = c, which is what GPUs actually do internally.

    Now, none of this helps you a ton for what you're looking to do, but the short version would be you would have to mask out the edges you want to avoid, or manually only add interpolation data for the edges you want. The simpliest way I can think of would be use the 3 vertex colors / uv values baked into the mesh (you can store Vector3 or Vector4 values in mesh UVs by using SetUV() function instead of the .uv values) and not using geometry shaders at all. Since you're going to have to store per-vertex data to accomplish what you want, there's not really a point in using the geometry shader anyway.

    So, if the average triangle with a line on all 3 sides is (1,0,0), (0,1,0), (0,0,1) you could mask out the line on one edge by using (1,0,0), (1,1,0), (1,0,1), which means the red channel is never zero, and thus never an edge. If you're using derivatives in the fragment shader to do screen space line widths you would also have to branch on if the fwidth() for a channel is 0.0 and omit it.
     
    frosted likes this.
  3. miloshcavitch

    miloshcavitch

    Joined:
    Jan 31, 2017
    Posts:
    13
    wow thats actually pretty clever. 2 questions, both involving line width:
    1. so if you take into consideration a line width, instead of using == zero you would use < linewidth, where line width is a trigonometry calculation using the 3 color vectors?
    2. if you wanted the line width to be based on screen pixels as opposed to world pixels (when you move away from object the line width appears to stay the same) you'd have to take in to account the distance from the camera or is there a simpler way to do this?
    Thanks again, the above was very helpful
     
  4. miloshcavitch

    miloshcavitch

    Joined:
    Jan 31, 2017
    Posts:
    13
    * screen pixels vs world pixels is a terrible way to phrase it but you get the idea ;)
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Easier than that, min(a, min(b, c)) < line width == line

    That's the fwidth I mentioned.
    http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/

    In short fwidth() gives how how much a value changes between two pixels.
    barycentricCoords * fwidth(barycentricCoords) = pixel distance from each edge
     
  6. miloshcavitch

    miloshcavitch

    Joined:
    Jan 31, 2017
    Posts:
    13
    bgolus, i have no idea how to pronounce your name but you're real clutch!
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    It's not a pseudonym or anything, just first initial + last name. :p