How to Avoid Leaking Textures

I have a 5 sided cube (you don’t see one side), each face has a different texture. This is the code I use to set each texture:

        //Top
        GetComponent<MeshRenderer>().materials[0] = new Material(material);
        GetComponent<MeshRenderer>().materials[0].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.y / 2f);

        //South
        GetComponent<MeshRenderer>().materials[1] = new Material(material);
        GetComponent<MeshRenderer>().materials[1].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);

        //West
        GetComponent<MeshRenderer>().materials[2] = new Material(material);
        GetComponent<MeshRenderer>().materials[2].mainTextureScale = new Vector2(transform.localScale.y / 2f, transform.localScale.z / 1f);

        //North
        GetComponent<MeshRenderer>().materials[3] = new Material(material);
        GetComponent<MeshRenderer>().materials[3].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);

        //East
        GetComponent<MeshRenderer>().materials[4] = new Material(material);
        GetComponent<MeshRenderer>().materials[4].mainTextureScale = new Vector2(transform.localScale.y / 2f, transform.localScale.z / 1f);

So how do I avoid leaking textures while getting the same results?
Also, I notice it isn’t actually creating an instance of the material, but just using the default “diffuse” material instead, why is that?

Thank you in advance for all your help! :slight_smile:

That’s quite a lot of code, Could be very much simplified.

MeshRenderer rend = GetComponent<MeshRenderer>();
Vector2 texScale = new Vector2(transform.localScale.x / 2f, transform.localScale.y / 2f);

for(int i = 0; i <= 4; i++){
//Loop through the materials. Setting scale and making instances of Diffuse
rend.materials[i] = new Material(Shader.Find("Diffuse")); //Create an instance of a material from Diffuse shader.
rend.materials[i].mainTextureScale = texScale;
}

Also what exactly do you mean by “texture leaking”?

You’re creating materials that need to be destroyed manually. You can hide the message by using:

material.hideFlags = HideFlags.DontSave;

However, you still need destroy the materials to avoid leaks:

private void OnDestroy( )
    {
        MeshRenderer rend = GetComponent<MeshRenderer>();

        for(int i = 0; i <= 4; i++)
        {
            if(rend.materials[i] == null)continue;
            Object.Destroy(rend.materials[i]);
        }
    }

Problem is that most of the code is a bit different (look at the texture scaling), so a for loop wouldn’t help much. Thanks though! And the editor is complaining of texture leaking.

That doesn’t seem to change anything. The errors are still coming up, and the material is still just going to the default diffuse rather than what I put into “material”.

I did a test to see if I could duplicate your error based on your code, so created an object and added this code:

[ExecuteInEditMode]
public class Test : MonoBehaviour
{

    private void OnEnable()
    {
        // get renderer
        var meshRenderer = this.GetComponent<MeshRenderer>();
      
        // Get original material
        var originalMaterial = new Material(meshRenderer.materials[0]); ;

        // Replace with new material
        meshRenderer.materials[0] = new Material(originalMaterial);
        meshRenderer.materials[0].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);
    }
}

I also added ExecuteInEditMode so I could test it in the editor. After doing this I got this error:

Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.
UnityEngine.Renderer:get_material()
Test:OnEnable() (at Assets/Scripts/Test.cs:16)

It looks like calling the materials property of the renderer causes the renderer to clone the materials and that’s why you’re getting the leak message.

Instead of using the getter component of the materials property you could create a local list of materials and then assign them to the renderer. For example:

[ExecuteInEditMode]
public class Test : MonoBehaviour
{

    //Create local storage for your materials
    Material[] localMaterials = new Material[1];

    private void OnEnable()
    {
        // get renderer
        var meshRenderer = this.GetComponent<MeshRenderer>();
      
        // Recreate material
        var material = new Material(Shader.Find("Diffuse"));

        // Apply changes to material
        material.mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);
      
        // Load into list, this is the important change
        localMaterials[0] = material;

        // Replace materials
        meshRenderer.materials = localMaterials;
      
    }
}

After doing this the error was gone, hope this helps :slight_smile:

Ah I didn’t catch that it was different. Now that I understand you and it says it’s leaking materials into the sceen, use renderer.sharedMaterials. :wink:

Thanks, worked like a charm! I still get a similar notification when saving the scene, but I think that is just because I update this script in the editor as well (makes level building easier).