10
\$\begingroup\$

I've noticed that as I add more vertexes to a Line Renderer line the line twists and stops being a smooth line.

enter image description here

.GIF Here: http://i.imgur.com/hRAhCXM.gif

All lines are on the same z level, even if I remove the materials the lines still seem to twist.

I have no idea why it does this, or how to go about resolving it, any suggestions?C#

\$\endgroup\$
  • \$\begingroup\$ This is a problem with the algorithm the LineRenderer uses to position its vertices, which deals poorly with sharp corners. You can ameliorate it somewhat by adding more points to round-out the corners, but the most robust method I've found is to duplicate its functionality for yourself with a better algorithm, by creating a dynamic Mesh with the vertices you need, or creatively exploiting a ParticleSystem to draw the chain of points. \$\endgroup\$ – DMGregory Feb 10 '15 at 21:32
  • 1
    \$\begingroup\$ Update: Later versions of Unity (5 5+) have improved the LineRenderer algorithm, so users considering using LineRenderers may find the out-of-the-box result is substantially better these days. \$\endgroup\$ – DMGregory Jun 17 '17 at 12:23
10
\$\begingroup\$

The problem is basically this:

enter image description here

The LineRenderer is trying to connect the red dot positions. It's creating the green vertices to make a mesh. So the top line segment looks great. But then the LineRenderer tries to be economical, it reuses the vertices from the end of one line segment in the end of the second line segment. When there's a sharp angle, you get the problem you're seeing. The second line segment is pinched at the intersection because its 'end cap' is not perpendicular with its other 'end cap'.

The solution is to create your own line renderer, and not make it so economical. You can do this by generating a dynamic mesh. The mesh will consist of a series of thin quads. For each line segment, you can compute the four corners of the quad by calculating the normal of the line and a specified line width:

Vector3 normal = Vector3.Cross(start, end);
Vector3 side = Vector3.Cross(normal, end-start);
side.Normalize();
Vector3 a = start + side * (lineWidth / 2);
Vector3 b = start + side * (lineWidth / -2);
Vector3 c = end + side * (lineWidth / 2);
Vector3 d = end + side * (lineWidth / -2);

Here, a, b, c and d make up the four corners of a single line segment, just like the green dots in the image above. These vertices would be added to the mesh, and you'd also add the indices to make the four vertices into two triangles (so, six indices would be added, a-b-c and b-d-c).

This can obviously get rather complex. I believe another reason Unity implemented their LineRenderer the way they did was because doing it that way avoids another problem, corners. When you start drawing each line segment you'll start to see where the two line segments come together and form an ugly joint. There are ways to deal with this by calculating the shared normal between both lines and updating their vertices to the shared normal, but this only partially solves the problem, since you can still easily end up with pinched lines. The most robust solution is to generate additional vertices at the joints to act as corners.

\$\endgroup\$
  • 1
    \$\begingroup\$ You can also probably get away with using the unity line renderer and inserting additional points to create a small miter. Just put a point a little ways toward the next point from the previous point. \$\endgroup\$ – mklingen Feb 11 '15 at 22:20
  • \$\begingroup\$ Great explanation Byte, thanks. I'll probably try and see if I can't generate more points at each of the corners, if that doesn't work out well, I'll go ahead and try my hand at making a dynamic mesh. Thank you. \$\endgroup\$ – Douglas Gaskell Feb 12 '15 at 7:47
1
\$\begingroup\$

I had the same problem and I solve it adding more points to smooth edges its not an elegant solution but its simple and works. At least for me. I wrote a function to do this:

Vector3[] Generate_Points(Vector3[] keyPoints, int segments=100){
    Vector3[] Points = new Vector3[(keyPoints.Length - 1) * segments + keyPoints.Length];
    for(int i = 1; i < keyPoints.Length;i++){
        Points [(i - 1) * segments + i - 1] = new Vector3(keyPoints [i-1].x,keyPoints [i-1].y,0);
        for (int j = 1;j<=segments;j++){
            float x = keyPoints [i - 1].x;
            float y = keyPoints [i - 1].y;
            float z = 0;//keyPoints [i - 1].z;
            float dx = (keyPoints [i].x - keyPoints [i - 1].x)/segments;
            float dy = (keyPoints [i].y - keyPoints [i - 1].y)/segments;
            Points [(i - 1) * segments + j + i - 1] = new Vector3 (x+dx*j,y+dy*j,z);
        }
    }
    Points [(keyPoints.Length - 1) * segments + keyPoints.Length - 1] = new Vector3(keyPoints [keyPoints.Length-1].x,keyPoints [keyPoints.Length-1].y,0);
    return Points;
}
\$\endgroup\$
0
\$\begingroup\$

One way to solve this for opaque lines is to render a circle at each joint. The circle needs to have a diameter equal to the line width. This requires a few extra vertices to be rendered, but looks really nice. You also can then get rid of the shared normals between the joints.

This still requires that you write your own rendering code instead of using the Unity Line Renderer.

I made an asset that does just this at http://u3d.as/nFE

\$\endgroup\$
0
\$\begingroup\$

The way I avoided this is that I always set the z position to 0 in the LineRenderer.SetPosition() method. So instead of writing

LineRenderer.SetPosition(i, myVector3);

I wrote

LineRenderer.SetPosition(i, new Vector3(myVector3.x, myVector3.y, 0));

But that isn't the best solution as you will still get those strange lines. The best solution that I could think of was creating separate lines. That worked perfectly for me. Hope this helps you.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.