Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  5. Nominations have been announced for this years Unity Awards. Celebrate the wonderful projects made by your peers this year and get voting! Vote here!
    Dismiss Notice
  6. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  7. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

Renderer.SetPropertyBlock without overwriting previous block?

Discussion in 'Shaders' started by pastaluego, Dec 7, 2018.

  1. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    27
    I'm pretty sure it's not possible, but how difficult would it be to be able to make an overload with a flag that only overwrites properties specified in the new MaterialPropertyBlock and keeps previous ones?

    The reason I ask is because I'm trying to break down all renderers in my scene into 'layers' (not Unity layers) and blend them with a color assigned to that layer. Blending is the easy part, just a lerp(texcolor, blendcolor, blendamount) in their fragment shader.

    Unfortunately, the only way I could think of doing this is by having each 'layer' have a cached materialpropertyblock with the blendcolor and blendamount set, iterate over all renderers assigned to each 'layer' and then set their block to the one assigned to the layer. The major downside is since I'm using ALL renderers for this specific functionality, I can't use materialpropertyblocks for anything else without interfering with it.

    Since I need to iterate over all renderers in the scene and not just visible ones, it's not feasible for me to check if each renderer has a propertyblock, get it, then set only the properties I want to overwrite on the block again, and then set that block back to the renderer. It's way too expensive compared to just overwriting the block.

    So I was wondering if there was any way to like make a renderer.AppendToPropertyBlock(mpb) method that just overwrites any properties specified in the passed propertyblock.

    Or if someone knows another way to achieve what I described, that would work too.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    5,354
    This is what GetPropertyBlock() is for.

    rend.GetPropertyBlock(matBlock);
    matBlock.SetColor(colorID, new color);
    rend.SetPropertyBlock(matBlock);
     
  3. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    27
    Yeah I was trying to explain in the 4th paragraph in the original post how considering I need to iterate over all renderers in the scene, the act of getting it first is a lot more expensive than simply overwriting it with
    rend.SetPropertyBlock(presetMatBlock);
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    5,354
    Unfortunately that's the only access we get from C# land.

    In the past I've had a property block wrangler of sorts. Keep all of the data I need in an extra class all my other scripts communicate to, then only apply the property block with those settings (if they've changed) in a LateUpdate.

    The other option is don't use property blocks for this. If your objects are in otherwise static layers, you could set that as a property block value, or separate them by some z value. Use a global array of color values you set with SetGlobalVectorArray and read the appropriate index in the shader.
     
  5. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    27
    Oh man setting layers to a certain Z and using the Z as an index for the color array in each shader set by Shader.SetGlobalVectorArray is a great idea, thanks so much!
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    5,354
    One note about using SetGlobalVectorArray for colors.

    Unity handles gamma correction for material properties that are defined as colors. Using SetColor() or SetVector() doesn't actually matter as the special handling is implicit depending on the property itself.

    Since you can't have material properties that are arrays, you can't define your list of colors as colors, so you need to do this gamma correction yourself. Actually, if you're using gamma color space for your project, you don't have to do this, but if you're using linear color space then you do:

    Code (csharp):
    1. Vector4[] colVecArray = new Vector4[numLayers];
    2. for(int i=0; i<numLayers; i++)
    3. {
    4.   if (QualitySettings.activeColorSpace == ColorSpace.Linear)
    5.     colVecArray[i] = colors[i].linear;
    6.   else
    7.     colVecArray[i] = colors[i];
    8. }