Search Unity

Passing a property to a Shader without breaking batching

Discussion in 'Shaders' started by LorenzoNuvoletta, Nov 21, 2015.

  1. LorenzoNuvoletta

    LorenzoNuvoletta

    Joined:
    Apr 28, 2014
    Posts:
    54
    In the case you have multiple GameObjects with the same custom Shader:
    How can I preserve batching if I pass a dynamic property to the material of one of those objects (for example doing something like renderer.material.SetFloat( "_MyPropertyName", 2f ) ), without using sharedMaterial.

    Thanks!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Short version: You can't.

    Longer version: Unity supposedly has some utilities with MaterialPropertyBlock that it uses internally for reducing draw calls for objects placed via the terrain painting... but this doesn't actually reduce the draw counts or allow batching in the general case and it remains a bit of a mystery what it's doing internally.

    What you can do is manually batch objects and store the unique parameters in each object's vertex colors or extra UV channels. If this is something that needs to change frequently this obviously doesn't work well, but it's the option we've got.
     
    furquijos likes this.
  3. LorenzoNuvoletta

    LorenzoNuvoletta

    Joined:
    Apr 28, 2014
    Posts:
    54
    Thanks bgolus, could you give me an example using the extra UV channels? Also how can I make sure those are not used internally by Unity?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    UV1 (aka TEXCOORD0) is usually used for texturing, and UV2 gets used by Unity if the object is using light maps. UV3 and UV4 (TEXCOORD2 and TEXCOORD3) are almost never used.

    So you have a mesh, you make a copy of that mesh and use SetUVs(3, someVectorList) to set the values. Combine multiple meshes manually using CombineMeshes. Then in the shader get TEXCOORD2 in the vertex shader and pass them onto the pixel shader.

    http://docs.unity3d.com/ScriptReference/Mesh.SetUVs.html
    http://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html
    http://docs.unity3d.com/Manual/SL-VertexProgramInputs.html

    When using SetUVs you'll actually need to make a list with as many elements as the mesh has vertices and set them all to the values you want the shader to read. If you need more than 4 values you can use UV4 or do float packing. I think there's even some utility functions in the Unity cginc files for packing 4 lower precision floats into 1 float value (like a color).
     
    LorenzoNuvoletta likes this.
  5. LorenzoNuvoletta

    LorenzoNuvoletta

    Joined:
    Apr 28, 2014
    Posts:
    54
    Thanks! Would this work only with a Mesh Renderer or also with a Sprite Renderer?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Mesh only. Sprite renderer doesn't have direct access to the UVs. For that I think you can only reuse the sprite color as alternate parameters. It's not that the sprite renderer is incapable of this, it just doesn't give you access as is. Internally it's working almost identically to the method I described, just generating quad meshes instead of copying an existing mesh and combining.
     
  7. LorenzoNuvoletta

    LorenzoNuvoletta

    Joined:
    Apr 28, 2014
    Posts:
    54
    Ok thank you very much! I am wondering if it's worth not using 2D Sprite and go directly to Mesh Renderer just for this.
     
  8. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Like others have said - if you start using different per-instance/material shader parameters, then we can't do batching. Since, well, the parameters are different, and this means that somewhere some code needs to change them in between rendering the objects. Which means the objects can't be drawn in the same go.

    This will get somewhat possible once we implement "draw call instancing", where on capable GPUs (that support instancing), and shaders that properly declare "this will be per-instance data", you'll be able to setup something like a MaterialPropertyBlock with an array of per-instance values and they all would be rendered in one draw call. Or something like that. Currently not sure when that work will be finished, maybe Unity 5.4 or 5.5. (hmm, looks like that is not even on the public roadmap yet, will poke folks to add it)
     
    SiliconDroid, Sebioff and bgolus like this.
  9. Infinite-3D

    Infinite-3D

    Joined:
    Jan 5, 2020
    Posts:
    39
    I know this is a really old post, but did anything come out of this?
     
  10. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550