Search Unity

Toggle "Blend SrcAlpha OneMinusSrcAlpha" in a custominspector?

Discussion in 'Shaders' started by sunmachine, Apr 9, 2015.

  1. sunmachine

    sunmachine

    Joined:
    Jun 4, 2014
    Posts:
    9
    Hey all, I've been researching every possible angle on this one. I am a bit stumped.

    Right now, I am completing the unification of my shaders into a simple multi-compile solution on Unity 4.6.3. I have a custom inspector working, and all of my multicompile features within the CGPROGRAM of the script is working great. No complaints there.

    What I can't figure out is toggling the Blend keyword and SubShader tags to change the RenderType and Queue depending on what the material is being used for. From what I can tell so far, this is not possible. If it is, please enlighten me! I'm very interested in binding this to a precompiled solution and my Google-Fu is failing me.

    Another way I'm looking at this problem is understanding how the Blend keyword operates in ShaderLab. In the context of mobile shader development and avoiding unnecessary blending whenever possible (like OpenGL ES 2.0 specifically with PowerVR devices), is Unity smart enough to know when I'm not using any alpha values other than 1, and "boil out" that blending process? My assumption is, "No, it is not that smart, and it is always blending." Any insights on the specifics of this would be appreciated!

    Presently, my next point to test is to fidget around with some #if define directives and see if I can use Material.Enable/DisableKeyword. Wish me luck.


    -d
     
    Lipoly likes this.
  2. sunmachine

    sunmachine

    Joined:
    Jun 4, 2014
    Posts:
    9
    Hey all, bumping for good measure.

    I have already gone ahead and developed a solution to safely work around this lack of understanding. That said, I decided to play it safe and segregate Blend Off and Blend SrcAlpha OneMinusSrcAlpha into separate shaders. Regardless of this, this kind of clarification would likely save people a lot of time in the future, as well as satiate my curiosity

    Cheers,

    -d
     
    Lipoly likes this.
  3. UnityGuillaume

    UnityGuillaume

    Unity Technologies

    Joined:
    Mar 16, 2015
    Posts:
    123
    The Blend Keyword is basically an indication for Unity (not a shader language part) that it should enable blending on the rendering. It allow it to enable/disable a bunch of state on the rendering API ( D3D, OpenGL...)

    So (unless I'm wrong) Unity is not "intelligent" enough to disable it dynamically if you only use alpha value other than one : first it would ahve to execute the shader first to know the value, then activate blending, and GPU don't work like that.

    That said, blending is not "that" expensive. What is expensive is overdraw. The reason why transparent is expensive is because they need to be rendered back to front to be rendered properly (if not, since depth write is disable, a triangle behind another one would "overwrite it" if its alpha is 1). And that mean that no pixel are culled by depth test between transparent triangles. So if you have 20 triangles overlapping on one pixel, that pixel will have the shader executed 20 times.

    Now for your problem, There may be a way, but not sure it work in 4.x, so you'll have to test it! =p

    It seems the Blend & ZWrite keyword can take as value Properties

    So add in the Properties :

    [HideInInspector] _SrcBlend ("__src", Float) = 1.0
    [HideInInspector] _DstBlend ("__dst", Float) = 0.0
    [HideInInspector] _ZWrite ("__zw", Float) = 1.0


    Then it seems you can use :

    Blend [_SrcBlend] [_DstBlend]
    ZWrite [_ZWrite]


    (note the [ ] they tell ShaderLab that it should not output the string, but the value of the properties)

    Then pass value using SetInt (ZWrite is either 0 or 1, for the blend value, use the enum UnityEngine.Rendering.BlendMode)

    If you do that though, you'll have to set the render queue of the material manually, so that it's rendered properly. You can find renderQueue value here : http://docs.unity3d.com/Manual/SL-SubshaderTags.html

    so, for example, in a script, when you toggle the alpha, you can do :


    Code (CSharp):
    1. material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
    2. material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    3. material.SetInt("_ZWrite", 0);
    4. material.renderQueue = 3000; //or 3001 if you want it render just after all transparent objects etc...
     
    jonathans42, Alekxss, Lipoly and 5 others like this.
  4. sunmachine

    sunmachine

    Joined:
    Jun 4, 2014
    Posts:
    9
    Hey Guillaume, thank you very much for the reply. This highlights my misunderstandings very nicely and shines some light on a specific route I have never considered or read about before.

    This is very helpful! I will let you know if this works in 4.6.3. Crossing my fingers!
     
    Lipoly likes this.
  5. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,054
    Thanks for this information as it impacts some shader work i'm currently doing and had been wondering how to deal with an mega shader using blending or not. That in turn had led me to wondering how the new Standard Shader in Unity achieved this and from examining the standardShaderGUI.cs I can see its exactly as you outlined in your post.

    However i'm now somewhat concerned that this means the standard shader is not as efficient as it could or should be due to a lack of being able to control the use of blending in the shader. Whilst overdraw and sorting are issues for transparent blending, when you are meant to be rendering an opaque mesh they shouldn't be.

    This leads me to the big question, does using the blend keyword in a shader mean the gpu is still reading the backbuffer (to get the dst color/alpha) when for an opaque mesh it doesn't need to, and wouldn't such an action add a considerable overhead?

    I'm unsure of the answer, so i'm going to do some digging online and maybe try to build a test suite. If true then I feel either Unity are going to need to update and split their standard shader ( opaque vs transparent) or there needs to be some means added to shader compilation that can provide the ability to dynamically enable or disable blending keyword in the shader completely.

    There is a way to disable blend in a shader by having 'Blend Off', but i'm not sure that can be accomplished dynamically using custom material property drawers since it would need to be in the form 'Blend [ _src ] [ _dst ]'. Would 'Blend Off Off' be valid shaderlab code? If not could this maybe be implemented in a future version, along with adding 'Off' as a enumeration to the existing blendMode attributes?
     
  6. UnityGuillaume

    UnityGuillaume

    Unity Technologies

    Joined:
    Mar 16, 2015
    Posts:
    123
    Unity check and disable blend in case of non blending operation set (source == One, dest == Zero, sourceAlpha == One, destAlpha == Zero)
     
    sunmachine and Noisecrime like this.
  7. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,054
    Thanks for the fast reply and this very useful information.
     
  8. sunmachine

    sunmachine

    Joined:
    Jun 4, 2014
    Posts:
    9
    That looks pretty smart to me!
     
  9. sunmachine

    sunmachine

    Joined:
    Jun 4, 2014
    Posts:
    9
    Here is what I ended up using inside of my material properties in order to access these guys. I love property drawers. I'm working in Unity 4.6.3 presently:

    Code (CSharp):
    1. [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("BlendSource", Float) = 1
    2. [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("BlendDestination", Float) = 0
    3. [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull", Float) = 0
    4. [Toggle] _ZWrite ("ZWrite", Float) = 0

    In my custom inspector, I had it do a check to confirm if the set blending options would enable or disable, and then set the m.renderQueue value that way:

    Code (CSharp):
    1.  
    2. //... GenericCustomMaterialInspector.cs ...
    3. // ... OnInspectorGUI ...
    4. // ... target as Material
    5.  
    6. //Check the blend settings.
    7. int src = target.GetInt("_SrcBlend");
    8. int dst =  target.GetInt("_DstBlend");
    9.  
    10. // Return true if blending. This function just checks if the paired settings are blending or not.
    11. bool isBlending = DoesItBlend(src, dst);
    12.  
    13. //Let's set the render queue based on the current blendmode.
    14. if (isBlending) {
    15.      Debug.Log("GenericCustomMaterialInspector : It blends!");
    16.      target.renderQueue = 3000;
    17.  
    18. } else {
    19.      Debug.Log("GenericCustomMaterialInspector : Don't breathe this!");
    20.      target.renderQueue = 2000;
    21. }
    22.  
    23. Debug.Log (target + " has a renderQueue of " + target.renderQueue + ".");
    24.  
    Thanks for pointing me in the right direction y'all.
     
    Alekxss and Lipoly like this.
  10. echo4papa

    echo4papa

    Joined:
    Mar 26, 2015
    Posts:
    158
    Lipoly likes this.
  11. kofe

    kofe

    Joined:
    Aug 4, 2015
    Posts:
    4
    It's just what I looked for! It was had to find =/
    Thanks)