Search Unity

Standard material shader ignoring SetFloat property "_Mode"?

Discussion in 'Shaders' started by Hotdug, Jul 30, 2015.

  1. Hotdug

    Hotdug

    Joined:
    Jul 3, 2014
    Posts:
    11
    In my code I create a texture and fill it with colors (some are 100% transparent, others are 100% opaque). I then create a material and put the texture in it. Finally a quad is created and the material is used on its renderer.

    Code (csharp):
    1.       Color32[] colors = ...  //length: 250000
    2.  
    3.       Texture2D t = new Texture2D(500, 500, TextureFormat.ARGB32, false);
    4.       t.SetPixels32(colors);
    5.       t.Apply();
    6.  
    7.       Material mat = new Material(Shader.Find("Standard"));
    8.       mat.SetFloat("_Mode", 2);
    9.       mat.mainTexture = t;
    10.  
    11.       GameObject go = GameObject.CreatePrimitive(PrimitiveType.Quad);
    12.       go.transform.localScale = new Vector3(500, 500, 1);
    13.       go.GetComponent<MeshRenderer>().material = mat;
    The problem is that alpha is ignored, all colors are 100% opaque. If I select the quad while playing I can see the properties of the material in Unity's inspector. Shader is "Standard" and the Rendering Mode is indeed "Fade".

    If I manually select "Fade" again from the drop down menu the transparent pixels in the texture immediately become transparent. If I set the inspector to Debug and manually go to Saved Properties > Floats > Element 10 (which is "_Mode") I can see that "Second" is indeed set to "2" (which means "Fade"). If I manually change this number to 1 ("Cutout") nothing happens. If I manually set it back to 2 nothing happens as well, all pixels remain opaque and alpha is still ignored.

    If I set it to 3 (for "Transparent") nothing happens. If I then change the inspector back to "Normal" still nothing happens, except that the Material's Rendering Mode shows as "Transparent" in the Inspector. If I then click the drop down list and select the already-selected "Transparent" option again *then* the alpha values are respected and some colors in the texture becomes non-opaque.

    Is this a bug or is something weird required when using SetFloat?

    Because SetFloat("_Mode", 2); does not work on the Standard shader. It seems like we can't use the Standard shader as intended from script.
     
  2. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
  3. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    It's much easier to call the method in the shader's custom GUI, which will set all that up properly.

    Code (csharp):
    1. //Material material < this assumes you have a reference to the material already set.
    2. StandardShaderGUI.SetupMaterialWithBlendMode (material, StandardShaderGUI.BlendMode.Fade);
     
  4. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    Oh, that's very handy indeed, I was unaware of its existence.
     
  5. Hotdug

    Hotdug

    Joined:
    Jul 3, 2014
    Posts:
    11
    "The name `StandardShaderGUI' does not exist in the current context"

    http://docs.unity3d.com/ScriptReference/30_search.html?q=StandardShaderGUI
    No results.

    Googling "StandardShaderGUI" gets strange results. It's like it doesn't exist or aren't supposed to be used in released games.

    Unfortunately your reply doesn't make sense. Do I need to import something weird? Why isn't this feature built into the Material class in the first place? I'm far from the only guy having trouble with this and from what I've read many other also think this is a bug.

    This works but frankly it feels like a joke that all of those lines are required just to get a standard shader that support transparency. Unity should do something about this.

    Putting the code here for anyone finding this thread in their search of making sense out of _Mode not working:

    Code (csharp):
    1. Material m = new Material(Shader.Find("Standard"));
    2. m.SetFloat("_Mode", 2);
    3. m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
    4. m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    5. m.SetInt("_ZWrite", 0);
    6. m.DisableKeyword("_ALPHATEST_ON");
    7. m.EnableKeyword("_ALPHABLEND_ON");
    8. m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    9. m.renderQueue = 3000;
     
  6. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    The alternative is having a material with the mode you require set up in the inspector, and then copy that material to create the materials you need at runtime (using Instantiate(), not new Material(sourceMaterial)).

    You could also define an extension method to make your life a bit easier.

    Code (CSharp):
    1. public static class Extensions
    2. {
    3.     public static void SetAsFade(this Material material)
    4.     {
    5.         material.SetFloat("_Mode", 2);
    6.         material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
    7.         material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    8.         material.SetInt("_ZWrite", 0);
    9.         material.DisableKeyword("_ALPHATEST_ON");
    10.         material.EnableKeyword("_ALPHABLEND_ON");
    11.         material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    12.         material.renderQueue = 3000;
    13.     }
    14. }
    Then you can just do the following:

    Code (CSharp):
    1. Material mat = new Material(Shader.Find("Standard");
    2. mat.SetAsFade();
     
    Last edited: Jul 31, 2015
    vicator likes this.
  7. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    My bad, the StandardShaderGUI is part of the built-in shader code. You can get it from there, though.

    Code (csharp):
    1.  
    2.     public enum BlendMode
    3.     {
    4.         Opaque,
    5.         Cutout,
    6.         Fade,        // Old school alpha-blending mode, fresnel does not affect amount of transparency
    7.         Transparent // Physically plausible transparency mode, implemented as alpha pre-multiply
    8.     }
    9.  
    10.     public void SetupMaterialWithBlendMode(Material material, BlendMode blendMode)
    11.     {
    12.         switch (blendMode)
    13.         {
    14.             case BlendMode.Opaque:
    15.                 material.SetOverrideTag("RenderType", "");
    16.                 material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
    17.                 material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
    18.                 material.SetInt("_ZWrite", 1);
    19.                 material.DisableKeyword("_ALPHATEST_ON");
    20.                 material.DisableKeyword("_ALPHABLEND_ON");
    21.                 material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    22.                 material.renderQueue = -1;
    23.                 break;
    24.             case BlendMode.Cutout:
    25.                 material.SetOverrideTag("RenderType", "TransparentCutout");
    26.                 material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
    27.                 material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
    28.                 material.SetInt("_ZWrite", 1);
    29.                 material.EnableKeyword("_ALPHATEST_ON");
    30.                 material.DisableKeyword("_ALPHABLEND_ON");
    31.                 material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    32.                 material.renderQueue = 2450;
    33.                 break;
    34.             case BlendMode.Fade:
    35.                 material.SetOverrideTag("RenderType", "Transparent");
    36.                 material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
    37.                 material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    38.                 material.SetInt("_ZWrite", 0);
    39.                 material.DisableKeyword("_ALPHATEST_ON");
    40.                 material.EnableKeyword("_ALPHABLEND_ON");
    41.                 material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    42.                 material.renderQueue = 3000;
    43.                 break;
    44.             case BlendMode.Transparent:
    45.                 material.SetOverrideTag("RenderType", "Transparent");
    46.                 material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
    47.                 material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    48.                 material.SetInt("_ZWrite", 0);
    49.                 material.DisableKeyword("_ALPHATEST_ON");
    50.                 material.DisableKeyword("_ALPHABLEND_ON");
    51.                 material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
    52.                 material.renderQueue = 3000;
    53.                 break;
    54.         }
    55.     }
     
  8. aheydeck

    aheydeck

    Joined:
    Dec 17, 2014
    Posts:
    8
    It took me a full day to finally find this post, but it has eased my headache! Thank you! =)
     
  9. JohnRossitter

    JohnRossitter

    Joined:
    Dec 18, 2013
    Posts:
    1,027
    So, Im dealing with this now too.
    Strange thing for me is that it works in editor based code.
    But if I run in in a compiled version it does not....how does that make sense?
     
  10. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    In a packaged build, Unity will leave out any shader variants which are not used in any scene, since it believes it would be wasted space otherwise. You need to add an object which uses a material with the required mode to a scene .
     
    moyashi121 and HirokiYamada like this.
  11. ChristyJamesUST1

    ChristyJamesUST1

    Joined:
    Jun 26, 2019
    Posts:
    2
    Thank you a lot. This saved my day.