Shared Material references, Scene<->Prefabs, in builds

An interesting problem presented itself - and while I have found a workaround (noted below), I’m wondering if this is the intended behaviour, or if there’s a bug?

To illustrate the issue, you could add public Material test; to a component in your scene. Then drag a material to it. Now create a prefab and add a 3D object and assign the same material to the MeshRenderer. Now, in the editor, during play mode or edit mode, these are the same material. If I assign a texture via the Inspector or through myComponent.test.SetTexture(whateverKey, whateverTexture);, the change is immediately reflected on the model in the prefab, whether it’s the prefab itself or an instantiated clone of it, as they’re sharing the material. I’m accustomed to this workflow and personally think it’s great.

The problem arises in builds. In this case, a material assigned a URP Shader Graph (I haven’t tested other pipelines). Mark the prefab Addressable, load at runtime in a build (tested in 2022.1.0a15 WebGL), and now it does NOT behave this way. Setting a texture to the reference in the scene has no effect on the content loaded at runtime. But in the editor, all of the content loaded at runtime does share material references with the scene.

The workaround I’m using for now is to assume that materials in loaded prefabs can not be updated through references in the scene, and grab my texture references from the materials in the scene and assign them to corresponding materials in the prefabs. Of course, this is not ideal… but it works.

Is anyone able to elaborate on whether this is “just the way it is” because […], or if it’s something that is already on the way to being fixed, or something that should be reported as a bug ? Thanks :slight_smile:

To illustrate further, here’s a functional approach to restore lost references to a material which was shared between a scene and downloadable prefabs in the editor, but not in builds:

// a material in your scene, that is also used by however many renderers in prefabs..
Material material = theSceneMaterialReference;

// whichever renderers you want to restore lost references for..
Renderer[] rens = gatheredFromPrefabAfterDownloadingIt;

// iterate over each renderer..
for (int r = 0; r < rens.Length; r++)
{
  Renderer ren = rens[r];
  // and for each material it has,
  for (int m = 0; m < ren.sharedMaterials.Length; m++)
  {
    // if the name is identical but the reference is not,
    if ((ren.sharedMaterials[m].name == material.name) && (ren.sharedMaterials[m] != material))
    {
      // grab the shared materials array from the renderer..
      Material[] materials = ren.sharedMaterials;
      // replace the specific material reference,
      materials[m] = material;
      // then assign the material array back to the renderer:
      ren.sharedMaterials = materials; // note: must replace this way - assigning via .sharedMaterials[m] =.. does not work.
    }
  }
}

Previously I would let the prefab request what it needs from systems already running in the scene, or by assigning textures to the materials in the prefab directly. But I’ve found that letting the scene manage shared materials is a far better approach for a number of reasons (at least in my project)… the only hiccup being the prefab renderers’ materials aren’t actually still “shared” between them and scenes in builds.

Edit: fixed a code comment