Search Unity

Successfully Rendering 2500 Characters With Physics In HTC Vive At 90FPS

Discussion in 'AR/VR (XR) Discussion' started by brilliantgames, Oct 16, 2016.

  1. brilliantgames

    brilliantgames

    Joined:
    Jan 7, 2012
    Posts:
    1,937
    Hey everyone. Just wanted to show off some tech I've been working on for VR, and share how I did it.

    I wanted to have battles on a scale comparable to the Total War series. This is an ambitious proposition to say the least. Unity is not built to handle rendering large amounts of characters on both the physics and the rendering side. I will go over how I was able to accomplish this.


    Rendering:
    My first task was getting Unity to render thousands of characters at 90FPS on an HTC Vive. How could this be possible? The challenges of rendering in VR are almost as difficult as mobile. You have 2 cameras, which cuts FPS in half, and you need to reach that difficult goal of 90FPS so that the player doesn't get sick. The answer in my mind was simple. Sprites. The approach I took for rendering characters to sprites is very similar to Unitys approach to rendering tree sprites on terrain. I rendered a single character to a render texture and rotated his axis according the cameras rotation. A very effective and elegant solution.

    Physics:
    This was a challenging problem. Simply moving a transform in Unity can be expensive, let alone having thousands of characters with physics. So how did I do it? All character movement is handled in a single array. Physics are very simple. The units move along a grid in increments. When the battalion is ordered to move forward one increment, a series of raycasts are done in a grid like pattern for each units future position in the battalion. Once the future positions have been determined, each unit is lerped into his future position. This approach is extremely efficient for 2 reasons. First, raycasts only occur in per increment of movement, meaning it only occurs every 4 feet or so of travel(Or whatever the units spacing is set to). Secondly, the raycasts can be spread out over a set amount of frames with almost no effect on the units movement.

    Hope this helps out anyone hoping to accomplish some ambitious things in Unity. PS, without worrying about VR I would say having well over 10,000 characters rendering would be very reasonable with this system.


    Here is a video of 2500 characters marching on rough terrain. At the moment, all characters are sprites, even when close to the camera.
     
  2. Katerpilet

    Katerpilet

    Joined:
    Aug 2, 2015
    Posts:
    87
    The typical way of doing this in graphics programming is to have the same 3D odel and texture loaded into the GPU, and then just using that model/texture for each instantiation (without touching GPU state). Did you do any tests to see how many units you could get on screen without using a sprite based approached (which will cause a reduction in visual quality)?
     
  3. brilliantgames

    brilliantgames

    Joined:
    Jan 7, 2012
    Posts:
    1,937

    About 30-40 characters on a Vive and it's done for... This video clip is only demonstrating thousands of sprites. I have already made it so 3d models fade in when they get close.
     
  4. Thermos

    Thermos

    Joined:
    Feb 23, 2015
    Posts:
    148
    Amazing Job!

    We are trying to do the same thing, right now we can only achieve 1,000 units at 45FPS on E3 + GTX970. Physics takes 4-6ms while units are moving. I'm very intreseted in your approach of moving characters, however I'm not quite understand the "Once the future positions have been determined, each unit is lerped into his future position".. Lerp a character into its destination also moved its transform, how cloud that saving performance, because moving a transform which has collider on it is also expensive. Would you like to give us a little more detail? Thanks!

    Here is what we are doing for now:
    bandicam 2016-10-14 14-25-46-054_20161019095721.JPG bandicam 2016-10-14 14-42-15-815_20161019095803.JPG
     
  5. brilliantgames

    brilliantgames

    Joined:
    Jan 7, 2012
    Posts:
    1,937
    @Thermos My method of movement isn't as important as reducing your "transform.position" calls. "transform.position" is expensive to call on a large scale. Here's where your problem is.. You are using skinned meshes. Regardless of how low poly your characters are, Unity has to focus on moving all the children objects being in that character. Bones count as transforms! Characters often have hundreds of bones and if you move it's parent each bones transform has to be moved with it. Number of things you can do here. 1. You could reduce the number of bones in your characters to a very minimal amount. 2. Pre render your character animations into a bunch of solid meshes. 3. Use sprites for lower LOD like I did.

    The point of my grid movement method was not to reduce 'transform.position', it was to solve the problem of making the units stay grounded and have physics without colliders. Raycasts are used to determine ground height but are only called every 3-4 feet or so followed by the character lerping to that position.
     
  6. Thermos

    Thermos

    Joined:
    Feb 23, 2015
    Posts:
    148
    @brilliantgames , Thanks for reply. Seems I mistake your intent. Our game use spline to move entire unit group which contains 10-20 units so that avoid aligning with ground. Because each unit can take damage, I have to assign collider to each of them. Moving bunch of colliders forward is really really expensive. Setting unit group as rigidbody can make transform computation into physics computation thus saved 1-2ms overall, however it's still quit expensive.
     
  7. brilliantgames

    brilliantgames

    Joined:
    Jan 7, 2012
    Posts:
    1,937
    @Thermos Changed how the system works. It's now all meshes, no Sprites. Discovered a way to render without having transforms. 'Graphics.DrawMesh'. 10,000 characters having a battle in this vid. You're welcome. :)

     
  8. olkeencole

    olkeencole

    Joined:
    Oct 2, 2009
    Posts:
    17
    Very cool!

    Are you using BakeMesh on a few SkinMeshRenderers and then pumping it to Graphics.DrawMesh? You mentioned in the video description that you are also using LOD and (I assume) GPU Instancing. How do you use these with Graphics.DrawMesh?
     
  9. Thermos

    Thermos

    Joined:
    Feb 23, 2015
    Posts:
    148
    @brilliantgames Cool! I was hesitate to use drawmesh for I thought it might be slower to call it manually, seems I was wrong. Also, I'm trying to build my own octree structure to track all the units on battlefield, and update it using compute shader or multi-threading, that way when a dealing an explosive damage, I can find those units within range fast(no need for calculating vector magnitude) without having any collider on them. Still, just a theory, I'll test it in my spare time.
     
  10. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Wow, very cool. Thanks for the breakdown. How did you drive the various animations?
     
  11. brilliantgames

    brilliantgames

    Joined:
    Jan 7, 2012
    Posts:
    1,937
    Latest demonstration video. Character avoidance and awareness on a massive scale.