Search Unity

Frag Shader - Drawing Continuous Lines

Discussion in 'Shaders' started by LudoLoop, Apr 18, 2017.

  1. LudoLoop

    LudoLoop

    Joined:
    Apr 18, 2017
    Posts:
    5
    Hello!

    I'm trying to draw lines in game using a fragment shader and blit operation. It's working, however I'm having a problem trying to create a continuous smooth line. Things work great if I move the drawing actor at a relatively slow speed, however above a certain threshold the line becomes dotted.

    To try and compensate for the dotted line I added 'previousPosition' and 'currentPosition' input parameters to the the fragment shader, then am using a line distance calculation to plot the line. This has helped and things are much better, however still... once I get to a certain speed the line becomes dotted!

    Some example code for the line calculation:

    Code (CSharp):
    1. float segment(float2 p, float2 a, float2 b) {
    2.     float2 ab = b - a, ap = p - a;
    3.     return length(ap - ab * clamp(dot(ab, ap) / dot(ab, ab), 0.0, 1.0));
    4. }
    .. line drawing in the main

    Code (CSharp):
    1. // Draw Line
    2. float dist = segment(uv, previousPosition, currentPosition);
    I expected that the line would be continuous always and even if I moved too fast the shader would connect the lines, which it does in a way.. but the line is dotted once I get above a certain movement speed.

    Any idea how I can achieve a smooth, continuous line? Is there a way to interpolate the line drawing or another way?

    Any help appreciated, thanks!
     
  2. LudoLoop

    LudoLoop

    Joined:
    Apr 18, 2017
    Posts:
    5
    So.. our current theory on what is happening.
    • Our system is using an additive blend mode, so when moving the quickly, where the lines overlap from drawing it's creating a dot on the line join / overlap.
    • Faster the paint actor moves more dots you see, as more line joins (previousPosition -- currentPosition).
    • We're also using alpha and color drop-off, so the joins / dots are more visible in this case.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    If you're painting directly into the game view's render target (like if you're doing this as a post process on top of the final rendered scene) the technique you're using is going to be difficult to solve the problem for. You might be able to pass in the previous line segment's information in as a "subtraction" to cut out a notch, preventing the overlap, but that might not join perfectly.

    If you're doing this for PC then passing an array of line segments and a line count, then drawing all of the line segments in a single shader can solve the problem for you, as you can do max() operations instead of an add. For mobile this gets a little harder as dynamic branches either don't work or are much slower, but it's still possible with some creative use of keywords and fixed length loops.

    If you draw your lines into a separate render texture that you composite into the scene after rendering all of the lines, you can add BlendOp Max to the shader and get a similar effect as rendering it all in one shader.

    However ultimately the "best" option is to not use blits at all, and instead use a line mesh. This will be way faster to render than doing multiple full screen blits to render line segments. So my real suggestion is use Unity's LineRenderer and just set the points from script.
     
  4. LudoLoop

    LudoLoop

    Joined:
    Apr 18, 2017
    Posts:
    5
    Thanks @bgolus, we appreciate the good reply, along with some decent suggestions!

    To clarify on the type of operation -- we are developing for PC and are drawing on a render texture applied to game mesh objects. So the line drawing and blit operation happen in-game and is actually in control of the player.

    Am pretty sure we have a decent approach for our use-case, however the dotted line issue when moving the drawing actor too fast is a real problem. For a seemingly simple problem, it's proving a real challenge to find a viable solution.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Are you doing a blit into a "lines only" render texture, then applying that on top of the object, or doing a blit directly over the object texture (assuming the object isn't black). If it's the prior then you might just need to do an outputColor = max(src, lineColor) instead of outputColor = src + lineColor if that makes sense. If you're doing the later then using the previous line as a "cutout" might be your best bet.
     
  6. LudoLoop

    LudoLoop

    Joined:
    Apr 18, 2017
    Posts:
    5
    This is really helpful thanks -- we'll investigate a fix and let you know the final result. :)
     
  7. doodleblue

    doodleblue

    Joined:
    Jul 14, 2017
    Posts:
    3
    Please do let us know the final results :)
     
  8. LudoLoop

    LudoLoop

    Joined:
    Apr 18, 2017
    Posts:
    5
    @doodleblue -- we interpolated the line drawing.. so basically used a technique to draw the line with an variable amount of dots that related to the distance and performance etc. This filled in gaps, gave a smooth line and was acceptable for performance in our project.