Search Unity

Material Properties and Unity Serialization

Discussion in 'General Graphics' started by DanWeston, Nov 13, 2018.

  1. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    32
    I have a couple of custom Shaders which use a specific DeltaTime variable. I pass the value from C# into the Material instance. This all works fine, however, each material instance serializes this value. This is annoying as my git always shows changes for these files. Is there an easy way to mark a material as non-serializeable?

    See git diff here:

    https://imgur.com/aNqXRYl
     
  2. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    I I've run into the same issue and I've mostly learned to ignore it. If you really can't get over it, you can use
    #if UNITY_EDITOR
    ,
    #else
    , and
    #endif
    to make different code for testing in-editor and running a build. Your build code should remain the same, but your editor code should modify the "renderer.material" instead of the "renderer.sharedMaterial" so that a temporary copy of the material is edited at runtime, and your runtime changes to material properties through code are not permanently stored.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Don't include _DeltaTime in the properties block of the shader if you don't want it to be serialized.
    Code (csharp):
    1. Shader "MyShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         // don't put _DeltaTime in here, this exists just for serialization
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Pass
    12.         {
    13.             CGPROGRAM
    14.             #pragma vertex vert
    15.             #pragma fragment frag
    16.  
    17.             float _DeltaTime; // just need it here for the shader to access it
    18.  
    19.             ...
    20.  
    21.             ENDCG
    22.         }
    23.     }
    24. }
    If you need the value to still appear in the inspector, but not be serialized, you'll need to use a custom material editor.

    Additionally, if the value isn't unique to each material instance, you might want to use Shader.SetGlobalFloat instead, or alternatively use material property blocks so you're not directly modifying the material.


    Also, unless you need a custom delta time, there's already a global shader float value built in.
    unity_DeltaTime
     
  4. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    32
    Thanks for your response.

    I always assumed that variables placed in the properties block was required for accessing in C#! Cheers for the info, no need to serialize the value so it's all removed.

    I wasn't aware of SetGlobal, I measured it's performance against setting all individually and it gave a nice increase... I use a custom Delta Time in my project which is why I've not been using the built in global.

     
    bgolus likes this.
  5. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    769
    This annoys the hell out of me. I animate my materials properties. Every time I hit play, it produces changes that end up in git conflicts.
    I opened a bug with unity and they closed it as "by design".

    Here is my solution. Add this script in an Editor folder:

    Code (CSharp):
    1. using System.IO;
    2. using System.Linq;
    3. using UnityEditor;
    4.  
    5. // prevents materials from being saved in play mode
    6. public class PlayModeMaterials : UnityEditor.AssetModificationProcessor
    7. {
    8.     static string[] OnWillSaveAssets(string[] paths)
    9.     {
    10.         if (EditorApplication.isPlaying)
    11.         {
    12.             return paths.Where(path => Path.GetExtension(path) != ".mat").ToArray();
    13.         }
    14.         else
    15.         {
    16.             return paths;
    17.         }
    18.     }
    19. }
    20.  
     
    atomicjoe, DTAli and wharvey like this.
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Won't that just defer the save until after you come out of play mode? I.E. if you save your project, the materials would still get saved with their changes?

    Generally speaking it's better to never modify materials at runtime and rely on Shader.SetGlobal__() and MaterialPropertyBlocks when you can. For the cases where that's not an option (like the skybox), I manually create a material instance (copy) and re-assign it when running in the editor.
     
  7. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    769
    It works fine for me, the modified material is simply destroyed from memory when I exit play mode. It does not save it afterwards even if I click "save" or "save project".
    I agree, I use MaterialPropertyBlocks whenever possible, but sometimes I want to update a shared material. I have hundreds of objects sharing a material and you want to update them all. A materialPropertyBlock is per renderer, so I would have to set it 100 times. In my particular case, I need to set the texture of a shared material, but the texture is only available at runtime.
     
    atomicjoe and bgolus like this.