Search Unity

Alpha to Coverage doesn't work reliably on mobile devices

Discussion in 'Shaders' started by Arycama, May 30, 2019.

  1. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    185
    After testing with some mobile GPU's, it seems alpha-to-coverage can not be reliably determined based on shader-targets alone.

    I have a simple alpha-tested foliage shader. It uses #pragma 3.5 to enable an alpha-to-coverage subshader, otherwise it falls back to a regular alpha-test. This works as expected on a Galaxy S4. Changing the target Graphics API in Player Settings from 3.0 to 2.0, causes it to fall back correctly. When using GLES 3.0, the alpha-to-coverage shader works correctly with anti-aliased transparency.

    However on a Galaxy S8 (Which definitely supports target 3.5), the fallback does not work. It still runs the alpha-to-mask shader but as the feature does not appear to be supported, it outputs a fully opaque surface.

    There needs to be a #pragma require alphatocoverage or similar, so that this feature can be reliably detected on mobile devices, and a suitable fallback used otherwise.

    The relevant frag shader is below:

    Code (CSharp):
    1. fixed4 frag(v2f i) : SV_Target
    2. {
    3.     fixed4 color = tex2D(_MainTex, i.uv);
    4.  
    5.     // Handle alpha to mask
    6.     #if SHADER_TARGET < 35
    7.         clip(color.a - _Cutoff);
    8.     #else
    9.         color.a = (color.a - _Cutoff) / max(fwidth(color.a), 0.0001) + 0.5;
    10.     #endif
    11.  
    12.     return color;
    13. }
    The two Shader blocks which call this function are setup like this, so that the first subshader will correctly fall back to the second subshader if 3.5 target is not supported.

    Code (CSharp):
    1.     SubShader
    2.     {
    3.         Pass
    4.         {
    5.             AlphaToMask On
    6.  
    7.             CGPROGRAM
    8.             #pragma vertex vert
    9.             #pragma fragment frag
    10.             #pragma target 3.5
    11.  
    12.             // Fragment shader from above goes here
    13.  
    14.             ENDCG
    15.         }
    16.     }
    17.  
    18.     SubShader
    19.     {
    20.         Pass
    21.         {
    22.             AlphaToMask Off
    23.  
    24.             CGPROGRAM
    25.             #pragma vertex vert
    26.             #pragma fragment frag
    27.  
    28.             // Fragment shader from above goes here
    29.  
    30.             ENDCG
    31.         }
    32.     }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    AFAIK alpha to coverage works on the S8, several GearVR titles make use of A2C so it would be quite surprising if it doesn’t work on the S8.

    Have you confirmed that the correct sub shader is running, and it’s not a bug with SHADER_TARGET? Also, make sure your quality settings all have MSAA enabled. I know on PC if you don’t have MSAA enabled it’ll behave identical to clip(), but on GLES devices A2C is completely ignored.

    However there’s no harm in using clip with the A2C path to work around the issue. Just use a constant clip like clip(color.a - 0.1); after rescaling the alpha. That should work up to 4x MSAA, you can use - 0.06 if you want to handle 8x MSAA.