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 does not write alpha value

Discussion in 'Shaders' started by iivo_k, Jun 18, 2015.

  1. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Using Unity 5.1.0f3.

    I have a scene with a RenderTexture set as target to a Camera. There's also a script attached to the camera, that exports the RenderTexture to a PNG.

    Objects using the new standard shader are rendered properly, opaque or transparent.

    However, when using a custom surface shader, as soon as my shader has "alpha" as surface shader parameter, the shader stops writing alpha values to the RenderTexture.

    Scene view & Camera preview
    CameraPreview.png

    Resulting PNG:
    GeneratedTexture.png


    What can I do to make the shader render the alpha values to the RenderTexture? Is this a bug or some weird surface shader limitation? I want to avoid having to write all the shaders again as CG shaders and figuring out all the lighting and shadow stuff.

    I attached my simple test project as a UnityPackage.

    Here's the shader code:

    Code (CSharp):
    1.  
    2. Shader "Custom/TestShader"
    3. {
    4.    Properties
    5.    {
    6.      _Color ("Tint Color", Color) = (1,1,1,1)
    7.      _MainTex ("Main Texture", 2D) = "white" {}
    8.    }
    9.  
    10.    SubShader
    11.    {
    12.      Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    13.      LOD 200
    14.    
    15. CGPROGRAM
    16.      #pragma surface surf Lambert alpha
    17.      #pragma fragmentoption ARB_precision_hint_fastest
    18.    
    19.      fixed4 _Color;
    20.      sampler2D _MainTex;        
    21.  
    22.      struct Input
    23.      {
    24.        float2 uv_MainTex : TEXCOORD0;
    25.      };
    26.    
    27.      void surf (Input IN, inout SurfaceOutput o)
    28.      {
    29.        fixed4 col = _Color * tex2D(_MainTex, IN.uv_MainTex);
    30.        o.Albedo = col.rgb;
    31.        o.Alpha = col.a;
    32.      }
    33. ENDCG
    34.    }
    35.    Fallback "Diffuse"
    36. }
    37.  
    Changing the line
    #pragma surface surf Lambert alpha
    to
    #pragma surface surf Lambert
    makes the items render fully but without transparency.
     

    Attached Files:

    Last edited: Jun 18, 2015
  2. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    Everything looks fine to me, but there are two things you might want to check:

    1) Make sure your camera has a clear color (unless you had something else in mind)
    2) Discard fully transparent pixels from your shader*

    * What I mean by this is that even your fully transparent pixels with alpha working correctly will go on to the lighting stage unless you discard them. There's a way to do it with shader properties (AlphaTest or something), but I do it with code in the shader. You can add something like this to your surface shader "surf":
    Code (CSharp):
    1. if (col.a < 0.05f)
    2. {
    3.     discard;
    4. }
     
  3. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    1) The pink in the camera preview is the clear color.
    2) I'm not sure how discarding would help, the problem is that no matter what I set the alpha value to in my shader, it's ignored.
     
  4. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    Try removing "alpha" from
    Code (CSharp):
    1. #pragma surface surf Lambert alpha
    and add this inside the subshader (line 14ish in your code block):
    Code (CSharp):
    1. Blend SrcAlpha OneMinusSrcAlpha
     
  5. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    That was actually one of the first things I tried. Removing "alpha" just makes the objects opaque and the Blend parameter has no effect.
     
  6. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Actually, doesn't seem to be related to RenderTexture. I tried using Texture2D.ReadPixels to read directly from the screen and the shader still doesn't write alpha.
     
  7. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
  8. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Doesn't affect the outcome. Says it's for opaque shaders too.
     
  9. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,051
    I believe the problem you are facing here is that for transparent surface shaders Unity automatically masks out the alpha channel using 'ColorMask RGB'. This means the alpha channel/component is never rendered to in the screen buffer or rendertexture.

    I'm unsure if you can fix this directly within surface shaders, but you can fix it by using pre-compiled versions of the shaders. If you need to use the default Unity shaders then download the 'built-in shaders' pack from Unity for the version you are using, then

    1. Create or add shader to Unity Project.
    2. Select shader.
    3. In the inspector click on 'Show Generated code'
    4. In MonoDevelop when it opens the compiled shader, do select all and copy.
    5. Create a new shader, give it an appropriate name.
    6. Paste the compiled shader into this one, remember to make sure its using a unique shader name.
    7. Search for 'ColorMask RGB' and replace with 'ColorMask RGBA', or actually you could probably just comment it out completely.
    8. Profit!

    Obviously its quite a bit of a pain, not sure if there is an easier way, but at least it only applies to transparent shaders. Be careful when copying compiled shader code that the shader is set up to generate all permutations and api versions you'll need.
     
    Last edited: Jun 23, 2015
    Henof likes this.
  10. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    Yeah, but thought it would be worth a try regardless. I think Noisecrime's got a very intuitive thought. I would think that's quite likely to be the issue.
     
  11. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Well I ended up using my old CG shader in this situation, but I'd really like a dev explanation on why it's like this with surface shaders.
     
  12. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,051
    Having checked it would appear the ColorMask setting is solely due to legacy reasons and Unity's old alpha-based glow technique. Hopefully this means it will be addressed in future release and removed or made optional.
     
  13. Bravo_cr

    Bravo_cr

    Joined:
    Jul 19, 2012
    Posts:
    148
    I just hit my face with this issue. This is still happening. Any clues?
     
  14. SoftwareGeezers

    SoftwareGeezers

    Joined:
    Jun 22, 2013
    Posts:
    902
    I have the same problem. My custom shaders aren't drawing to the alpha in rendertextures and my objects are invisible. Does writing pure CG shaders fix this?

    Why doesn't Unity provide a decent set of mobile shaders?!?! I don't want frickin' physically based uber-real objects in my mobile game. I want normal mapped sprites that can be used for efficient rendering using the full range of techniques. Years on from 2D mode and 2D is still an uphill struggle with problem after problem caused by lack of attention from Unity. It really wouldn't take them long to put together a batch of mobile optimised shaders to match the capabilities of the past few years of devices.
     
  15. BurgerBob

    BurgerBob

    Joined:
    Feb 10, 2014
    Posts:
    3
    I had the same problem and I found a solution. Just add a simple pass ColorMask A to write what you want to write in the alpha channel.

    Code (CSharp):
    1. SubShader {
    2.         Pass
    3.         {
    4.             Tags
    5.             {
    6.                 "Queue" = "Transparent"
    7.                 "IgnoreProjector" = "True"
    8.                 "RenderType" = "Transparent"
    9.                 "PreviewType" = "Plane"
    10.                 "CanUseSpriteAtlas" = "True"
    11.             }
    12.  
    13.             Cull Off
    14.             Lighting Off
    15.             ZWrite Off
    16.             Blend One OneMinusSrcAlpha
    17.             ColorMask A
    18.  
    19.             CGPROGRAM
    20.             #pragma vertex SpriteVert
    21.             #pragma fragment frag
    22.             #pragma target 2.0
    23.             #include "UnitySprites.cginc"
    24.  
    25.             fixed4 frag(v2f IN) : SV_Target
    26.             {
    27.                 fixed4 c = SampleSpriteTexture(IN.texcoord);
    28.                 return c;
    29.             }
    30.             ENDCG
    31.            
    32.         }
    33.        
    34.         Tags {
    35.             "RenderType"="Transparent"
    36.             "Queue" = "Transparent"
    37.         }
    38.         LOD 200
    39.        
    40.         Blend One OneMinusSrcAlpha
    41.         ZWrite Off
    42.         Cull Off
    43.         ColorMask RGB
    44.  
    45.         CGPROGRAM
    46.         // Physically based Standard lighting model, and enable shadows on all light types
    47.         #pragma surface surf Standard fullforwardshadows alpha:fade alpha:auto  //vertex:vert
    48.  
     
  16. soulburner

    soulburner

    Joined:
    Mar 25, 2012
    Posts:
    169
    Just bumped into the issue.
    When adding
    Code (CSharp):
    1. alphatest:_Cutoff
    the result alpha channel is empty. But making a manual discard like this:
    Code (CSharp):
    1. clip(c.a-_Cutoff);
    fixes the issue.

    Have no idea why...
     
  17. ancientfear

    ancientfear

    Joined:
    Feb 28, 2017
    Posts:
    2
    Thanks for this post! I had the same problem in my old Unity 5.6.
    My surface shader tend to skip alpha for my render texture. Although the camera rendered the object correctly to the editor small window || directly to screen. Adding alpha || alpha:fade || others options inside shader had incorrect rendering. So, i've tried the solution posted by by Noisecrime. It worked out. Indeed, Mask RGB to Mask RGBA is an elegant solution.