Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Provide per-instance data to the shader.

Discussion in 'Shaders' started by afrobeer, Aug 30, 2014.

  1. afrobeer

    afrobeer

    Joined:
    Oct 15, 2012
    Posts:
    2
    I couldn't find any info related to this concrete thing, and I come from OpenGL/GLSL where you are able to do it, so I'll explain my problem.

    I have a geometry with a material, and I need to provide an uniform to the shader attached to that material with a different value controlled by myself for each instance of this geometry. Let's say I have 100 enemies and I need to provide a shader uniform's value specific for each enemy in the material. This could be done if I could set values from script before drawing, but don't know if this is even possible.

    Any suggestions? Thanks in advance!
     
  2. CatchCo

    CatchCo

    Joined:
    Nov 27, 2012
    Posts:
    17
    The easiest way to approach this is to use a MaterialPropertyBlock for each renderer that you want to customize.

    The general idea is this:
    1. For each renderer that you want to customize create a MaterialPropertyBlock
    2. Set a value on that property block
    3. In each Update assign that property block to the renderer via renderer.SetPropertyBlock (the documentation doesn't explicitly say that it needs to be done every frame but it doesn't work otherwise).

      Note: No need recreate the MaterialPropertyBlock in Update. You can use the same one over and over each frame
    One issue you will note is that it breaks batching, so use this method wisely.

    Another important concept to remember is that in order to remove the property block you must call renderer.SetPropertyBlock(null).

    An example of how to use MaterialPropertyBlocks in general can be found here: http://docs.unity3d.com/ScriptReference/MaterialPropertyBlock.Clear.html
     
  3. afrobeer

    afrobeer

    Joined:
    Oct 15, 2012
    Posts:
    2
    Thank you for the quick response!

    I'm glad to hear this is possible, but a bit horrified about hearing it breaks batching. I'm planning on making a quick research on Unity's batching/rendering documentation, just to know if is there any possible way to not to break batching.

    Judging from your comment, I can think it's impossible, but this breaks a bit of work I've already done. I supposed this would be trivial to achieve, since it is in OpenGL with instancing.

    Anyway, let me know if you're sure that there's no way to achieve batching using property blocks. Something that flies over my mind is setting a property block that is an array, but I should have an index in the shader for each instance, so I'm at the same point.

    Thanks!



    IMPORTANT EDIT: Looking inside the docs about batching (http://docs.unity3d.com/Manual/DrawCallBatching.html), it seems that skinned meshes don't support batching, and I'm using skinned meshes, so I guess I couldn't use batching anyway. In this case, the problem is solved, I won't have batching anyway, so property blocks are the way to go.


    IMPORTANT EDIT 2: Since I have to use Graphics.DrawMesh and there is no way to use it with skinned meshes, I'll just discard this approach. (http://docs.unity3d.com/ScriptReference/Graphics.DrawMesh.html)
     
    Last edited: Aug 30, 2014
  4. CatchCo

    CatchCo

    Joined:
    Nov 27, 2012
    Posts:
    17
    No, no. You don't need to use it with Graphics.DrawMesh. That was just an example. The way I use it is the method I mentioned above. I use renderer.SetPropertyBlock quite a bit without ever using Graphics.DrawMesh
     
  5. monkeyscience

    monkeyscience

    Joined:
    Dec 13, 2011
    Posts:
    705
    What kind of behavior do you see when you don't call it every frame? I don't think I call it every frame, but I've also had trouble with it on OSX desktops.
     
  6. CatchCo

    CatchCo

    Joined:
    Nov 27, 2012
    Posts:
    17
    Ok, since I've posted this things have changed I don't believe it needs to be called every frame (if the values remain constant?). At the time of writing this I was writing shaders who's input values were changing each frame. Something I would like cleared up is this: Do I have to update the renderer (with SetPropertyBlock) if I change one of the values on the propertyblock?

    This is a particular section of the docs that could use fleshing out and examples.

    Note, I'm primarily developing for OSX.
     
  7. hicks

    hicks

    Joined:
    Nov 24, 2013
    Posts:
    13
    A bit late but will post it anyway.
    @CatchCo I think the property block is not bound to the renderer. It's just a data class. So, if you want to change a value, I think you need to set the property block of the shader again.

    @afrobeer GPU batching with skinned meshes is possible but it has some drawbacks (like not all instances can have different animations). You would need to use Graphics.DrawProcedural() in combination with your own shader where you have the per-instance data stored in buffers. However, this is some pretty advanced graphics programming and not easy to get it working. If you're still interested this thread addresses the topic in more detail: http://forum.unity3d.com/threads/gpu-mesh-instancing.19670/
     
  8. MattFS

    MattFS

    Joined:
    Jul 14, 2009
    Posts:
    219
    I know this is old... but I feel the need for this feature as well. ProperyBlocks and setting material values per Update isn't the key... it's to have something like a vertex stream or buffer that can be index based on Instance ID.

    A good example is how you can lightmap several instances of a gameobject and they still batch BUT have a unique index offset into the lightmap atlas. So they all have unique lighting but are the same shader/material... its not like you set 15 lightmap textures for 15 barrels.

    This can be used for lots of things... as per the OP, allt he uniform index's could be written to a texture that that each instance looks up using its ID as an offset. ....etc This allows variation without breaking batching.