Quick Material Creation

Table of Contents

1. Quick Material Creation

It is painful to create materials in Unity. The process goes like this:

  • Create->Material
  • Name the Material asset
  • Choose a shader for the material from the material inspector shader drop-down
  • Find the texture(s) to be used in this material, drag and drop them one by one to the respective texture channel

This sounds like a simple process for creating a single material with a single texture. But this seemingly simple thing gets out of hand and becomes a click-fest too quickly when you have to create multiple materials. So I’ve created a simple editor extension with Unity to simplify this process.

1.1 How to use

  • Select one or more textures in the Project window
  • Right click on any of them to invoke the Assets context menu (Alternatively click the Assets menu item in the menu bar)
  • Find Utilities/Generate material
  • Select the material type you want to generate

All your textures will have a material created for them and their main textures assigned to their mainTexture property.

1.2 Features

  • Works on single or multiple textures
  • Assigns the textures to their fields automatically
  • Very easy to add new shader types for material generation. Copy/paste and rename the method and done.
  • Can be extended and improved

1.3 Source Code

Extremely verbose comments included for clarification.

/* 
    A small editor extension to automate material generation        
    Sarper Soher                                                 
    http://sarpersoher.com/unity3d/quick-materials/  
*/

using UnityEngine;
using UnityEditor;

internal sealed class ContextMenuCreateMaterial : Editor {
    // The menu path to be used in the context menu, this is where our material options will reside in the right-click menu
    private const string menuPath = "Assets/Utilities/Generate Material/";

    // This is the main method which does all the work, see the comments in it to understand how it works (Warning: Unnecessarily verbose)
    private static void GenerateMaterial(Shader shader) {
        // Looks at the current selected assets, only gets the textures
        var selection = Selection.GetFiltered(typeof(Texture2D), SelectionMode.Assets);

        // If there are no textures selected, does nothing and returns a warning message
        if(selection.Length == 0) {
            Debug.LogWarning("There is no Texture2D selected");
            return;
        }

        // For every texture in the selection
        foreach(var texture in selection) {
            // Get the current texture in iteration
            var selectedTexture = texture as Texture2D;
            // Store it's path in selectedPath
            var selectedPath = AssetDatabase.GetAssetPath(texture);

            // Get the file extension from the path and remove it
            var extensionIndex = selectedPath.IndexOf('.');
            selectedPath = selectedPath.Remove(extensionIndex, selectedPath.Length - extensionIndex);

            // If there is a "tex" string contained in the name, switch it with a "mat"
            // It's here because I like to name my textures like "texBrick" for various reasons
            // More on that on another blog post
            if(selectedPath.Contains("tex")) selectedPath = selectedPath.Replace("tex", "mat");

            // Add ".mat" extension to the end of the file path
            selectedPath += ".mat";

            // Create a new material asset with the shader given as an argument to this method
            // Set the mainTexture property of the selected shader with the texture in asset selection
            var material = new Material(shader) {mainTexture = selectedTexture};

            // And finally create the material in the same folder besides the selected texture, voila!
            AssetDatabase.CreateAsset(material, selectedPath);
        }
    }


    // Below are some methods for various built-in shaders I use for mobile games
    // Duplicate and edit the duplicated method to add more shader types and menu items

    // Add a new menu entry, in this case it becomes "Assets/Utilities/Generate Material/Diffuse"
    [MenuItem(menuPath + "Diffuse", false, 0)]
    private static void CreateDiffuse() {
        // Call our material generated method above with the built-in Diffuse shader, simple!
        GenerateMaterial(Shader.Find("Diffuse"));
    }

    [MenuItem(menuPath + "Mobile/Diffuse", false, 1)]
    private static void CreateMobileDiffuse() {
        GenerateMaterial(Shader.Find("Mobile/Diffuse"));
    }

    [MenuItem(menuPath + "Unlit/Texture", false, 2)]
    private static void CreateUnlit() {
        GenerateMaterial(Shader.Find("Unlit/Texture"));
    }

    [MenuItem(menuPath + "Mobile/Particles/Additive", false, 1)]
    private static void CreateMobileParticleAdditive() {
        GenerateMaterial(Shader.Find("Mobile/Particles/Additive"));
    }

    [MenuItem(menuPath + "Mobile/Particles/Alpha Blended", false, 1)]
    private static void CreateMobileParticleAlphaBlended() {
        GenerateMaterial(Shader.Find("Mobile/Particles/Alpha Blended"));
    }

    // Just copy/paste a method from above to add more shader options

}

There is a ton of space for improvement and customization, for example currently it only assigns the “mainTexture” field. Why not assign the specular/normal/etc. maps too? Using the texture filenames could be a good start to add such a feature.