Search Unity

Question Default Shader Values and New Materials

Discussion in 'Shaders' started by davidbk, Sep 24, 2022.

  1. davidbk

    davidbk

    Joined:
    May 16, 2018
    Posts:
    10
    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:
    1. Is there a setting to force any newly created materials to use default shader values?
    2. Is there a way to propagate value changes made in the shader to all of its (child) materials?
    3. Is there a way to 'Reset' individual material property values to default, as opposed to resetting the entire material?
     
  2. kruskal21

    kruskal21

    Joined:
    May 17, 2022
    Posts:
    68
    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.
     
    davidbk likes this.
  3. davidbk

    davidbk

    Joined:
    May 16, 2018
    Posts:
    10
    kruskal21 likes this.
  4. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    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...

    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using System;
    3. using System.Collections.Generic;
    4. using System.IO;
    5. using System.Reflection;
    6. using UnityEditor;
    7. using UnityEngine;
    8. using UObject = UnityEngine.Object;
    9.  
    10. namespace burningmime.unity.editor
    11. {
    12.     static class MaterialThingOnForum
    13.     {
    14.         private static Shader _lastSelectedShader;
    15.  
    16.         [InitializeOnLoadMethod]
    17.         public static void bindSelectionChanged()
    18.         {
    19.             Selection.selectionChanged -= onSelectionChanged;
    20.             Selection.selectionChanged += onSelectionChanged;
    21.         }
    22.  
    23.         private static void onSelectionChanged()
    24.         {
    25.             foreach(UObject obj in getSelectedAssets())
    26.             {
    27.                 if(obj is Shader shader)
    28.                     _lastSelectedShader = shader;
    29.                 else if(obj is Material material)
    30.                     _lastSelectedShader = material.shader;
    31.             }
    32.         }
    33.  
    34.         [MenuItem("Assets/Create/New Material From Selected Shader %#m")]
    35.         public static void createMaterial()
    36.         {
    37.             if(!_lastSelectedShader)
    38.                 throw new Exception("Select a shader or material first");
    39.             ProjectWindowUtil.CreateAsset(new Material(_lastSelectedShader), getPathForNewMaterial());
    40.         }
    41.  
    42.         private static IEnumerable<UObject> getSelectedAssets() =>
    43.             Selection.GetFiltered(typeof(UObject), SelectionMode.Assets);
    44.  
    45.         private static string getPathForNewMaterial()
    46.         {
    47.             MethodInfo getActiveFolderPath = typeof(ProjectWindowUtil).GetMethod("GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
    48.             string contextPath = (string) getActiveFolderPath!.Invoke(null, null);
    49.             return AssetDatabase.GenerateUniqueAssetPath(Path.Combine(contextPath, "New Material.mat"));
    50.         }
    51.     }
    52. }
    53. #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.
     
    kruskal21 likes this.