Drawing line strips,... at runtime

I’m working on a small app (Windows and Android) that reads a text file with coordinates and some additional info and then uses these infos to draw stuff on screen. I already created an app like that in Java that uses OpenGL_ES but I’m having troubles finding the right way to do it in Unity now (C#, version 2017.3.1f1 Personal).

The 3 most important things that I have to draw are:

  • Some kind of line strip (GL_LINE_STRIP with OpenGL). The txt file lists coordinates of single vertices, which have to be connected in the right order (first vertex != last vertex).
  • An untextured plane (GL_TRIANGEL_FAN or GL_TRIANGLES with OpenGL). The vertices in the text file describe a line strip (first vertex == last vertex) that has to be filled.
  • A textured plane (rectangular with 90° angles). The txt file includes the coordinates of the 4 corner vertex, which then have to be used to display a picture (comes with the txt file) in the right spot.

What I have to do with these objects:

  • Change the line width of the line strips
  • Change the color of the line strips and untextured planes
  • Get a grip on the created objects, so I can store them in an array in my static class, delete them from the scene and load them again later
  • They don’t have to cast shadows, be animated or move but they do have to be created at runtime, which means that I can’t just import a mesh that was created in e.g. Blender.

I already tried this but google tells me that you can’t change the line width of stuff drawn with OpenGL in Unity and I’m not sure how you would even reuse the created objects (or whatever they are):

void OnRenderObject() {
    Shader shader = Shader.Find("Hidden/Internal-Colored");
    Material mat = new Material(shader);
    mat.SetPass(0);

    GL.Begin(GL.LINE_STRIP);
    GL.Color(new Color(1,0,0));
    GL.Vertex3(0,0,0);
    GL.Vertex3(0,20,0);
    GL.Vertex3(0,20,20);
    GL.Vertex3(0,0,20);
    GL.End();
}

Is there a way to do all of this in Unity without using additional assets from the store?

There are much simpler solutions for this than using GL. For the lines you can use the line renderer component, it includes the color, positions and width properties that you need.

As for the triangles and planes, you should create a new mesh. If you already used OpenGL then you shouldn’t have to much trouble making the faces. For the colors, change the material color of the mesh renderer.

2 Likes

As @whileBreak said you can use the line renderer or use GL_QUADS to draw larger lines (but that doesn’t look great at the joins unless you add extra polys to take them into account).

I know you were wanting a free solution but if you get stuck then Vectrosity is great and probably ideal for what you want to do. (Vectrosity | Particles/Effects | Unity Asset Store)

@whileBreak
Thanks, the line renderer is indeed pretty nice and easy to handle and the awesome thing is: You can use it in a class that doesn’t derive from “MonoBehaviour” (can’t call “Destroy(myobject)” then, of course)!

The only two things I don’t like are the weird, flat look (“billboard lines (polygons that always face the camera)” according to the documentary), which looks especially weird if there’s a narrow zigzag, and the flickering if you get further away, which you can prevent (at least a bit) by increasing the line width but then zigzags look even worse.
Is it possible to get rid of the latter with some setting, so without having to increase the line width even more?

It took me a while to find a good tutorial that actually focused on how to do everything with the line renderer programmatically and ended up using this:

private GameObject MakeLine(Vector3[] vertices) {
    GameObject o = new GameObject();
    LineRenderer line = o.AddComponent<LineRenderer>().GetComponent<LineRenderer>();
    Shader shader = Shader.Find("Hidden/Internal-Colored");
    Material mat = new Material(shader) {color = Color.white};
    line.material = mat;
    line.startWidth = 1f;
    line.endWidth = 1f;
    line.positionCount = vertices.Length;
    line.SetPositions(vertices);
    return o;
}

Is there a difference in terms of performance if you use a shader instead of the mesh renderer to create a material to change the color?

I noticed that you can set a name and a tag, can you automatically display either (or some type of label) at one beginning/end of the line strip and show/hide it (without also effecting the line strip)? I know that you can add your own separate (3D) text objects but you’ll have to rotate and move those to fit the objects’ positions.

I haven’t gotten into creating meshes yet but I should get to that tomorrow and will post again then.

@tonemcbride
The problem with OpenGL here is that I can’t seem to get a grip on an actual object, which I have to do to store it for later use and using quads for lines sadly is rather suboptimal.
Thanks for the suggestion, I looked into it but it looks like it’s overkill for what I have to do and the reviews agree - at least for drawing/displaying lines.

The thing is that when objects are instantiated at runtime they usually create a new instance of their material, so you need to access to the material to set the overall color. And to do that you need the reference to that particular material instance hold on the MeshRenderer.

On the other hand you could just set the color of the vertices themselves and use a vertex color shader. You would do that at the time you create the mesh.

Yes you can have Canvas set to World Canvas, but it would need a script to always look at the camera.

Update()
{
     transform.LookAt(Camera.main.transform);
}

I usually just make them look on the plane direction so it doesn’t bend upwards or downwards.

Update()
{
     Vector3 dir = Camera.main.transform.position - transform.position;
     dir.y = 0f;
     dir.Normalize();

     transform.LookAt(transform.position + dir);
}

Oh, you’re fast! Thanks!

I don’t mind either way. The code I posted last time works (using the shader to set the color) but if there’s an alternative with better performance, I’ll use that.

I want to use what’s fastest (and doesn’t have to be added as an asset/programmed aditionally). I already tried “line.startColor”/“line.endColor” but it always used the color of the material used last, which happened to be the red of the buttons in the main menu (it’s the “Pressed Color”).

No, I don’t. :wink:
If I can’t give the line strips automatic labels that can be displayed/hidden, I’ll go with creating a couple of (flat) text objects (1 for each line strip with alpha set to 0.5) that are positioned next to the first or last vertex of the line strip (depending on where that is).
I don’t want them to rotate because it would simply be too distracting (imagine 30 line strips with rotating texts next to them…). If I can make the text readable from both sides (front and back), that’s okay, otherwise I’ll disable backface culling (if the depth test still works properly!), so the text is mirrored if you look at it from the other side. But the whole backface culling thing is a different problem I haven’t really looked into yet.

1 Like

I’m now working on displaying the planes properly (currently only untextured) and it’s surprising/awesome how much it actually lets you do (use specific colors for triangles and even color gradients!). My code (bare minimum of what you need):

Vector3[] v = new Vector3[] {new Vector3(0,0,0),new Vector3(50,0,0), new Vector3(0,25,25),new Vector3(0,0,0),new Vector3(50,0,0),new Vector3(25,0,-25)};
GameObject o = new GameObject();
Mesh m = new Mesh();
Material mat = new Material(Shader.Find("Hidden/Internal-Colored"));

o.AddComponent<MeshRenderer>();
o.AddComponent<MeshFilter>();
o.GetComponent<MeshFilter>().mesh = m;
o.GetComponent<MeshRenderer>().material = mat;
m.vertices = v;
m.triangles = new int[] { 0,1,2,3,4,5 };
m.colors = new Color[] {Color.red,Color.yellow,Color.white,new Color(0,0,1,0.5f),new Color(0,0,1,0.5f),new Color(0,0,1,0.5f)};

Apart from the obvious you only have to set a couple of things: The MeshRenderer and the triangles, otherwise nothing’ll be displayed, and the material/shader to set a specific color (otherwise the model’s just pink and the first triangle is missing) - so luckily no UVs or normals (which I haven’t got anyway).
Also: “m.colors” sets the colors of vertices, which lets you create color gradients (like in my code), for a single color for the whole mesh just call “mat.color”.

The only thing I haven’t managed to get to work yet is transparency - setting the alpha channel in “new Color” doesn’t do anything.
If this is caused by the shader not supporting transparency (it does with the LineRenderer), which one can I use instead (I’m not using any lights apart from the default “Directional Light”)? I already tried “Standard” and “Transparent/Diffuse” but with both only the blue (=second) triangle gets displayed as a black, fully opaque one.

Edit:
Hm, the transparency does work in a way: With the alpha set to 0.2, the meshes look darker than they do with 0.5 (because of the black background) but the visability with the LineRenderer is messed up somehow: You can see the lines that are further away through the mesh but if you walk towards them, they disappear behind the mesh.