Search Unity

DrawMeshInstanced with MaterialPropertyBlocks

Discussion in 'General Graphics' started by KYL3R, Mar 8, 2018.

  1. KYL3R

    KYL3R

    Joined:
    Nov 16, 2012
    Posts:
    135
    I just played a bit with this.

    I have: Trees (Gameobjects), that I can interact with, I can cut them down, search them via OverlapSphere etc.

    I want to: Draw these trees instanced, with different colors (MaterialPropertyBlocks)

    I tried: checking "enable gpu instancing" in material - they get rendered in one draw call. (but all same color)

    I tried DrawMeshInstanced, where I need to declare the mesh and an Matrix4x4 array, containing all the position,rotation,scale for the trees. -> I need to manage drawing my trees in one spot in code.
    Sure, I can collect all of them, disable the meshRenderer and draw them with Graphics.DrawMeshInstanced instead of using the meshRenderer. But:

    1. Who is taking care of culling now? All the examples on Youtube render 100k cubes or units, but they are all in the cameras view. These "tech demos" or "examples" are not close to a real-world application.

    2. The Documentation says: "You can draw mesh instances at once without the need of gameobjects" - fine, but I have gameobject. Is there a nicer way in this case?

    2.1 Are there any plans to make the MaterialPropertyBlocks a component, so I can just attatch it to my gameobject? Or maybe put a propertyblock to the meshRenderer?

    I mean, the checkbox "enable gpu instancing" is a very easy step. Adding MaterialPropertyBlocks seems to complicate things a lot.

    3. Another, relatively easy way would be: I attach a "treeRenderer" script to my gameobjects. I take the mesh from the meshRenderer and call Graphics.DrawMeshInstanced, but the array only contains that one element. I do this to all trees, so I end up with 60 trees, and 60 scripts on them, calling "DrawMeshInstanced". If I do this, they are not drawn together. Why isn't Untiy just waiting for all Update() calls to finish, then in the Render Loop, check if the "DrawMeshInstanced" calls refer to the same mesh? That way I would not need to handle the Rendering in one script, just keep my gameObjects where they are and replace the meshRenderer with that.

    Am I doing something wrong? Are there any ways to make this more easy?

    When I check "enable gpu instancing" it renders them in batches, why can't I just set the MaterialPropertyBlock in OnPreRender or something?
     
  2. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Hi.

    1. When drawing with DrawMeshInstanced you need to handle the culling.

    2. The upside of instanced rendering like this is that you do not need the GameObjects. This saves you CPU time that unity would use to process the hierarchy of all the GameObjects

    2.1 The MaterialPropertyBlock is a object that you can assign to MeshRenderers if you want. Or pass as a parameter when using DrawMeshInstanced.

    Enabling the instanced checkbox allows unity to internally batch together the meshes using instanced materials. But it is also needed when using the manual API

    3. Calling DrawMeshInstanced multiple times with one instance will not speed up anything. This is basically the same as using Graphics.DrawMesh. To get any speed improvements you need to batch them together in lists of up to 1023 items.
     
  3. KYL3R

    KYL3R

    Joined:
    Nov 16, 2012
    Posts:
    135
    Okay, thanks.

    Then how would I handle units? My Trees don't move until I cut them down, but Unite Austin showed the RTS demo from Nordeus. They had 100k units. Joachim told us about ECS, memory layout etc. But he also mentioned there are no gameobjects, and that the units are drawn using gpu instancing. How do you handle the movement and interaction of those, without gameobjects in general?


    Thanks, I'll try to assign it to the MeshRenderer.

    I'm aware of that. Just to try things, I disabled it and FrameDebugger told me they could not be batched because of that.

    That's what I found while testing, too. I just wonder if Unity can automate this step for us.
     
  4. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Think of each tree as a struct that holds the info about the tree. The matrix4x4, tree type etc.
    These you could add to quadtree or other structure to do selection/culling.

    then each frame you do a selection based on the current camera frustum. Build your render lists and do the DrawMeshInstanced calls to render.

    for cutting down trees you would have to have your own logic to select the instances from the quadtree and remove or change type to render a tree trunk instead.

    One additional thing to think about would be tree shadows. You would have to manage selecting the invisible trees that could cast shadows visible in your frustum.

    I used an approach where I instantiate colliders for the trees close to the camera. These exist as gameobject only when close and could be used for interaction/selecting trees in the game.

    Lennart
     
    Opeth001 likes this.
  5. KYL3R

    KYL3R

    Joined:
    Nov 16, 2012
    Posts:
    135
    Thanks a lot. For my scenario, adding the MaterialPropertyBlock to the renderer, in combination with enableGpuInstancing on the material, works like I wanted it.

    I now have instanced meshes with different colors. Leaf trees, fir trees and crops are different meshes, but have the same (instanced) material. That makes at least one draw call per tree-type, but in general the instancing works.



     
  6. Opeth001

    Opeth001

    Joined:
    Jan 28, 2017
    Posts:
    1,117
    Hello guys,
    im not familiar with rendering stuff :p
    do you guys know how to :
    1) pass Lightmap Data as a parameter to the MaterialPropertyBlock ?
    2) is it possible to set Cast shadow as a property to the MaterialPropertyBlock ?

    also what's the best approach for mobile, using DrawMeshInstanced or DrawMeshInstancedIndirect and why ?

    Thank you in advance!