Search Unity

Set fixed alpha value for final pixel

Discussion in 'Shaders' started by PhilippCh, Aug 23, 2017.

  1. PhilippCh

    PhilippCh

    Joined:
    Jun 14, 2013
    Posts:
    19
    Hi there!

    I'm working on a shader which acts like an intermediary, only good for storing raw values in the RGBA channels of each pixel. For this, I have the following requirements:
    • The shader for some reason only works as a Surface shader, not a legacy fragment shader.
    • The shader has to support writing alpha values.
    • The fragment's alpha value has to override any already present alpha value in the texture, there cannot be any blending of clear color/colors from previously rendered fragments.
    • The fragment's albedo cannot be blended with previously rendered fragments.
    • The shader renders to a RenderTexture (but that shouldn't matter, right?)
    An example output of this shader would be:

    Code (CSharp):
    1.  
    2. initial_clear_color_of_camera = RGBA(0, 0, 0, 0);
    3. albedo = RGB(255, 128, 64);
    4. alpha = 182;
    5.  
    6. final_pixel_on_rendertexture = RGBA(255, 128, 64, 182);
    7.  
    What I currently have is this shader:

    Code (CSharp):
    1.  
    2. Shader "Custom/Selection Id" {
    3.     SubShader {
    4.         Tags { "RenderType"="Transparent" }
    5.         ZWrite on
    6.         LOD 200
    7.        
    8.         CGPROGRAM
    9.         #pragma surface surf Lambert alpha
    10.  
    11.         struct Input {
    12.             float2 uv;
    13.         };
    14.        
    15.       UNITY_INSTANCING_CBUFFER_START(Props)
    16.          UNITY_DEFINE_INSTANCED_PROP(fixed4, _SelectionColor)
    17.       UNITY_INSTANCING_CBUFFER_END
    18.  
    19.         void surf (Input IN, inout SurfaceOutput o) {
    20.             fixed4 c = UNITY_ACCESS_INSTANCED_PROP(_SelectionColor);
    21.            
    22.             o.Emission = c.rgb;
    23.             o.Alpha = c.a;
    24.         }
    25.        
    26.         ENDCG
    27.     }
    28.     FallBack "Diffuse"
    29. }
    30.  
    I can see that ZWrite on doesn't seem to have any positive effect on the resulting output color.

    So in short, do you have any idea on how to use my fragment's alpha as the final alpha of the pixel without blending my fragment's albedo with the underlying clear color?

    Cheers
    Philipp
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    What do you mean by "legacy" fragment shader? Surface shaders are vertex fragment shaders, and do a lot of work you don't want or need. I would suggest you post the vertex fragment shader you were trying to use and we can go from there, as what you want should be pretty close to the simpliest, most basic, default behavior for a shader.

    Are you using Blit(), or rendering with a camera that has the render texture as it's target, or using SetRenderTarget & Draw commands? Honestly, shouldn't really matter, but can change some minor things in the way you handle this.


    Going from your surface shader, this is what I assume you want.
    Code (CSharp):
    1. Shader "Custom/Selection Id"
    2. {
    3.     SubShader
    4.     {
    5.         Tags { "RenderType"="Transparent" }
    6.  
    7.         Pass
    8.         {
    9.             CGPROGRAM
    10.             #pragma vertex vert
    11.             #pragma fragment frag
    12.          
    13.             #include "UnityCG.cginc"
    14.  
    15.             UNITY_INSTANCING_CBUFFER_START(Props)
    16.                 UNITY_DEFINE_INSTANCED_PROP(fixed4, _SelectionColor)
    17.             UNITY_INSTANCING_CBUFFER_END
    18.          
    19.             float4 vert (appdata_full v) : SV_POSITION
    20.             {
    21.                 return UnityObjectToClipPos(v.vertex);
    22.             }
    23.          
    24.             fixed4 frag (v2f i) : SV_Target
    25.             {
    26.                 return UNITY_ACCESS_INSTANCED_PROP(_SelectionColor);
    27.             }
    28.             ENDCG
    29.         }
    30.     }
    31. }
    32.  
     
  3. PhilippCh

    PhilippCh

    Joined:
    Jun 14, 2013
    Posts:
    19
    I was just referring to regular vertex and fragment shaders. I know that surface shaders are compiled down to regular vf shaders, but for some reason, when initially testing this idea, I used a fragment shader that wasn't properly rendering at all. I don't have it in my VC at all, so unfortunately am unable to procure it.

    The great news is that your proposed shader is working (almost) perfectly! I made some small additions since Unity was throwing errors regarding v2f not existing, but kept the rest to your suggestion.

    I'm rendering certain objects in my scene using a second camera with Replaced Shaders to a RenderTexture. Atm, I don't really care about rendering order, so I'm just grabbing what's currently available on the texture during Update().

    Now the only issue that persists with your solution is the fact that the shader doesn't properly write to the Z-Buffer. Check out the GIF I made to see the issue:

    What should happen is that as soon as the cursor moves over the foremost object, it should get highlighted instead of the object behind it. I tried adding ZWrite On to the shader, but unfortunately to no effect.

    This is what I currently have:

    Code (CSharp):
    1. Shader "Custom/Selection Id"
    2. {
    3.     SubShader
    4.     {
    5.         Tags { "RenderType"="Transparent" }
    6.         Pass
    7.         {
    8.             ZWrite On
    9.          
    10.             CGPROGRAM
    11.             #pragma vertex vert
    12.             #pragma fragment frag
    13.        
    14.             #include "UnityCG.cginc"
    15.             UNITY_INSTANCING_CBUFFER_START(Props)
    16.                 UNITY_DEFINE_INSTANCED_PROP(fixed4, _SelectionColor)
    17.             UNITY_INSTANCING_CBUFFER_END
    18.        
    19.             struct v2f {
    20.                 float4 pos : SV_POSITION;
    21.             };
    22.        
    23.             v2f vert (appdata_full v) : SV_POSITION
    24.             {
    25.                 v2f o;
    26.              
    27.                 o.pos = UnityObjectToClipPos(v.vertex);
    28.              
    29.                 return o;
    30.             }
    31.        
    32.             fixed4 frag (v2f i) : SV_Target
    33.             {
    34.                 return UNITY_ACCESS_INSTANCED_PROP(_SelectionColor);
    35.             }
    36.             ENDCG
    37.         }
    38.     }
    39. }
    Again, thanks so much for your help!
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You should just delete the v2f i in the frag() rather than adding a v2f. It's unnecessary and now you have two things writing to SV_POSITION. Honestly surprised your edit compiles either.

    Shaders default to writing to the Z buffer, so adding it to a shader that doesn't have it will make no difference.

    I think you forgot something. ;)
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Oh, random thought, does your RenderTexture have a depth buffer enabled?
     
  6. PhilippCh

    PhilippCh

    Joined:
    Jun 14, 2013
    Posts:
    19
    It doesn't compile without v2f added. I get that passing a single fixed4 to the fragment shader would be beneficial, but Unity complains about semantics when switching v2f to fixed4.

    Afaik, RenderType Transparent shaders do not write to the Z-Buffer by default, hence I turned it on manually.

    My RenderTexture has a 24bit Depth Buffer enabled, so that can't be it.

    Sorry about the missing GIF, here it is:

    https://gfycat.com/ValuableWeeklyArthropods
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You don't actually want to pass anything from the vertex function to the fragment function. There's no benefit here as you don't want any of that data and it'll cost more to read the _SelectionColor in the vert() and pass it to the frag() than just read it directly in the frag().

    This should compile with out an issue.
    Code (CSharp):
    1. Shader "Custom/Selection Id"
    2. {
    3.     SubShader
    4.     {
    5.         Tags { "RenderType"="Transparent" }
    6.         Pass
    7.         {
    8.             CGPROGRAM
    9.             #pragma vertex vert
    10.             #pragma fragment frag
    11.        
    12.             #include "UnityCG.cginc"
    13.             UNITY_INSTANCING_CBUFFER_START(Props)
    14.                 UNITY_DEFINE_INSTANCED_PROP(fixed4, _SelectionColor)
    15.             UNITY_INSTANCING_CBUFFER_END
    16.        
    17.             float4 vert (float4 vertex : POSITION) : SV_POSITION
    18.             {
    19.                 return UnityObjectToClipPos(v.vertex);
    20.             }
    21.        
    22.             fixed4 frag () : SV_Target
    23.             {
    24.                 return UNITY_ACCESS_INSTANCED_PROP(_SelectionColor);
    25.             }
    26.             ENDCG
    27.         }
    28.     }
    29. }
    Note I removed the v2f and the appdata in this one. They're both extraneous since we only need one input and one output from the vert().

    The "RenderType" Tag in itself doesn't do anything. That's there purely for replacement shaders, like what you're doing. You could add that line to a normal opaque shader and see no change apart from how it shows up in certain camera textures (specifically the _CameraDepthNormalTexture) and replacement shader passes. It has no affect on the other rendering qualities. "Queue"="Transparent" does affect rendering to a degree, but still does not disable Zwrite or blend modes, just changes when it is rendered and the order it sorts meshes to render in.

    Have you confirmed there is actually anything else rendering in the replacement shader pass? Maybe have stuff other than the highlighted objects render out green, or some other random color, then use the frame debugger to step through?
     
  8. PhilippCh

    PhilippCh

    Joined:
    Jun 14, 2013
    Posts:
    19
    Again, thank you so much for your help! Looking over my setup code for the RenderTexture, I noticed that the Inspector was showing me "At least 24bit" for Depth, but I wasn't setting it through my code. Setting the depth explicitly solved the issue.

    I was always under the impression that you were required to pass at least something to the fragment shader, but your solution removing v2f and appdata works perfectly fine.