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. Dismiss Notice

How to Avoid Leaking Textures

Discussion in 'Scripting' started by Afropenguinn, Dec 5, 2014.

  1. Afropenguinn

    Afropenguinn

    Joined:
    May 15, 2013
    Posts:
    305
    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:
    Code (CSharp):
    1.  
    2.         //Top
    3.         GetComponent<MeshRenderer>().materials[0] = new Material(material);
    4.         GetComponent<MeshRenderer>().materials[0].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.y / 2f);
    5.  
    6.         //South
    7.         GetComponent<MeshRenderer>().materials[1] = new Material(material);
    8.         GetComponent<MeshRenderer>().materials[1].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);
    9.  
    10.         //West
    11.         GetComponent<MeshRenderer>().materials[2] = new Material(material);
    12.         GetComponent<MeshRenderer>().materials[2].mainTextureScale = new Vector2(transform.localScale.y / 2f, transform.localScale.z / 1f);
    13.  
    14.         //North
    15.         GetComponent<MeshRenderer>().materials[3] = new Material(material);
    16.         GetComponent<MeshRenderer>().materials[3].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);
    17.  
    18.         //East
    19.         GetComponent<MeshRenderer>().materials[4] = new Material(material);
    20.         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! :)
     
    Last edited: Dec 5, 2014
  2. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    489
    That's quite a lot of code, Could be very much simplified.
    Code (CSharp):
    1. MeshRenderer rend = GetComponent<MeshRenderer>();
    2. Vector2 texScale = new Vector2(transform.localScale.x / 2f, transform.localScale.y / 2f);
    3.  
    4. for(int i = 0; i <= 4; i++){
    5. //Loop through the materials. Setting scale and making instances of Diffuse
    6. rend.materials[i] = new Material(Shader.Find("Diffuse")); //Create an instance of a material from Diffuse shader.
    7. rend.materials[i].mainTextureScale = texScale;
    8. }
    Also what exactly do you mean by "texture leaking"?
     
  3. RSG

    RSG

    Joined:
    Feb 20, 2013
    Posts:
    93
    You're creating materials that need to be destroyed manually. You can hide the message by using:

    Code (CSharp):
    1. material.hideFlags = HideFlags.DontSave;
    However, you still need destroy the materials to avoid leaks:

    Code (CSharp):
    1. private void OnDestroy( )
    2.     {
    3.         MeshRenderer rend = GetComponent<MeshRenderer>();
    4.  
    5.         for(int i = 0; i <= 4; i++)
    6.         {
    7.             if(rend.materials[i] == null)continue;
    8.             Object.Destroy(rend.materials[i]);
    9.         }
    10.     }
     
  4. Afropenguinn

    Afropenguinn

    Joined:
    May 15, 2013
    Posts:
    305
    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".
     
  5. RSG

    RSG

    Joined:
    Feb 20, 2013
    Posts:
    93
    I did a test to see if I could duplicate your error based on your code, so created an object and added this code:

    Code (CSharp):
    1. [ExecuteInEditMode]
    2. public class Test : MonoBehaviour
    3. {
    4.  
    5.     private void OnEnable()
    6.     {
    7.         // get renderer
    8.         var meshRenderer = this.GetComponent<MeshRenderer>();
    9.      
    10.         // Get original material
    11.         var originalMaterial = new Material(meshRenderer.materials[0]); ;
    12.  
    13.         // Replace with new material
    14.         meshRenderer.materials[0] = new Material(originalMaterial);
    15.         meshRenderer.materials[0].mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);
    16.     }
    17. }
    18.  
    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:

    Code (CSharp):
    1. [ExecuteInEditMode]
    2. public class Test : MonoBehaviour
    3. {
    4.  
    5.     //Create local storage for your materials
    6.     Material[] localMaterials = new Material[1];
    7.  
    8.     private void OnEnable()
    9.     {
    10.         // get renderer
    11.         var meshRenderer = this.GetComponent<MeshRenderer>();
    12.      
    13.         // Recreate material
    14.         var material = new Material(Shader.Find("Diffuse"));
    15.  
    16.         // Apply changes to material
    17.         material.mainTextureScale = new Vector2(transform.localScale.x / 2f, transform.localScale.z / 1f);
    18.      
    19.         // Load into list, this is the important change
    20.         localMaterials[0] = material;
    21.  
    22.         // Replace materials
    23.         meshRenderer.materials = localMaterials;
    24.      
    25.     }
    26. }
    27.  

    After doing this the error was gone, hope this helps :)
     
  6. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    489
    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. ;)
     
  7. Afropenguinn

    Afropenguinn

    Joined:
    May 15, 2013
    Posts:
    305
    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).