Search Unity

What is the best way to add a lot objects(grass)

Discussion in 'General Graphics' started by IlisanVlad, Aug 17, 2020.

  1. IlisanVlad

    IlisanVlad

    Joined:
    Dec 3, 2017
    Posts:
    30
    I am doing a 3D mobile game and I need to add a lot of 3D grass instances but the performance is a bit low. What I initially tried is to place all the grass objects separately in blender and export it to unity.
    But this gives me around 4000 batches. To make the game playable I am using a script with custom camera clipping and occlusion culling.

    What I am trying to figure out now is how to batch the grass up so the engine doesn't see every grass instance as a different mesh even though the one near it is exactly the same.

    The unity built in static batching won't really work for me because there are 5 different grass meshes and some have extra materials. And I don't want all the grass to get batched together because they are spread out and the player will never be able to see all of them at once.

    Update: I can join the grass in a single mesh in blender and this reduces the batches.
    Should I also use LODs and is this the best way?

    If I find a good answer I will apply this to all the rocks, trees etc


    Thank you and have a good day ;)
     
    Last edited: Aug 17, 2020
  2. BrandyStarbrite

    BrandyStarbrite

    Joined:
    Aug 4, 2013
    Posts:
    2,076
    I was now going to tell you to do that. Lol!


    Yeah, that might be a good idea for rocks and trees. But that might cause some performance issues too.

    Note: If there are some trees that are far off in the distance, in areas that the player is not supposed/never supposed to reach, make some billboard trees for those areas. Or png trees with alpha transparency on them. Or a long cluster of trees on a billboard.
     
    Last edited: Aug 18, 2020
  3. Aviryx

    Aviryx

    Joined:
    May 30, 2020
    Posts:
    97
    Joining all of those objects together is a terrible idea. I know it might seem logical (1 object = less to render) but that's not really the case... especially with grass/trees/etc. You completely destroy Unity's ability to help you out. Goodbye frustum culling and occlusion culling.

    What you should do is enable GPU instancing on the material(s) used for your grass. In fact this is what Unity suggests in the documentation - specifically mentioning using static batching for things like trees/grass as a terrible idea.

    https://docs.unity3d.com/Manual/GPUInstancing.html

    "Use GPU Instancing to draw (or render) multiple copies of the same Mesh at once, using a small number of draw calls. It is useful for drawing objects such as buildings, trees and grass, or other things that appear repeatedly in a Scene."

    Also, imo, LODs for grass/trees is pointless. The amount of switching for that many objects basically degrades anything decent you get from using LODs.
     
    andreiagmu and Gravesend like this.
  4. IlisanVlad

    IlisanVlad

    Joined:
    Dec 3, 2017
    Posts:
    30
    I wasn't going to batch all the grass in one mesh. Would it help if I batch a few grass patches thogeter in a size that the player will be able to see with my current grass render distance?

    I have enabled the GPU instancing on my grass materials. Does it matter if there are 9 different grass types? And dthey have a combination of the same 3 materials? Some have the first material, other have 2,3 or 1,3,2 etc.
     
  5. Aviryx

    Aviryx

    Joined:
    May 30, 2020
    Posts:
    97
    Theoretically yes... but only to a certain point. Frustum Culling is the process of culling anything outside of the camera frustum. Unity does this automatically. However, this becomes an issue with large (dense) meshes that appear both inside and outside of the frustum as they will not be culled. So you need to find a balance with how big an individual cluster of grass is compared to how much of it sits inside the frustum.

    You could try seeing how many grass meshes you can fit on the screen (inside the camera frustum) and then that would be your "maximum size" for a single mesh of grass. Although it might not be worth the effort as GPU instancing can be really good in terms of performance (the add in frustum culling/occlusion culling and other optimisation techniques). Bear in mind that manually combining smaller meshes into larger meshes means you might miss out on performance gains from occlusion culling too. Especially since grass is likely to be occluded quite often.

    The only difference that it makes will be an increase in draw calls but it will still be x1000 better than static batching. Static Batching tends to be for larger, irregular-shaped geometry that sits inside of the view frustum. The reason to use static batching on those items are because it's more efficient in terms of reducing calls to change the render state having to rebind textures to meshes.

    If you have 9 different grass models but each model has the same 3 materials then if you were to place one of each grass type - you would get 9 draw calls. GPU instancing can not be applied to models with different geometry. The usae of it is to dramatically increase workload when trying to render hundreds of the same object (trees/grass/etc)

    so no matter how far you push it (50 models with 30 materials) GPU instancing is still far more efficient than static batching when it comes to that sort of stuff.

    What you will find is that the only performance issue is when you switch between different models in the render pipeline. (Consider rendering from "farthest away" to "closest to the camera") ... but it's still the most efficient way.

    4 Draw Calls
    Grass Type 1 [First batch]
    Grass Type 2 [Previous batch is broken because it's a different model]
    Grass Type 2 [Able to be batched with previous draw call]
    Grass Type 1 [Previous batch is broken because it's a different model]
    Grass Type 2 [Previous batch is broken because it's a different model]

    2 Draw Calls
    Grass Type 1 [First batch]
    Grass Type 1 [Able to be batched with previous draw call]
    Grass Type 1 [Able to be batched with previous draw call]
    Grass Type 2 [Previous batch is broken because it's a different model]
    Grass Type 2 [Able to be batched with previous draw call]
     
  6. IlisanVlad

    IlisanVlad

    Joined:
    Dec 3, 2017
    Posts:
    30
    Alright then. Thank you so much.
    I've ditched static batching and changed my materials to use gpu instancing.

    Can I use this on moving objects that will mostly stay in the same areas?

    And if you know, I have an almost offtopic question. I am using custom camera clipping distances for grass and other object. Will frustum culling occur if the object is in my view but I can't see it because of the clip distance. I am aking this because I want to know if there is an extra step for a better performance gain since the custom clip script was way too simple to write and fix this problem and I am not used to this :))
     
    Gravesend likes this.
  7. Aviryx

    Aviryx

    Joined:
    May 30, 2020
    Posts:
    97
    Yes (and it doesn't matter if they stay in the same areas or are really far apart). In fact it would be the best method as you can not static batch a moving object.


    No as the camera skips rendering that geometry entirely.

    https://docs.unity3d.com/ScriptReference/Camera-layerCullDistances.html

    "Normally Camera skips rendering of objects that are further away than farClipPlane."
     
  8. IlisanVlad

    IlisanVlad

    Joined:
    Dec 3, 2017
    Posts:
    30
    I haven't learned something so useful and so quick in a while. Thank you so much ;)
     
    Aviryx likes this.
  9. colin299

    colin299

    Joined:
    Sep 2, 2013
    Posts:
    181
    Gravesend and Dawdlebird like this.
  10. Dawdlebird

    Dawdlebird

    Joined:
    Apr 22, 2013
    Posts:
    88