Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by kneave · Aug 13, 2014 at 04:55 PM · performancesimulation

Large Numbers of GameObjects/Items

I've been tasked with developing a front end for a situational awareness system that tracks objects globally and I've two solutions, both with serious issues, and despite a few weeks of research failing to find a solution.

The scenario: Currently I'm tracking around 45,000 objects, this is due to increase to around 150,000. Each object has a position, direction and speed that vary over time and need to be displayed on screen. Hardware isn't so much an issue as my workstation has a Quadro K5000 card and the system that will be running this in production has three Quadro K6000, 24 cores and 256GB RAM.

The approaches I've taken are as follows;

  1. A particle system based on this tutorial. This approach is brilliant for the global view and can easily handle a million data points on screen at a time. This includes rendering on a rotating globe. The big issue here is that due to a limitation, of the particle system or of my knowledge, zooming in causes the whole system to be occluded. As I understand it, this occurs when the centre of the particle system is off camera for performance purposes.

  2. An array of GameObject with a simple mesh, instantiated in Start() and moved into position as needed. This allows me to render 45,000 or so objects and zoom in at will as they each have a rendered of their own, the massive drawback to this approach is that transformations need to be done on the main thread and as such performance drops to around 13fps making the whole thing jerky.

Personally I love the particle system approach as performance is brilliant and can handle up to a million particles without breaking a sweat. If I could solve the occlusion issue it would be ideal. From the past few weeks of forum trawling it seems to have been an issue since the system was introduced and not an easy one to fix. It certainly doesn't help that I'm not using the particle system for it's intended use I guess.

My question is, is there a best practice for handling large numbers of objects such as my requirement? I'm not sure if my approaches are off or my expectations but if someone could point me in the right direction I'd greatly appreciate it.

Comment
Add comment · Show 6
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image robertbu · Aug 13, 2014 at 05:05 PM 1
Share

I went ahead and published your question since someone might know how to solve your ParticleSystem occlusion issue, but in general, design questions should be posted to Unity Forums. If you cannot solve the occlusion problem, then look to doing a custom mesh solution. Someone who knows Unity and meshes could knock it out in a day or two. Google 'voxels'.

avatar image robertbu · Aug 13, 2014 at 05:36 PM 0
Share

Untested idea: Based on a quick glance at your 'this tutorial' link, you are setting the particle properties directly in the particle array. If so, make sure you are using 'World' simulation space, make sure the particle system is in front of the camera, then parent the particle system to the camera.

Edit: See the answer by @djarcas in this question:

http://answers.unity3d.com/questions/218369/shuriken-particle-system-not-rendering-particles-w.html

Though if you are always setting the position of your particles manually, the my untested idea should work.

avatar image kneave · Aug 14, 2014 at 08:44 AM 0
Share

Thanks for publishing and apologies for posting in the wrong place, still new to the community and finding my feet.

Alas I'd tried the parenting option already, it still requires the camera to have the centre of the particle system in view and setting to world space doesn't seem to make a difference either.

Voxels do look quite cool as a concept, it'll give a really nice volumetric look to the UI too. Thanks for the tip, I'll take a look.

For completeness, I had a long think overnight and I'm probably going to have to use both my approaches and switch modes depending on zoom level.

avatar image robertbu · Aug 14, 2014 at 02:45 PM 0
Share

Alas I'd tried the parenting option already, it still requires the camera to have the centre of the particle system in view

If you make the particle system a child of the camera while the particle system is in view, then no matter how you move or rotate the camera, the particle system will still be in view.

avatar image kneave · Aug 14, 2014 at 06:18 PM 0
Share

In that case I may have misunderstood how to parent an object. I've created an offset to render points at different points around 0,0 which seems to work. Is parenting done in code or in the hierarchy window?

Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by robertbu · Aug 16, 2014 at 12:16 AM

I think your current particle system issue can be figured out. But I was giving some though to how this would be done in a mesh (vs individual game object), and decided to write some test code. What I came up with is a class called ParticleDisplay. It has most of the standard particle properties:

  • color;

  • lifetime;

  • position;

  • rotation;

  • size;

  • velocity;

  • angularVelocity

There's no emitter...just access to the array so the properties can be modified dynamically. Performance is around 10x faster than using game objects and roughly 1/2 the performance of a particle system with all particles displayed. I'm sure there are general performance improvements to be had in this code, but given your hardware, the best thing would be to rework it to use threads.

 using UnityEngine;
 using System.Collections;
 
 public class ParticleDisplay : MonoBehaviour {
     public Camera camera;                     // Camera to use for rotation
     public int maxParticles = 1000;
     public bool dynamicColor = false;         // Use color defined in particle
     public Material material;                 // Material for particles
     public float defaultParticleSize = 0.1f;
     public Color defaultParticleColor = Color.white;
 
     public struct Particle {
         public float angularVelocity;
         public Color color;
         public float lifetime;
         public Vector3 position;
         public float rotation;
         public float size;
         public Vector3 velocity;
     }
 
     public Particle[] particles;
 
     private const int quadsPerMesh = 16380;
 
     private GameObject[] goMeshes;
     private Mesh[] meshes;
     private bool isRunning = false;
 
     private Transform camTrans;
 
     void Awake() {
         if (camera == null) 
             camera = Camera.main;
 
         if (camera != null)
             camTrans = camera.transform;
 
         CreateMeshes();
     }
 
     void CreateMeshes() {
         if (maxParticles <= 0) return;
         
         particles = new Particle[maxParticles];
         for (int i = 0; i < particles.Length; i++) {
             particles[i].size = defaultParticleSize;
             particles[i].color = defaultParticleColor;
         }
         
         int meshCount = maxParticles / quadsPerMesh + 1;
         int quadsLastMesh = maxParticles % quadsPerMesh;
 
         goMeshes = new GameObject[meshCount];
         meshes = new Mesh[meshCount];
         
         for (int i = 0; i < meshCount; i++) {
             GameObject go = new GameObject();
             go.transform.parent = transform;
             MeshFilter mf = go.AddComponent<MeshFilter>();
             Mesh mesh = new Mesh();
             mesh.MarkDynamic ();
             mf.mesh = mesh;
             Renderer rend = go.AddComponent<MeshRenderer>();
             rend.material = material;
 
             Vector3[] vertices;
             if (i != meshCount - 1) {
                 vertices = new Vector3[4 * quadsPerMesh];
             }
             else {
                 vertices = new Vector3[4 * quadsLastMesh];
             }
 
             mesh.vertices = vertices;
 
             int[] triangles = new int[mesh.vertices.Length / 2 * 3];
                 
             for (int j = 0; j < vertices.Length / 4; j++) {
 
                 triangles[j * 6 + 0] = j * 4 + 0;    //     0_ 3        0 ___ 3
                 triangles[j * 6 + 1] = j * 4 + 3;    //   | /         |    /|
                 triangles[j * 6 + 2] = j * 4 + 1;    //  1|/            1|/__|2
                 
                 triangles[j * 6 + 3] = j * 4 + 3;    //       3
                 triangles[j * 6 + 4] = j * 4 + 2;    //    /|
                 triangles[j * 6 + 5] = j * 4 + 1;    //  1/_|2
             }
 
             mesh.triangles = triangles;
             Color[] colors = new Color[mesh.vertices.Length];
 
             for (int j = 0; j < colors.Length; j++) {
                 colors[j] = defaultParticleColor;
             }
 
             mesh.colors = colors;
 
             Vector2[] uvs = new Vector2[mesh.vertices.Length];
             for (int j = 0; j < vertices.Length / 4; j++) {
                 uvs[j * 4 + 0] = new Vector2(0,1);
                 uvs[j * 4 + 1] = new Vector2(0,0);
                 uvs[j * 4 + 2] = new Vector2(1,0);
                 uvs[j * 4 + 3] = new Vector2(1,1);
             }
 
             mesh.uv = uvs;
 
             goMeshes[i] = go;
             meshes[i] = mesh;
         }
     }
 
     public void Clear () {
         for (int i = 0; i < particles.Length; i++) 
             particles[i].lifetime = -1.0f;
     }
 
     public void Play() {
         isRunning = true;
         foreach (GameObject go in goMeshes) go.renderer.enabled = true;
     }
     
     public void Stop () {
         isRunning = false;
         foreach (GameObject go in goMeshes) go.renderer.enabled = false;
     }
 
 
     void LateUpdate() {
         if (!isRunning) return;
         
         Vector3 fwd = camTrans.forward;
         Quaternion qCam = camTrans.rotation;
 
         for (int i = 0; i < meshes.Length; i++) {
             Mesh mesh = meshes[i];
             Vector3[] vertices = mesh.vertices;
             Color[] colors = null;
             if (dynamicColor) {
                 colors = mesh.colors;
             }
             for (int j = 0; j < vertices.Length / 4; j++) {
                 Particle particle = particles[i * quadsPerMesh + j];
                 
                 if (particle.lifetime < 0) {
                     vertices[j * 4] = Vector3.zero;
                     vertices[j * 4 + 1] = Vector3.zero;
                     vertices[j * 4 + 2] = Vector3.zero;
                     vertices[j * 4 + 3] = Vector3.zero;
                 }
                 else {
                     float l = particle.size / 2.0f;
                     Vector3 v0 = new Vector3(-l, l, 0);
                     Vector3 v1 = new Vector3(-l,-l, 0);
                     Vector3 v2 = new Vector3( l,-l, 0);
                     Vector3 v3 = new Vector3( l, l, 0);
                     
                     Quaternion q = Quaternion.AngleAxis(particle.rotation, fwd) * qCam;
                     
                     vertices[j * 4] = particle.position + q * v0;
                     vertices[j * 4 + 1] = particle.position + q * v1;
                     vertices[j * 4 + 2] = particle.position + q * v2;
                     vertices[j * 4 + 3] = particle.position + q * v3;
                     
                     if (dynamicColor) {
                         colors[j * 4] = particle.color;
                         colors[j * 4 + 1] = particle.color;
                         colors[j * 4 + 2] = particle.color;
                         colors[j * 4 + 3] = particle.color;
                     }
                     
                     particle.lifetime -= Time.deltaTime;
                     particle.rotation += particle.angularVelocity * Time.deltaTime;
                     particle.position += particle.velocity * Time.deltaTime;
                     
                     particles[i * quadsPerMesh + j] = particle;
                 }
             }
             mesh.vertices = vertices;
             
             if (dynamicColor) {
                 mesh.colors = colors;
             }
         }
     }
 }


And here is simple class as an example of how to drive the ParticleDisplay class:

 using UnityEngine;
 using System.Collections;
 
 public class ParticleDisplayTest : MonoBehaviour {
 
     private ParticleDisplay pd;
     private ParticleDisplay.Particle[] pdp;
 
     void Start () {
 
         pd = GetComponent<ParticleDisplay>();
         pdp = pd.particles;
 
         for (int i = 0; i < pdp.Length; i++) {
             Particle p;
             pdp[i].velocity = Random.insideUnitSphere;
             pdp[i].angularVelocity = Random.Range (0.0f, 180.0f);
             pdp[i].lifetime = Mathf.Infinity;
             pdp[i].color = new Color(Random.Range (0.0f, 1.0f), Random.Range (0.0f, 1.0f), Random.Range (0.0f, 1.0f));
         }
 
         pd.Play();
     }
     
     void Update () {
         for (int i = 0; i < pdp.Length; i++) {
             if (pdp[i].position.magnitude > 8.0f) {
                 pdp[i].velocity = -pdp[i].velocity;
                 pdp[i].position = pdp[i].position.normalized * 7.999f;
                 pdp[i].color.r = (pdp[i].color.r + Time.deltaTime * 0.5f) % 1.0f;
             }
         }
     }
 }

There's no default material, so you'll need to create one. I used Particles/Additive as the shader, and a snowflake with transparency for the test image.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image kneave · Nov 09, 2014 at 11:56 PM 0
Share

Apologies for not responding, I'll have a look through this tomorrow as it looks very promising! Thanks for your hard work. :)

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

2 People are following this question.

avatar image avatar image

Related Questions

To what limit can Unity scale? 1 Answer

Networking for send command (string, float, boolean, int) 0 Answers

how to simulate mobile performance? 0 Answers

Performance issue: Updating list for a simple crowd simulation 2 Answers

Does a coroutine do all its work during one frame? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges