Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Terrain culling and colliders is hosed

Discussion in 'Physics' started by snacktime, Mar 14, 2015.

  1. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    This is so bad there aren't really words for it. The terrain tree culling is causing huge stutters because of how it's enabling/disabling the colliders based on what's being rendered as opposed to what's in range. A lot of people have run into this and I finally got around to fixing it because it was the single biggest performance killer in my game.

    I placed all my trees without colliders, then used a 2d spatial hash to load/unload colliders only on trees within a certain range as I move around,and now my performance doesn't suck.

    It took me about 2 hours to fix this on my end. The issue has been open for months, I think there is a ticket on it but couldn't find it. This is something that has caused issues for several games I've seen, and it's such a simple thing to fix. Come on guys.

    If it helps anyone else, here is the code for the 2d spatial grid I used. Note the coordOffset is used for grids that go into negative territory, as the hashing algo doesn't support that. Most folks can just ignore that.

    https://gist.github.com/gamemachine/102be3ace8a308a3bd5f
     
    Last edited: Mar 14, 2015
    spryx, elias_t, Mister-Royzo and 2 others like this.
  2. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    FYI I'll post the specific code that handles the actual loading/unloading of tree colliders at runtime once I get it in better shape.

    I started out enabling/disabling colliders on trees as they came into and went out of range, but I'm changing that approach to using a pool of colliders with rigidbodies set to kinematic that are always on, and just moving them around, which is hugely less expensive then enable/disable/create/destroy.
     
  3. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    So it's still really rough because it's part of a real game that is a WIP, but here is the code that will dynamically place colliders on trees or other objects as a player moves around the world. There are a couple of game specific items in this code I didn't put any time into making it completely drop in, but it should only take some minimal adjustments to remove my game specific logic or tweak it to meet your needs.

    The start method has some game specific stuff like the player object.

    Terrains are assumed to be in a gameobject container called Terrains. Other objects are assumed to be under a gameobject called TerrainObjects, with one child for each terrain that is a container for the actual objects. You can easily modify the above to suit your own game. My maps are minimum 64 terrains each with a lot of detail, so I have to break it all out like this just to manage it.

    The core logic is I create a pool of colliders with kinametic rigidbodies for the trees. For other objects I just enable/disable their mesh collider.

    I place limits on how many colliders get added/remove at any one time to avoid spikes. You might want to adjust the numbers to suit your game.

    https://gist.github.com/gamemachine/d222e3e1ff3a8c30d297
     
    Magic73, twobob and John-G like this.
  4. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    Why is everyone not saying how awesome this is.

    this is awesome. There I said it.
     
  5. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    So the issue is only really noticed when you are moving the camera around, right? And only if you actually have a somewhat far billboard start distance.

    Do you think I am probably fine if my billboard start distance is something like 50? It's not going to be enabling/disabling hundreds of colliders at a time as you sweep the camera around, right?
     
  6. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    "So the issue is only really noticed when you are moving the camera around, right, "And only if you actually have a somewhat far billboard start distance." Possibly.

    You should probably just with/wtihout test and see if this is a bottleneck in your final implementation, if so, this is a solution. if not, happy days.

    "It's not going to be enabling/disabling hundreds of colliders at a time as you sweep the camera around, right?" No, that is what the current system does. This is the opposite.
     
    Last edited: Apr 9, 2015
  7. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    I think you misunderstood what I was asking to a degree.

    With the default setup, using a 50meter billboard start, there aren't really that mean trees with full mesh around you, even if the tree view distance is 1000meters, because most a billboards. Therefore, stock terrain scripting, only has the colliders on for those handful of trees within 50meters. If I rotate the camera around, the trees within 50m in my view will become full mesh with colliders enabled.

    Using @snacktime's script, it will turn on colliders within a certain range just the same. The only difference would be that I could set the colliders to only come on within 25meters instead of 50meters for example, right?

    I guess what I am trying to say, is that at the end of the day, this isn't as big of an issue if your tree billboard start is relatively close at 50m, because you already don't have that many full LOD trees with colliders in range, regardless of tree view distance, right?
     
  8. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    Assuming the setting for the amount of mesh trees around does it's job.
    (pretty certain that didn't work last time I checked, but UT have a dedicated "Terrain person" now so...)

    And also assuming that the billboards don't have colliders... However, if the current system already ./had/ a working way to disable colliders based on distance (your assertion) - rather than "render visibility" - there would be no need for this script...

    I am going to do some tests today around this area. I will see if I can dredge up some "control" metrics. Which this discussion is sorely lacking.
     
  9. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    Yeah, I know the max mesh trees setting is broken, but I'm saying IF we assume there's only colliders enabled on full mesh trees AND billboard start is 50m, then there's on avg lets say 10 trees that are full mesh with colliders enabled. So as you move around, only 10 colliders are ever enabled. Colliders are currently enabled on render visibility, but is it MESH visibility or MESH+BILLBOARD visibility?

    If you had billboard start at 200meters for example, that's A LOT more mesh trees, so I can see where the colliders would be causing more of an issue.

    If you can figure out somethings, like are colliders enabled even if the tree is FULL billboard, that would be awesome.
     
    Last edited: Apr 9, 2015
  10. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    Yeah - that is more or less what I want to be able to assert.

    You are asking exactly the right questions. I will go off and try to discover some solid answers
     
  11. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    1st test: Completely unrelated to the original usage.

    Can it turn particle trees "solid"

    upload_2015-4-9_14-58-28.png

    Dynamic "collision" trunks

    upload_2015-4-9_14-59-12.png

    Yes. Great.

    Actually implementing the code was fairly trivial, it is very self explanatory.
    I did end up ripping out the namespaces and changing a few hard-coded "Parent GameObject" names. nothing major.

    For simplicities sakes I ripped the whole of
    https://gist.githubusercontent.com/...2e083fd40e64a06ab19ebaf3f451edf8/spatial_grid
    and dumped it in
    https://gist.githubusercontent.com/...8ec6075004e2919b83909b007643417/grid_tracking
    and saved that as GridTracking.cs then attached that to my procedural particle tree script

    then reworked the terrain tree position extractor to simply be:
    Code (csharp):
    1.  int treecount = 0;
    2.  ParticleSystem.Particle p;
    3.  
    4.  for (int i = 0; i < PlaceParticles.ParticleList.Length; i++)
    5.  {
    6.  p = PlaceParticles.ParticleList[i];
    7.  Vector3 tpos = p.position;
    8.  
    9.  GridValue gridValue = new GridValue();
    10.  gridValue.position = tpos;
    11.  gridValue.id = "tree_" + treecount;
    12.  gridValue.entityType = GridValue.EntityType.Tree;
    13.  grid.Set(gridValue);
    14.  treecount++;
    15.  
    16.  }
    as you can see it easily walked over a particle system positions list, just as it would have done a terrain tree list, a list of rocks, anything really (which is why I said this was so awesome, not the terrain thing)

    upload_2015-4-9_15-3-50.png

    upload_2015-4-9_15-7-14.png

    I crank it up to 2000 particles to test, seems okay

    upload_2015-4-9_15-16-4.png


    As someone who cares little for the built-in terrain system, I see this as a nice way to spacially track - well - everything.

    I will pull up a test terrain and chuck a few trees at it though, just so we have some test numbers ;)
     
    Last edited: Apr 9, 2015
  12. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    the RemoveOld() method needs a garbage Tidy-up at some point.
    Initially just reworking the foreach and possibly a refactor of one of the collection types to give us free enumeration.

    I am looking at the 5000 hard limits right now since we can have /many/ terrain trees.
    If I get something I'll share.

    I am about to make a mistake here and not direct this at anyone in particular but:
    Feel free to jump in and help with the Terrain Tree testing.

    EDIT: Actually @snacktime can just confirm the points mbowen89 raised. without testing

    EDIT: Ran 7000, it was starting to get a little lumpy.

    here was the modified code I was using as one big lump
    https://gist.github.com/twobob/f7b34a4b404a9cee44da

    3000 seemed a decent mid ground.
    and if you are not too fussy with the light direction those shadows are tamable
    upload_2015-4-10_2-56-16.png
    cheap trees and a confirmation that the system works nicely for desktop.
    Might need a tiny bit more tidying up to be perfect for all mobile.
     
    Last edited: Apr 10, 2015
  13. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Happy to see this is getting some use.

    Ya it could use some performance optimizations. I didn't know in advance where the optimizations would be needed and just wanted to get a working version out.

    So I'm kind of hesitant to put this next one out there. It has a very limited use case but if you actually need it, I'm not aware of anything else out there that currently does a better job. That is the java version that was designed for tracking objects in range on the server side and is highly optimized for space when it comes to bandwidth. It's fully concurrent. Uses protocol buffers for bit packing. Converts floats to integers before sending over the wire, using a set scale so you just multiply/divide to convert. Sends delta's instead of the full position when possible. Uses id substitution so you can effectively use string id's but they only go over the wire when the object first comes into range, and a short integer id is used all other times. Smart enough to resend full data when objects go in/out of range so you can sync coordinates and id's.

    This is the core code. If someone actually has a need for it I can provide the C# class I use on the client,and the protocol buffer message definitions as well. I've been using this for over a year so it's fairly stable, just a bit more complex to get setup and running.

    https://github.com/gamemachine/game...r/src/main/java/io/gamemachine/core/Grid.java
     
    twobob likes this.
  14. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    like, heroic. *puts up hand for c# version*
     
  15. rpg_gamer

    rpg_gamer

    Joined:
    Nov 28, 2012
    Posts:
    214
    Great script.... I get some errors of course about GameManager and PlaceParticles not existing, but also a couple of errors to do with navmeshobstacle:

    Assets/scripts/GridTracking.cs(413,29): error CS1061: Type `UnityEngine.NavMeshObstacle' does not contain a definition for `center' and no extension method `center' of type `UnityEngine.NavMeshObstacle' could be found (are you missing a using directive or an assembly reference?)
     
  16. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    Right, just ditch that line then.

    you don't mention your version. but.

    in the 5.0 release notes

    Improved accuracy and raised limits:
    • NavMeshObstacle supports two basic shapes - cylinder and box for both carving and avoidance.
    which I guess is the "center", without checking

    Either way: It's an optional, additional, navMeshObstacle that can be generated at the tree capsule point. If you don't need it, bin it :) or rework it to use your navMeshObstacle geometries.

    and https://gist.github.com/twobob/0ab2a30ecb92c4627c0d for place particles (pretty sure I used that one or very similar)

    The particles will do that "rotation like a billbard" thing...
    I would probably recommend using this in conjunction with TurboForest for best results.
     
    Last edited: May 6, 2015
  17. rpg_gamer

    rpg_gamer

    Joined:
    Nov 28, 2012
    Posts:
    214
    ah ok. I'm using unity 4.6 on my project still, have not fully moved over to 5 yet.
     
  18. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    twobob, I completely lost track of this thread.

    What exactly did you mean by this? What I was saying was true or false?

    When I look at the physics in the profiler, if you start game with Tree Colliders enabled, there are 528 static collliders. When I start game with colliders disabled, those all go away.

    If I look around, up at sky, whatever, that 528 number doesn't change. If I set tree distance to 0 and billboard at 0 so no trees, the colliders are still at 528.

    Seems like Unity is keeping 500+ colliders alive all the time?

    Not sure how internally it's calculating which ones though.
     
  19. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
  20. DeathRace26

    DeathRace26

    Joined:
    May 26, 2019
    Posts:
    75
    Sorry for asking, is this related to PrepareSceneCullingParameters log in profiler window? Im using speedtrees and from my tests, i have 8 ms delay just because of culling. I have 24 map sectors.. Occlusion culling holes are as as big as possible. I still dont get it. Why trees dont get disabled on nested terrain part that gets occluded? Am i actually culling trees one by one? And what actually means dynamic occluded? Should i disable that? I have trees made into prefabs because for layering which allows camera to go through trees. Could also that have some kind of effect on my framerate? Im thinking about moving to different game engine, because the performance is so horrible. Almost seems like unity is not designed for smaller aa games.