Search Unity

Frame rate drops when too close to object

Discussion in 'General Graphics' started by shakozzz, Aug 9, 2017.

  1. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    I am using a bunch of trees in my scene. Each tree consists of a container which holds the following items:
    - A static game object which holds the tree's mesh and material. This is set to only cast shadows such that all that is visible of the tree after a bake is its shadow.
    -A dynamic animated tree object with a skinned mesh renderer. This is the tree that actually appears in the game. It has an unlit shader on it and is, therefore, not included in the bake.

    A single tree in my scene works fine. However, I start experiencing issues when the number of trees increases (In my case to 30 trees). What happens is that once I approach a tree and get close enough to it, the game starts lagging. As I move away from the tree everything is fine again.

    I tried overriding the tree texture for Android to have the max size decreased to 32 to no avail. All I end up having are very pixelated trees, but no performance gain. This leads me to believe the problem lies somewhere else.

    This is what the static tree's mesh looks like:
    upload_2017-8-8_21-53-55.png

    This is the animated tree's mesh:
    upload_2017-8-8_22-0-50.png

    Here's one of the textures:
    upload_2017-8-8_21-54-48.png

    I recall having read somewhere that transparency is costly. Around the trunk there is a lot of transparency due to it being much thinner relative to the mesh than the leaves. What further supports my suspicion is that the lag only occurs when looking at the trunk. It doesn't occur when looking at the leaves or the ground for example. Could this be the reason why it's lagging? Could it be calculating the transparencies?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    This is the result of overdraw.

    When people say "transparency is costly" they don't mean the areas that are transparent, they mean the entire surface that can potentially be transparent (including the opaque areas) are costly. It's not really even that the transparency in itself is costly either.

    When a mesh is opaque the GPU has several techniques it can use to ignore surfaces that can't be seen in the final image. They're still not free, but it can significantly reduce the cost of occluded surfaces. The basic technique here is depth sorting and early z rejection. When each opaque object is rendered it first tests to see if it's the closest object that's been rendered for that pixel, and if something else already rendered closer, the GPU doesn't render it.

    Transparent objects they can't do this since you can't as easily reject pixels just from seeing if something has rendered closer since the nature of transparent objects is you can see more than one at time if they're overlapping. Instead the individual meshes are roughly sorted furthest to closest and rendered one after another.

    The TLDR is if you've got multiple trees in a row, even if you can't see them all in the final image, you're paying the full cost of rendering each tree individually. In your case since your trees are 4 intersecting planes when you're close to a tree all 4 of those planes might be taking up a significant portion of your screen, and every other tree behind it is also 4 more overlapping planes presumably taking up a decent portion of your screen as well.

    Some PC devs will tell you "just use an alpha test / cutout shader!" This works for desktop hardware, but on most mobile hardware using alpha test is more expensive than using a transparent / alpha blending shader. While cutout shaders on mobile will do depth sorting, and z rejection, they won't necessarily skip rendering the hidden pixels but instead still render them and then discard them.

    The real answer is you'll want to modify your meshes to be as tight around the texture as possible. Some people even split up the fully opaque areas and the transparent areas into separate meshes. There are some tools to do this automatically for you with sprites, but I don't know of any for 3d meshes.

    https://www.assetstore.unity3d.com/en/#!/content/37599
     
    shakozzz and theANMATOR2b like this.
  3. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    So I'm not entirely sure I completely understood what you were suggesting. Here's the new model after changing it based on my understanding of your reply. The problem still persists with this model.
    upload_2017-8-9_18-19-41.png
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    In the editor's scene view enable overdraw (click on the text right under the Scene tab that says "Shaded" and change it to "Overdraw"). The brighter the pixel the more overlapping things are being rendered, and the more expensive it is to render. The more of your screen that's bright the slower your game will run.
     
  5. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    I see. I can't see any way to optimize my model further to achieve this result. Do you know of any way to maintain the look I'm aiming for while at the same time achieving what you are describing.
    This is what I want to achieve:
    upload_2017-8-9_19-53-51.png
     
  6. ifurkend

    ifurkend

    Joined:
    Sep 4, 2012
    Posts:
    350
    It depends on your target platform (mobile I assume) and how close those trees will be zoomed into in your actual gameplay. A compromise would be a plain sprite billboard (vertical billboard) without any cross plane. As long as you keep overdraw low, alpha blended shader is fine.

    Or as a last resort: Change the art direction completely to use opaque polygonized tree model instead.
     
    theANMATOR2b likes this.
  7. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    That is what I was doing prior to discovering the low vert, high visual reward approach of planes with transparent textures on them as shown in the images.

    I'm developing for the Gear VR. In the game, the player can walk around in a confined environment, i.e. a forest, and get as close as can be normally expected to the trees. So far I've been using Legacy Shaders/Transparent/Cutout/Soft Edge Unlit.
    For trees that are used as boundaries in my scene I've been using quads with the tree texture on them and a billboard script. How similar is that to what you describe here:
     
  8. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    Am I correct in thinking that this is similar to occlusion culling? (Which I am btw not using in my game since it caused a performance drop)
     
  9. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    Fill rate is the killer on mobile, always. In VR its just worse.

    The best route is probably a new strategy for the trees entirely.

    If you're hellbent on planes... First, cut the geometry SUPER CLOSE to the outline of your tree (as @bgolus recommended). Second... in your shader, where you do the dot product to fade out odd angles you don't want to draw you should also push invisible planes super far away so they aren't in view in the vertex program. But again, this is just an optimization for what ultimately is not a good idea (soft alpha planes everywhere)
     
    Peter-Bailey likes this.
  10. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    I've made my models very close to the design suggested this post which is to some degree in accordance with the suggestions made by yourself and @bgolus . As for shaders, I'm not using any custom shaders as I don't know the first thing about writing shaders. I'm using Mobile/Unlit for the blob and Legacy/Transparent/Cutout/Unlit for the planes. This is what I'm working with right now:
    Final.png

    As long as the camera isn't too close to these trees there is no lag. So I placed box colliders around them to prevent this from happening. A work around not a solution I know, but it's the best I can do at the moment.
     
  11. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    There are massive areas of those planes that are completely transparent. You need to cut the geometry so all of transparent area is not part of your mesh at all and therefore never rendered.

    When you are close to the tree, your entire screen is covered by one or more of those clear planes, which means you're rendering everything multiple times to determine every screen pixels value times two cameras.

    Also I can't believe you're not hiding glancing edges, that'd be a relatively easy first shader.
     
  12. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    Could you please elaborate on this further? I'm curious as to how this would change if I was close to a completely opaque tree with occlusion culling turned off.

    Also, anywhere I can learn more about glancing edges? I'm hearing about them for the first time :)
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    A thin tree won't do anything for occlusion culling. Occlusion culling relies on quite large (minimum of 5 units?) objects to be occluders. A tree that's 0.1 units wide won't be doing any occluding at all. So in the case of a forest enabling or disabling occlusion culling will likely have little to no difference as there are no objects large enough to be considered as occluders.

    In other words standing super close to a tree with the opaque trunk filling the entire view, all the other trees are still being rendered behind it.

    Occlusion culling is something that's happening on the CPU side of things, rejecting entire objects from being sent to the GPU. On the GPU there's additional "culling" happening which they can be smart enough to skip rendering pixels behind fully opaque objects that have already been rendered. This is called "early z rejection". It isn't perfect though as it's highly dependent on the order in which objects render in, it doesn't necessarily reject pixels behind very thin objects or "grainy" alpha testing, and there's still some cost to rendering the pixels that are rejected.
     
    shakozzz likes this.
  14. shakozzz

    shakozzz

    Joined:
    Mar 1, 2017
    Posts:
    60
    I don't know why I'm not getting this, but wasn't this exactly the problem with transparent objects that using opaque objects is supposed to solve? If objects are still being rendered behind opaque objects then how does this differ from using transparent objects?
     
  15. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    There are multiple points during rendering where things can be culled or occluded with varying potential costs and savings for each.

    Here's a list of some of those in the order they usually occur.

    1. Is the game object / component enabled. Pretty simple one.
    2. Frustum culling. Basically it's the object's bounds with in the "cone" of the camera's view, and with in the near and far clip.
    3. CPU side occlusion culling. Optional process usually only occluding using large solid objects. For example a wall with no holes, windows, or doors can occlude, but one with a small hole might not. Best used for indoor dungeons / hallways / etc. The cost can be quite high to use depending on the setup.
    4. GPU hierarchical z / early z rejection. When any object is rendered on the GPU it will calculate the pixel depth after it runs the vertex shader. If a pixel had already been written to closer to the camera than that object it can skip rendering the fragment shader. Different GPUs implement this in different ways, and different shader features may prevent or skip this. For some GPUs, especially mobile GPUs, using an alpha test style shader can disable some additional optimizations used in the z buffer (what's used to keep track of the closest depth for reach pixel), making early rejection much slower for anything rendered after that. The result is a full screen quad using alpha test on mobile can make everything significantly slower to render, regardless of if it's opaque or transparent.
     
    sylon likes this.