Search Unity

TrailRenderer with gpuinstancing failed

Discussion in 'General Graphics' started by ShinomoriRinnon, Apr 21, 2021.

  1. ShinomoriRinnon

    ShinomoriRinnon

    Joined:
    Apr 13, 2020
    Posts:
    2
    Recently i have tried to make a scene with a large amount of trail renderers.

    The materials be used in different trails will have slightly different parameters. So i tried to using MaterialPropertyBlock in C# code, and INSTANCING_BUFFER in shader code.

    The result is GameView showed correctly, but batch failed(each renderer uses one drawcall).

    Things i want to know:
    1. is there any way to batch something using trail renderer with different material parameter
    2. will TrailMaterial + particleSystem solve my problem( not success yet
    3. any other suggestion to solve it

    Thanks.
     
    Last edited: Apr 21, 2021
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    No.

    No.

    The solution is, unfortunately, you can't use any of Unity's built in systems if you want instanced trails.

    First an explanation of why instancing isn't working. Unity's trail renderer generates a new custom mesh for each trail, for each frame, for each camera that can see the trail. It does this on the CPU and uploads those meshes to the GPU.

    Instancing only works if the mesh being rendered is exactly the same mesh for every single instance. Since every single trail is a unique mesh, they can't be instanced.

    So the solutions are:
    1. Just leave them as individual draws and don't worry about it. If you're still able to keep the framerates you want, you really don't need to do anything else. Unity's trail renderers are quite efficient at what they do, even if they do end up being individual draws.

    2. Use a custom instanced particle renderer. For a (non-Unity) game I worked on we used 100% instanced particles for everything, including trails. However trails where a big lie, and were really just long chains of quad particles that where lined up to go from one particle's position to the next particle's position. There was a little bit of code to tweak the connecting vertices so they'd line up with the next particle. The basic idea is you create a list of points (position, color, width, etc.) as a compute buffer or arrays, and then render it using Graphics.DrawMeshInstanced with a shader that makes use of the current point + the next point in the structured buffer to align the quad and end vertices as wanted. The ugly comes when needing to support multiple trails you need to join those lists together, probably with a "dummy" point or two in between the shader knows to skip (like having an additional instance offset per particle, or just a "if this value is set in either point, draw the quad at 0,0,0").

    3. Use a compute shader to construct all of the trail meshes yourself as one giant mesh, and render that (using something like Graphics.DrawProceduralIndirect). This is very similar to the above option, in that you'll need to have a list of points you construct and hand off to the GPU. But instead of instanced quads the compute shader would generate a mesh and then render that. The benefit of this option is you reduce the number of duplicate vertex calculations nearly in half. Or it can be used to do much more interesting things to the line, like smooth the trail by adding in more points by calculating a bezier curve between those originally passed to the GPU, or handling sharp corners more gracefully. The downside is it's a lot more work.
     
    zhuhaiyia1 likes this.
  3. ShinomoriRinnon

    ShinomoriRinnon

    Joined:
    Apr 13, 2020
    Posts:
    2
    Thanks for your reply.

    It seems that I have to pack up the meshes and shader parameters, then using Graphics.DrawMeshInstanced for setting up GPU's data buffer once for all. Is that right?

    -----------------------------------
    -----------------------------------

    Furthermore, here comes a new problem that I'd like to draw the trails with transparent materials.

    My scene has a amount of transparent material using 2D texture, and I have set up renderer's property "sortingOrder" to manage the merge result.

    I want to know If i can let my drawCommand be executed between these renderers without adding a new Camera which seems to be unnatural.