Search Unity

A surface shader to render a line to represent a flat plane on its side

Discussion in 'Shaders' started by reblGreen, Nov 14, 2017.

  1. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    Hey guys,

    The description makes no sense at all so I'll try and explain better...

    I've modified a surface shader to get the rendering effect I want for a flat plane, but when the plane rotates onto its side in front of the camera it becomes invisible. Obviously this is because the flat plane has 2 dimensions in a 3D space and it has no depth (thickness).

    What I'm trying to accomplish is when flat plane would become perfectly horizontal (invisible) to the viewport, it is replaced by a line which represents it making it appear that it is still visible in the camera view giving the illusion that it has a thickness.

    I don't really want to add a line renderer for each plane and swap them out via script if it's possible to do it inside the shader.

    I'm using

    fixed facing : VFACE;

    in my input and

    if (IN.facing > 0.0f) { ... }

    in my surf which seems to tell me which side of the plane is visible to the camera, but this doesn't seem to help when the plane is on its edge and neither side is visible to the camera. I'm also unsure if it's even possible to draw a line from within the surface shader as I'm pretty new to shaders :)

    Any ideas anyone?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    As you said, VFACE is just telling you what side of a surface is visible. It will always either be 1.0 or -1.0 for front or back facing, it will never be 0.0, and it does not know how close to edge on it is.

    Also once you're getting to the surf function it's too late to do anything about what pixels are being drawn to be able to expand an infinitely thin quad into a line.

    Basically there are two main parts to a shader, the vertex shader and the fragment shader. The fragment shader controls what a pixel looks like, and the vertex shader controls what pixels will be rendered. A Surface shader is really a vertex and fragment shader generator, with the surf function being called within the fragment shader that's doing most of the work. Surface shaders do have a vertex function you can modify, but this too is just another function that gets called within the larger vertex shader which you can use to modify the vertex data before it gets transformed into its screen space position. However you only know about a single vertex at a time so you don't really have enough information to know if the plane is going to be too thin to see or not, though you could conceivably make a guess from the surface normal.

    What you would need to do this in a shader would be to use a geometry shader, another shader step which can be inserted between the vertex and fragment shaders, which Surface shaders do not have support for.

    Or you can just use a thin box mesh instead of a quad and it'll be way faster and easier.
     
  3. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    Thanks for the reply bgolus. I can't really use a box mesh as the texture has transparent rounded corners (playing cards) and it's for a mobile VR project so I could do with keeping vertices to a minimum.

    Basically the shader is a modified version of "Legacy Shaders/Transparent/Cutout/VertexLit" which adds a second texture and double-sided.

    I wouldn't know where to start with converting it to vert/frag and get the shadows/lighting correct. I've had a play with modifying few shaders but I'm a full blown newb.

    Thanks for your suggestions, I'll keep investigating and hopefully find a solution :)
     
  4. nat42

    nat42

    Joined:
    Jun 10, 2017
    Posts:
    353
    What's your polygon budget that edge geometry for the cards is a concern for you?

    Seems like you might be spending bigger than you need on your fragment shader (sampling an extra texture for the unseen side of the card, and using transparency so the card has to be outside of the tile based opaque rendering)


    I think it may be somewhat possible in a vertex shader... just shift some corners a little one direction and the others the other way based on the angle (a subtle billboard effect)
     
    Last edited: Nov 16, 2017
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    FYI, "cutout" or alpha test is quite expensive on mobile platforms. Even with rounded corners a mesh would likely end up being cheaper.
     
  6. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    Thanks for the input guys :)

    I think you're right, I'll just model the cards and use LOD group I think. I was just hoping for a quick and dirty shader solution for a demo I'm creating.

    It's one of those mental challenges i might itterate over again if I ever become a shader guru :)
     
  7. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    Sorry for the unfinished post, was getting 503's from unity forum. Continued...

    I was basing this assumption on the quad being flat but thinking about it further, I guess both sides are sampled indefinitely in the event that the quad is curved??
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    A if in a shader, especially on mobile, doesn't usually do what you think it does.

    Example:
    Code (CSharp):
    1. fixed4 col = 0;
    2. if (facing > 0)
    3.     col = tex2D(_TexA, uv);
    4. else
    5.     col = tex2D(_TexB, uv);
    It would not be unexpected for you to assume in that example only one tex2D call ever happens. Certainly, in C# or really most programming language that would be an accurate assumption. For shaders this is not true, especially for the case of tex2D, and additionally so for mobile. The why is complicated, but most of the time you should assume all code in a shader gets run all of the time.

    That above example code gets compiled into something like this:
    Code (CSharp):
    1. fixed4 colA = tex2D(_TexA, uv);
    2. fixed4 colB = tex2D(_TexB, uv);
    3. fixed4 col = facing > 0 ? colA : colB;
    All of the code in both conditions gets calculated, then one side is used and the other thrown away.

    If you want to know why, look up dynamic branching and flow control. After that look up derivatives, pixel quads, and tex2D vs tex2Dgrad. I've written about most of these elsewhere in more detail.
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    There is no quad. No curve. GPUs draw each individual triangle, one after the other, and are by definition perfectly flat. A quad mesh is always just two (or more) triangles. The VFACE value is guaranteed to be constant across the entire face of a triangle.