Search Unity

Question Options for clipping parts of object at certain distance from camera - performance

Discussion in 'General Graphics' started by MMeyers23, Sep 21, 2021.

  1. MMeyers23

    MMeyers23

    Joined:
    Dec 29, 2019
    Posts:
    101
    Hi,

    I have a grass shader that is applied to a terrain object, which is quite large, resulting in lots of vertices on screen at once. For better performance, I want to clip the grass when it is a certain distance from the camera. Since the grass shader is applied to the terrain, I cannot clip the entire object, I need to clip the object where it is further than a certain distance from the camera. The two options I can think of would be to render the object on a separate camera with a shorter far clip plane, or else inside the shader I could clip the shader for vertex positions that a certain distance from the camera position.

    Which would be more performant? If part of the object is clipped (by shader or camera) are the vertices still rendered? If so, is there a way to not even render the particular vertices of an object that are a certain distance from the camera? I admit I could probably research this on my own but thought someone might be able to give a quick answer off the top of their head.

    Thanks!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    How is said grass being rendered? Is this a shader that's generating grass with a geometry shader? Are they manually placed meshes? Procedurally instanced meshes? Are they the Unity terrain detail meshes / sprites?

    If they're a geometry shader, don't emit the grass blade geometry beyond some distance you pass to the shader. You'll also likely want to frustum cull in the shader before emitting the grass so you're not spending time generating grass that will never appear on screen. I would expect many grass shaders from the store to have these options already.

    If they're manually placed meshes, you can give them an LODGroup and use an LOD with the renderer disabled at a certain distance. Or you can use the culling group API to manually disable meshes outside of a set range.

    If they're procedurally instanced meshes, the culling group API is also a good option. Or if you're generating the instance positions in a compute shader, cull there.

    If you're using the Unity terrain system's detail meshes feature, that has draw distance options.
     
  3. MMeyers23

    MMeyers23

    Joined:
    Dec 29, 2019
    Posts:
    101
    Hi bgolus,

    Unfortunately, it is none of the above. I wanted to use Amplify Shader Editor and couldn't figure out how to edit the generated code to convert it into a geometry shader (they currently don't support geometry shaders) so I went with a creative option... I apply a shader to my terrain object which takes all the vertices and rotates them in such a way that "billboards" are positioned perpendicular to the surface, and I apply my alpha texture to these billboards to make the grass blades. Its more involved than that but that's the gist. So, its all actually one object, and it is not generated geometry. The downside obviously is that all the vertices exist at all times, but I was wondering how much I am saving in performance by clipping beyond a certain distance. Im sure there is performance cost for have to calculate vertex positions even if they are not being rendered? Im just wondering how wasteful this is.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    ASE generates Surface Shaders when using the built in pipeline. You can't use geometry shaders with Surface Shaders. In fact it's explicitly not allowed.

    Even if ASE didn't generate Surface Shaders, Geometry shaders are antithetical to node based shader editors as their output is unbounded. Every (real time) node based shader editor I've ever seen does not support geometry shaders, unless they have some prebuilt systems for it that you can hook into, and Unity's Shader Graph does not.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Some, but not much. For grass in the distance a majority of the cost is in having the vertices there to begin with. You need to cull them from every existing at all for there to be a real performance improvement. You can collapse them down to a single point, like (0,0,0), and they won't ever get drawn by the fragment shader. That's about as good as it gets for culling them once you're to the vertex shader if the geometry isn't being generated later. You can try that and you might see a very minor performance improvement, but it won't be much.

    The "best" option in this case would be to chop up the ground into a grid of chunks with and without the grass billboards and use LODGroups to hide them if they're far away.
     
  6. MMeyers23

    MMeyers23

    Joined:
    Dec 29, 2019
    Posts:
    101
    Thanks bgolus for the insight. I was hoping to avoid breaking up the terrain, but I guess its not really that much work. Definitely worth the performance increase.
     
  7. MMeyers23

    MMeyers23

    Joined:
    Dec 29, 2019
    Posts:
    101
    Following up on your advice, I am having trouble with Unity's LOD system. The LOD Group component in the inspector is easy enough to work with, but the problem is that its not feasible to manually adjust each terrain piece's LOD properties by hand. I need to do it by script because of the number of objects. However, I can find absolutely nothing helpful to guide me through Unity's LOD API. I am simply looking for a script that would allow me to set values for the screenRelativeTransitionHeight for each LOD. That way I can run the script on every piece of terrain to give them each the same behavior. Can anyone help me understand how to write this script?

    Thanks!