Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Different material properties / shader uniforms for objects with a shared material?

Discussion in 'Shaders' started by davidlively, Nov 12, 2015.

  1. davidlively

    davidlively

    Joined:
    Aug 1, 2015
    Posts:
    12
    I'm dynamically generating a bunch of objects with a shared mesh and material.

    After generation, if I set a material property in the editor on one of the objects, it gets applied to all of them.

    Now, since I'm using .sharedMaterial, this makes sense. However, it also happens if I use the .material field.

    Do I need to create a different material instance for each object? There's around 100 of them, and it seems like a waste of resources. I am not sure if each material instance would share the shader stored on the GPU, or if I'd wind up with 100 copies of the shader. :/

    I suspect I'm just "doing it wrong" (tm):
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    You would have to make copies. The shader is shared. The material just holds the properties that get sent to the shader.
     
    awsapps likes this.
  3. Fuegan

    Fuegan

    Joined:
    Dec 5, 2012
    Posts:
    20
    What you could do is when you're generating your meshes and materials use something like this :

    Code (CSharp):
    1. renderer.material = new Material(SomeMat);
    with SomeMat being a material you have set in the editor. This way all new objects will have a unique instance of SomeMat.

    Note that after creating the instance when you modify the materials by script you should use .sharedMaterial to avoid creating tons of new material instances.
     
  4. Sky286

    Sky286

    Joined:
    Jan 21, 2015
    Posts:
    2
    Another option is to use MaterialPropertyBlock. It allows you to customize specific material properties without copying the material - and also allows the materials to be rendered in the same draw call (as far as I can tell)

    Code (CSharp):
    1.  
    2. var propertyBlock = new MaterialPropertyBlock();
    3. GetComponent<Renderer>().GetPropertyBlock(propertyBlock);
    4. propertyBlock.SetColor("_Tint", color);
    5. GetComponent<Renderer>().SetPropertyBlock(propertyBlock);
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    In the editor when not in play mode the shared material and material lists I believe are the same thing. In play mode or in stand alone doing a renderer.materials[0].SetColor() will create a copy of the material.

    Different materials that point at the same shader but have different properties still use the same shader, there's not a 100 copies of the shader on the gpu (unless you're using shader keywords and multi compile stuff, in which case there could be, but that's a different topic).

    So if you're programmatically assigning values to many objects in the editor before play, you would need to use new Material, or use the material property block suggested above. Property blocks might be more efficient, but either way the end result is the same; a mesh pointer, a shader pointer, and a big bundle of properties get packed up and sent to the GPU. Unless you've got hundreds of properties on your material this amounts to tiny amounts of data.