Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Using MaterialPropertyBlocks with ShaderGraph shaders (help wanted)

Discussion in 'Shaders' started by Rienhl, Mar 27, 2019.

  1. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    Hello!

    I'm trying to use material instancing with a shader created with ShaderGraph. I don't seem to find the way to set any property to be used in a MaterialPropertyBlock.

    I tried compiling the shader's code, adding the [PerRenderData] attribute before the property declaration but t doesn't work. Also, it looks like it can't be saved at all since when I open the code again, my [PerRenderData] attribute gets deleted.

    How should I approach this?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    This doesn’t do anything. It’s singular purpose is to hide the value in the inspector. It’s a legacy setting related to the Sprite Renderer and even there is unnecessary. There’s nothing you need to do to make material properties work with material property blocks, they all work by default.

    The one thing with Shader Graph is the name of the material property variable isn’t obvious as the name you set is just the display name, the actual variable gets an auto generated random string like _Vector_H264BBQUSB. Expand the property in the blackboard and change the reference name from the aforementioned gobblygook to something sensible, like _MyVector.
     
  3. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    Thanks for the answer man.

    So, in this post the author says
    . If this is the case then I don't see the point of using the MaterialPropertybLock here since I'm going for the GPU Instancing stuff.

    Also, in this other post the author says
    I'm confused about what can actually be done and how. I can't find any consistent documentation. Have I skipped a section of the Unity Manual?

    When wanting to replace textures while using GPU instancing, what's your recommended way?
    Should I use a Texture asset or a RenderTexture on the MaterialPropertyBlock?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    Yep, I'm familiar with that post. It is wrong.

    To the best of my knowledge it was wrong when the author originally posted it too. If you follow the steps he describes and simply omitting the [PerRenderData] step, it produces the exact same result. I can only guess the author never actually tested with out using [PerRenderData], or when they did had some bug in their code. It's unfortunate because so much of the information is useful, and it comes up often. It also predates Unity's GPU instancing support by about 3 months! It's often sited by people when taking about instancing, but it isn't what that post is doing!

    This post is correct. You cannot change the texture and still instance. Well, to be more precise, two objects with different textures cannot be instanced together. This is a GPU & graphics API limitation, not something Unity is imposing themselves.

    The way instancing works is each instance of a mesh gets rendered by the GPU with a different instance id that's passed to the shader. Every single instance is in fact using the exact same "material" with the exact same settings. The bit of magic is instanced properties are put into an array of values that's set on the material rather than a singular value. So when an instance is rendered, and your shader has the _Color value setup as an instanced property, then instead of doing:
    fixed4 col = _Color;

    it's doing
    fixed4 col = _Color[instanceID];

    The problem is you can't have dynamic arrays of textures. It's simply not supported by any graphics API. You can only have arrays of floats, ints, or bools.

    But that's why the Texture2DArray exists. It's a type of texture that is itself an array with multiple slices. Unity supports these, but offers no in editor tools for creating or managing them. So you have to do that yourself with custom scripts. Then you set the single Texture2DArray texture object on your material, and for each renderer you can set which index to use when reading from the array texture.


    But, there's one really big problem...
    Unity doesn't yet appear to support defining instanced properties in Shader Graph!!! The ShaderGraph materials themselves do support instancing, and multiple objects using the same material and mesh will be instanced, there's no way to set properties to be instanced properties, so this is all kind of moot. There's still good reasons to use material property blocks over setting values on a material directly, but not related for allowing instancing. :(
     
    noio, cLick1338, q23w1e and 6 others like this.
  5. Rienhl

    Rienhl

    Joined:
    Nov 14, 2013
    Posts:
    42
    Wow, you really went over the top with this answer! I appreciate it a lot!
    Thank you so much!
     
    amisner2k likes this.
  6. amisner2k

    amisner2k

    Joined:
    Jan 9, 2017
    Posts:
    30
    That racing snail really knows his stuff. :D
     
  7. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    446
    Is this supported by Shader Graph yet?
     
    Carrotpie likes this.
  8. STUDIOCRAFTapps

    STUDIOCRAFTapps

    Joined:
    Feb 4, 2015
    Posts:
    104
    It's not supported yet to my knowledge but I did manage to get the generated code and make it material property block compatible but I can't remember how
     
  9. lloydv

    lloydv

    Joined:
    Sep 15, 2015
    Posts:
    52
    Just want to add a little quirk I encountered today for anyone else who stumbles on this thread if they come across the same issue when working with shader graph and material property blocks. My issue was I was using a material property block in a tween on a shader graph material value and the initial + default value set in the shader/material for alpha was 1, but the tween always started at 0.

    It seems that MaterialPropertyBlock.GetFloat doesn't return the correct value until it has been set once via SetFloat. Maybe this is expected behaviour? Not sure.

    In any case, I was able to get the correct value via good 'ol Material.GetFloat, and then used the material property block for all subsequent gets/sets.

    Edit: material.GetFloat instantiates the material so I switched to sharedMaterial instead. Not exactly an ideal solution.
     
    Last edited: Aug 21, 2019
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    The MaterialPropertyBlock is just an arbitrary set of material properties in no way connected to a specific material. If you’ve not set data for a property on the block, then it’ll just return a default value (usually zero). If you want the value the material has by default, then you need to get it from that material.

    It would be nice to be able to know from code what the actual value a material is using is, with a property block applied, but that’s not possible. Internally material property blocks have a “has property” method, it’s not exposed so there’s no way to know if a material property block doesn’t have a value or if the value is black. To that end if there’s some value I need to track, or if I need the ability to reset a property back to using the material’s own value, I usually have a separate data structure which I keep track of all the values and update the property block from and never bother using the property block’s get methods.
     
    lloydv likes this.
  11. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    141
    Does this even work at all though? When I set a MaterialPropertyBlock on a bunch of renderers, the ShaderGraph shader actually breaks (pink!). The same shader _without_ the MaterialPropertyBlock does render correctly.

    Code (CSharp):
    1. var propBlock = new MaterialPropertyBlock();
    2. newRenderer.GetPropertyBlock(propBlock);
    3. propBlock.SetFloat("_WindOffset", variant / 17f);
    4. newRenderer.SetPropertyBlock(propBlock);


    (ShaderGraph 7.1.2, Unity 2019.3.0b12)
     
  12. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    141

    Edit: Updating to ShaderGraph 7.1.5 seems to have fixed this.

    However, batching no longer happens (as you said), with the FrameDebugger reporting:

     
    Last edited: Nov 26, 2019
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    I think Unity is expecting everyone to use the the SRP Batcher instead of Dynamic Batching or instancing, so I can see bugs falling through the cracks if you still have dynamic batching enabled. I'm kind of impressed it managed to totally break something though.
     
    chrismarch likes this.
  14. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    446
    So, almost a year passed.
    What's the current status of MaterialPropertyBlock for ShaderGraph support?
    What's the current best way to efficiently do it?

    EDIT : Tested it, and it is already working on the newest ShaderGraph!
     
    Last edited: Dec 24, 2019
    Carrotpie and JotaRata like this.
  15. najati

    najati

    Joined:
    Oct 23, 2017
    Posts:
    11
    @Gekigengar

    I'm currently wrestling with this - could you define your test in broad terms so I can see if I can reproduce it?

    Cheers!
     
    Carrotpie likes this.
  16. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    446
    Code (CSharp):
    1. public class MaterialBlockTest : MonoBehaviour
    2. {
    3.     private MaterialPropertyBlock materialBlock;
    4.     private MeshRenderer meshRenderer;
    5.  
    6.     void Start()
    7.     {
    8.         materialBlock = new MaterialPropertyBlock();
    9.         meshRenderer = GetComponent<MeshRenderer>();
    10.     }
    11.  
    12.     private void ChangePropertyBlock(float amount)
    13.     {
    14.         materialBlock.SetFloat("_SomeFloatProperty", amount);
    15.         meshRenderer.SetPropertyBlock(materialBlock);
    16.     }
    17. }
    Here is a sample, apparently you don't have to do anything special to your property in ShaderGraph to make this work.
     
  17. najati

    najati

    Joined:
    Oct 23, 2017
    Posts:
    11
    Thanks for the update! Batching also seems to be working for me without using a property block, just when setting props on the material (and letting Unity create an instance of it). Did you have to use a property block to get it to work?

    NB: The batching only works when rendering opaques, not the shadowmap. About to post a thread about that.
     
  18. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    446
    I don't get what you meant,
    Batching will work with or without material property block.

    Its just that having material instances increases draw call because its considered a different material, which is bad for performance for property values that requires unique values for animation/color change/etc.
     
    Last edited: Jan 30, 2020
  19. najati

    najati

    Joined:
    Oct 23, 2017
    Posts:
    11
    Hey @Gekigengar - thanks again for the response.

    I understand that different material instances are treated like different materials but the batcher seems to be able to batch them together for the opaques render pass. See my post here for a clear example:

    https://forum.unity.com/threads/bug...ap-or-am-i-misunderstanding-something.806544/

    When I change that example to use property blocks, it actually breaks the batching for the opaque pass. Perhaps I'm not using them correctly.
     
    Gekigengar likes this.
  20. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    446
    You're right, my bad, the moment I saw the materials aren't instanced, I prematurely assumed they're already properly batched. But after checking frame debugger, turns out they aren't batched. It seems like I misunderstood.

    Perhaps shader graph isn't working with material property block yet, despite the label saying material property block are used to change values in the inspector.

    Will need Unity to clarify when this is actually coming.
     
    Last edited: Jan 30, 2020
  21. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    "Working with material property blocks" and "working with instanced properties" are different things.

    Shader Graph supports material property blocks, and apart from a short period of time, always have.

    Shader Graph does not support custom instanced properties that would allow material property blocks to be set on multiple renderer components and still render as a single instanced draw, and there's no ETA on when that will be added, if ever.
     
    hyunBonfire likes this.
  22. STUDIOCRAFTapps

    STUDIOCRAFTapps

    Joined:
    Feb 4, 2015
    Posts:
    104
    Setting up instancing ourselves from the outputted shader is a pain. I don't see why it would never be added. What issue would it bring?
     
  23. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    No issues, it just seems like it’s a low priority for Unity. The impression I’ve gotten from their responses has been “the SRP Batcher should replace the need for traditional instancing.”
     
  24. An3Apps

    An3Apps

    Joined:
    Nov 22, 2017
    Posts:
    7
    I don't know if this is the same thing, but I achieved instanced shader graphs for my game by using animations and keyframing the values. So for example, when my enemies die, I spawn a ragdoll and animate the alpha clip value from shader graph to slowly dissolve. If I kill multiple enemies they independently dissolve.
     
  25. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,019
    Nothing you just described would be rendered using GPU instancing. Unity's built in skinned meshes have never used instancing, so even if you were using an instanced shader it wouldn't be rendered using instancing. This is just going to be rendering the objects normally.
     
  26. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    1,804
    This isn't exactly tailored question for this thread, but I could not find anything yet.. so does anyone know how batching works for URP + 2Drenderer ? I was hoping for SRP batching to work, but it all seems to be broken now..and have no info what so ever, on what kind of set up I need to to.. even shaders made using Shader Graph for 2DRenderer does not seem to batch at all....
     
unityunity