Efficiently drawing thousands of lines

Hey graphics gurus. I’m trying to replicate Eve Online’s 3D starmap based on the SDE they make available. So that’s about 5500 nodes (2D circles) and about 7000 edges (lines) I need to render. The user can move a camera around to inspect the map from different angles and click on the nodes to select individual nodes that are then highlighted. The user can also change the color scheme of edges and nodes based on meta-data (security status, ownership, etc). What’s a reasonably efficient way to do this and still be able to run a lot of CPU simulation logic under the hood?

I’m just a hobbyist so I’m using blunt force by spawn a game object for each node, placing it in world space and orienting the sprite billboard toward the camera on Update (partly so i can ray trace from the camera for click functionality). I’m also changing the scale of the billboard each Update based on camera distance so it appears to remain almost the same size regardless of distance. Using the Job system, I can actually do this pretty quickly. For edges/lines I’m using the old Vectrosity asset to create one line object with segments representing each edge, but that needs to be refreshed on Update as well. All told I get about 40 FPS on my 6 year old gaming laptop, when the map is zoomed out, with no simulation logic running. If I’m reading the profiler correctly, a lot of the latency is due to rendering time.

I don’t know much about graphics, but seems reasonable that a shader could draw my edges and nodes in screen-space based on their 3D position? Or I could somehow turn edges into a mesh, then apply a wireframe shader? Any suggestions would be very welcome, thank you!

You could use a particle system for the stars and a line renderer to connect them.

Thanks for the reply! I haven’t messed with Lin renderer in many years, did they make it super efficient? IIRC when I used it last it worked very similarly to Vectrosity. As for particles, I worked with them a long time ago and you could randomly spawn them in a volume or spawn them from a single point. How do I position individual particles and scale them based on distance to camera? Or what keywords can I search for to learn that? Thanks again!

You can specify a min and max scale in the particle system’s render module.

In the particle system’s main settings you can specify a random seed number so the particles always spawn in the same place. You’ll also need to set their lifetime to infinity so they don’t die. And disable looping.

It is possible to get and set their positions if you really need to do it. Lots of examples available through Google.

Something else to consider is to enable the Trails module on the particles and set the trail mode to Ribbon. Doing this will render a line between the particles. It obviously won’t show accurate routing but it may be okay for aesthetics. You can still use a Line Render for showing more accurate routing for when a star is selected.

try also GL.Lines

and Unity - Scripting API: Graphics.DrawProcedural
can easily draw millions of things.

(especially if this is on desktop devices)

1 Like

@zulo3d Thank you! Switching over to the particle system and just individually positions all the particles bumped my FPS by about 50. I’m using the Job system to set each particles scale and alpha based on distance to the camera. Haven’t tried the trail module yet. Any ideas for how a user could click on the particle to select it? As far as I can tell I cant ray cast against a particle.

@mgear Thank you, GL.Lines looks really promising! Also looks like I’ll be able color of each line based on the color of the two nodes it’s connecting.

The particle renderer will set the scale for you and you should be able to use a shader/material or just enable fog to darken the distant stars. To detect which star is selected you’ll need to iterate through the stars and turn their world position into a screen position and compare that with the cursor position.

@zulo3d So the selection you describe makes perfect sense. For the particles, looks like you’re referring to a block in VFX graph. I’d been using the old cpu particle system so I could easily set particle world position via c#. From what I can tell the only way to directly set position of individual particles in VFX graph with a graphics buffer? But even then, looks like you cant create new or update graphics buffers at run time. so then I’m not sure how I’d allow the user to set the color scheme of the particles based on different meta data (like a solar systems security status, faction ownership, etc). I’m pretty new to all particle related stuff, so I’m guessing I’m missing something? Thanks again!

I’ve never used VFX graph. I use the old particle system. I believe it’s possible to set the colors through the custom data module. I’ve never tried this myself.

Ah ok, so in the old particle system, I don’t see any methods or fields about screen space, just size in world space. How do I have it auto scale the world scale to maintain the same screen size? Thanks!

I don’t think there’s a setting to auto scale to the screen size. You’ll need do that yourself by setting the Scale X,Y,Z in the Shape module.

Unity - Scripting API: Graphics

DrawProceduralIndirect Draws procedural geometry on the GPU.
DrawProceduralIndirectNow Draws procedural geometry on the GPU.
DrawProceduralNow Draws procedural geometry on the GPU.

Unity - Scripting API: Graphics.DrawProceduralNow

I think you could use the above for lines using a MeshTopology.Lines (A->B C->D) Finding references on usage not the easiest, although Keijiro used it for something more complex.

GitHub - keijiro/NoiseBall3: A Unity example that shows how to use the new implementation of DrawProcedural.

Possibly the below too:

RenderPrimitives Renders non-indexed primitives with GPU instancing and a custom shader.
RenderPrimitivesIndexed Renders indexed primitives with GPU instancing and a custom shader.
RenderPrimitivesIndexedIndirect Renders indexed primitives with GPU instancing and a custom shader with rendering command arguments from commandBuffer.
RenderPrimitivesIndirect Renders primitives with GPU instancing and a custom shader using rendering command arguments from commandBuffer.

This ECS sample may also be helpful.

Thank you all! Using your suggestions my FPS (while not running simulation logic) went from 40FPS to about 200, and I’ve still got the click to select functionality! It’d be nice if I could figure out how to draw the GL.Line lines behind the particles, but I can live without that.

@Arithmetica your right, ECS is probably the optimal way to do this overall. I’ve been trying to go fast and just make something fun. Eventually, I’ll bite the bullet and refactor what I have to ECS. Since I’m already using the Jobs system for a few tasks already, hopefully the switch won’t be too painful. using Eve’s universe, I’d love to have like 300-400 factions fighting over resources with an abstracted representation of fleets and powering an economy based on the losses from battle. Looks like ECS means I could theoretically scale this to have tens of thousands of agents (fleets, factions, factories, etc). Thanks!