Default Shader Values and New Materials

I have a question about the behavior of the Unity Editor when making new materials.

I am noticing that when I make a shader with editable properties and I set the default values on those properties to specific values, when I then go and make a new material using the given shader, the new material does not use the default values I defined in the shader.

For example, if I create a Color property and set it’s default value to green, when I make a new material using the shader, the material’s Color defaults to white (not green).

I’m a longtime Unreal user making the shift to Unity. I’m used to seeing any instance of a material using the same initial values as defined by the shader. Unity’s default behavior seems odd to me, but maybe there’s something I’m not getting.

I notice that I can force the material to use the default values by right-clicking on the material in the inspector and going ‘Reset’, but I’m not sure why this wouldn’t just happen by default.

I also notice that if I make changes to default property values in the shader, those changes don’t automatically get applied to new materials.

To summarize my questions:

  • Is there a setting to force any newly created materials to use default shader values?
  • Is there a way to propagate value changes made in the shader to all of its (child) materials?
  • Is there a way to ‘Reset’ individual material property values to default, as opposed to resetting the entire material?

I can explain why default values from a shader doesn’t always apply: Unity actually keeps a record of all the properties already initialized in a material, and doesn’t initialize it again when you switch its shader. So a variable like “_BaseColor”, which is a property of the lit shader if you are using URP, would stay in the material and not be initialized again. This means that properties that don’t exist in the default material shader will apply their default values correctly. So a custom property named “_MyOwnColor” will behave the way you want it to.

To actually answer your questions:

  1. I can’t think of any settings for this. However, you can create a custom editor for your shader. This should be a class that inherits from ShaderGUI https://docs.unity3d.com/ScriptReference/ShaderGUI.html. The class should override the OnGUI method, and set the default property values by using GetPropertyDefaultFloatValue and GetPropertyDefaultVectorValue, before calling base.OnGUI.

  2. Again, can’t think of an easy way to do this. But I think a feature in the 2022.1 version of unity - material variants - would be a good fit to what you want. This feature allows you to define child materials that can selectively override properties from its parent material, and changes made in the parent are reflected in the children, unless they are overridden.

  3. Material variants would again be useful for this. An alternative would be using the custom editor described in 1, and only picking the properties you want to reset.

1 Like

@kruskal21 thank you! This is very helpful!

1 Like

Note if you have the shader selected when you select “new material”, it’ll make the material with that shader instead of the default one. Unity Editor is also easy to extend (for some people’s definition of easy…) so you can make an editor script like this…

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UObject = UnityEngine.Object;

namespace burningmime.unity.editor
{
    static class MaterialThingOnForum
    {
        private static Shader _lastSelectedShader;

        [InitializeOnLoadMethod]
        public static void bindSelectionChanged()
        {
            Selection.selectionChanged -= onSelectionChanged;
            Selection.selectionChanged += onSelectionChanged;
        }

        private static void onSelectionChanged()
        {
            foreach(UObject obj in getSelectedAssets())
            {
                if(obj is Shader shader)
                    _lastSelectedShader = shader;
                else if(obj is Material material)
                    _lastSelectedShader = material.shader;
            }
        }

        [MenuItem("Assets/Create/New Material From Selected Shader %#m")]
        public static void createMaterial()
        {
            if(!_lastSelectedShader)
                throw new Exception("Select a shader or material first");
            ProjectWindowUtil.CreateAsset(new Material(_lastSelectedShader), getPathForNewMaterial());
        }

        private static IEnumerable<UObject> getSelectedAssets() =>
            Selection.GetFiltered(typeof(UObject), SelectionMode.Assets);

        private static string getPathForNewMaterial()
        {
            MethodInfo getActiveFolderPath = typeof(ProjectWindowUtil).GetMethod("GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
            string contextPath = (string) getActiveFolderPath!.Invoke(null, null);
            return AssetDatabase.GenerateUniqueAssetPath(Path.Combine(contextPath, "New Material.mat"));
        }
    }
}
#endif

Put that script somewhere in your project, click on a shader in your assets window, and then press control+shift+M (or command+shift+M on mac). Voila, new material using that shader! Or click a material, and control+shift+M to make a new material with the same shader as the selected material.

1 Like