Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Surface shader keepalpha behaviour

Discussion in 'Shaders' started by sixolar, Dec 11, 2018.

  1. sixolar

    sixolar

    Joined:
    Jan 8, 2014
    Posts:
    37
    Hello !

    I made this :
    Code (CSharp):
    1. Shader "Custom/DetailColor" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    5.         _DetailColor ("DetailColor", Color) = (1,1,1,1)
    6.         _DetailMask ("Mask", 2D) = "white" {}
    7.         _Emission ("Emission", Color) = (0, 0, 0, 1)
    8.         _EmissionMap ("EmissionMap", 2D) = "white" {}
    9.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    10.         _Metallic ("Metallic", Range(0,1)) = 0.0
    11.        
    12.         // Blending state
    13.         [HideInInspector] _SrcBlend("__src", Float) = 1.0
    14.         [HideInInspector] _DstBlend("__dst", Float) = 0.0
    15.         [HideInInspector] _ZWrite("__zw", Float) = 1.0
    16.     }
    17.     SubShader {
    18.         Tags {"RenderType"="Opaque"}
    19.         LOD 200
    20.  
    21.         Blend[_SrcBlend][_DstBlend]
    22.         ZWrite[_ZWrite]
    23.  
    24.         CGPROGRAM
    25.        
    26.         #pragma multi_compile __ DETAIL_MASK
    27.         #pragma multi_compile ___ EMISSION
    28.         #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    29.  
    30.         // Physically based Standard lighting model, and enable shadows on all light types
    31.         #pragma surface surf Standard fullforwardshadows keepalpha
    32.  
    33.         // Use shader model 3.0 target, to get nicer looking lighting
    34.         #pragma target 3.0
    35.  
    36.         sampler2D _MainTex;
    37.  
    38.         struct Input {
    39.             float2 uv_MainTex;
    40.         };
    41.  
    42.         half _Glossiness;
    43.         half _Metallic;
    44.         fixed4 _Color;
    45.         fixed4 _DetailColor;
    46.         sampler2D _DetailMask;
    47.         fixed4 _Emission;
    48.         sampler2D _EmissionMap;
    49.  
    50.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    51.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    52.         // #pragma instancing_options assumeuniformscaling
    53.         UNITY_INSTANCING_BUFFER_START(Props)
    54.             // put more per-instance properties here
    55.         UNITY_INSTANCING_BUFFER_END(Props)
    56.  
    57.         void surf (Input IN, inout SurfaceOutputStandard o) {
    58.             // Albedo comes from a texture tinted by color
    59.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    60.             #if defined(DETAIL_MASK)
    61.                 c = lerp (_DetailColor * c, c, tex2D(_DetailMask, IN.uv_MainTex).a);
    62.             #else
    63.                 c = lerp (_DetailColor * c, c, tex2D(_MainTex, IN.uv_MainTex).a);
    64.             #endif
    65.             #if defined(EMISSION)
    66.                 fixed4 e = tex2D (_EmissionMap, IN.uv_MainTex);
    67.                 o.Emission = lerp (_Emission * e, fixed4 (0, 0, 0, 0), e.a);
    68.             #endif
    69.             o.Albedo = c.rgb;
    70.             // Metallic and smoothness come from slider variables
    71.             o.Metallic = _Metallic;
    72.             o.Smoothness = _Glossiness;
    73.             o.Alpha = c.a;
    74.         }
    75.         ENDCG
    76.     }
    77.  
    78.     FallBack "Diffuse"
    79.     CustomEditor "DetailColorGUI"
    80. }
    It's an attempt to have 2 uses in one shader :
    - I have opaque objects, and I want to apply a color to the parts of the main texture that have alpha (that's working fine)
    - I have a fade object, and I want to apply a color to the main texture following a mask, while the alpha of the main texture is totally transparent (that's working too, minus the last part...)

    This is done by enabling keywords based on whether or not there is a mask texture in a custom inspector.

    Code (CSharp):
    1.  
    2.                 SetKeyword(material, "DETAIL_MASK", true);
    3.                 material.SetOverrideTag("RenderType", "Transparent");
    4.                 material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
    5.                 material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    6.                 material.SetInt("_ZWrite", 0);
    7.                 material.DisableKeyword("_ALPHATEST_ON");
    8.                 material.EnableKeyword("_ALPHABLEND_ON");
    9.                 material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    10.                 material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
    However, in the second case, the transparent part of the object seem to receive lighting from point lights (not directional nor ambient at least), and it's not the case if I use alpha:fade in the pragma, but then the first case doesn't work anymore...
    no point light :
    nolight.PNG
    point light :
    light.PNG

    How can I have the same result as with alpha:fade with keepalpha? Am I missing a keyword or something?

    Thanks !
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Part of the problem is these keywords don't really do anything in Surface Shaders. They help inform the Standard shader on how to handle different opacity modes, but that's because of code specific to the Standard shader itself. A Surface shader does much of the same stuff, but based purely on the existence and type of alpha parameter used, and that cannot be switched based on keywords or material properties.

    However one of the biggest difference is the how the Blend mode is set for the forward add pass. Specifically, changing the rendering mode on the Standard shader changes the blend mode of the forward add pass by using the same _SrcBlend property on both the base and add passes, but a Surface Shader's forward add pass is hard coded and cannot be modified at runtime. It's totally controlled by that alpha parameter.

    The good news is you can deal with this. The default for keepalpha is Blend One One, which works for both opaque and alpha:premul, but for alpha:fade you want Blend SrcAlpha One. And you can very easily replicate a Blend SrcAlpha One with a Blend One One by multiplying the output rgb color by the alpha in the shader. And you can modify the final output color of a Surface shader using a finalcolor function. Now you only want to do this when rendering the forward add pass, which again is easy to test for.

    Code (csharp):
    1. #pragma surface surf Standard fullforwardshadows keepalpha finalcolor:ApplyForwardAddBlendMode
    2.  
    3. void ApplyForwardAddBlendMode (Input IN, SurfaceOutputStandard o, inout fixed4 color)
    4. {
    5.   #ifdef UNITY_PASS_FORWARDADD
    6.   color.rgb *= color.a;
    7.   #endif
    8. }
     
    WonkeeKim likes this.
  3. sixolar

    sixolar

    Joined:
    Jan 8, 2014
    Posts:
    37
    Wow, thank you so much !