Search Unity

Transparent Depth Shader (good for ghosts!)

Discussion in 'Shaders' started by Tim-C, Aug 31, 2012.

  1. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    So one of the things you will encounter when rendering complex meshs' using standard transparent rendering is that they depth test against the scene, but not against themselves. This is a limitation of how depth tested rendering works and there are some ways to work around this issue. I'm going to talk about one of them, then show you how to do it in Unity.

    So lets start by rendering a scene that has some complex geometry (in this context I am using complex to refer to geometry where there are overlaps and folds):


    You will notice that in this image there are areas of darkness where the object has been rendered twice. This occurs because there is no depth buffer for the transparent object and overlapping polygons will cause pixels to be rendered more then once. Taking a step back, the solution is to only have these pixels be rendered once, and only for the front most pixel. How can this be set up?

    Looking into the default surface shader for transparent objects you will see that it looks like this:

    Code (csharp):
    1.  
    2. Shader "Transparent/Diffuse" {
    3. Properties {
    4.     _Color ("Main Color", Color) = (1,1,1,1)
    5.     _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    6. }
    7.  
    8. SubShader {
    9.     Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    10.     LOD 200
    11.  
    12. CGPROGRAM
    13. #pragma surface surf Lambert alpha
    14.  
    15. sampler2D _MainTex;
    16. fixed4 _Color;
    17.  
    18. struct Input {
    19.     float2 uv_MainTex;
    20. };
    21.  
    22. void surf (Input IN, inout SurfaceOutput o) {
    23.     fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    24.     o.Albedo = c.rgb;
    25.     o.Alpha = c.a;
    26. }
    27. ENDCG
    28. }
    29. Fallback "Transparent/VertexLit"
    30. }
    31.  
    It's a simple shader that just sets up and configures the blend modes; this is done implicitly via the 'alpha' tag to the surface pragma. You can mimic this yourself and remove the 'alpha' pragma if you want... but it's not necessary.

    So what is the root issue? When this shader is executed there is nothing in the z-buffer to indicate which pixel is on top. So what we need to do is prime the z-buffer with real depth values. This needs to be done JUST before the transparent object is rendered so that ordering and stacking of transparent objects works properly.

    The easiest way to do this is to add an extra pass to the surface shader. A little secret when it comes to surface shaders is that you can insert your own passes, either before or after the main surface shader block. They will just be executed as part of the rendering pipeline.

    So lets add a depth only pass to the start of the surface shader, you'll notice that the rest of the shader remains the same:
    Code (csharp):
    1.  
    2. Shader "Custom/Ghost" {
    3. Properties {
    4.     _Color ("Main Color", Color) = (1,1,1,1)
    5.     _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
    6. }
    7.  
    8. SubShader {
    9.     Tags {"RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True"}
    10.     LOD 200
    11.    
    12.     Pass {
    13.         ZWrite On
    14.         ColorMask 0
    15.    
    16.         CGPROGRAM
    17.         #pragma vertex vert
    18.         #pragma fragment frag
    19.         #include "UnityCG.cginc"
    20.  
    21.         struct v2f {
    22.             float4 pos : SV_POSITION;
    23.         };
    24.  
    25.         v2f vert (appdata_base v)
    26.         {
    27.             v2f o;
    28.             o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    29.             return o;
    30.         }
    31.  
    32.         half4 frag (v2f i) : COLOR
    33.         {
    34.             return half4 (0);
    35.         }
    36.         ENDCG  
    37.     }
    38.    
    39.     CGPROGRAM
    40.     #pragma surface surf Lambert alpha
    41.     #pragma debug
    42.  
    43.     sampler2D _MainTex;
    44.     fixed4 _Color;
    45.  
    46.     struct Input {
    47.         float2 uv_MainTex;
    48.     };
    49.  
    50.     void surf (Input IN, inout SurfaceOutput o) {
    51.         fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    52.         o.Albedo = c.rgb;
    53.         o.Alpha = c.a;
    54.     }
    55.     ENDCG
    56. }
    57.  
    58. Fallback "Transparent/Diffuse"
    59. }
    60.  
    This pass simply sets color writes to off (using the colormask), and depth writes to true. Plugging this shader into out character now looks like this:


    The biggest issue with this method is that overlapping objects need to be sorted nicely. Unity does a good job at this for the most part. This also needs an extra draw call. If you are worried about performance remember to profile.

    This method of inserting extra passes into surface shaders can be used for a lot of cool things, you should have an experiment and see what else you can do.
     
    laurentlavigne and MJHughes like this.
  2. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    Well, the second issue with this method is that you need a second render pass which costs performance.
     
  3. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    That's true. Depth only passes are super fast though. When you don't write to the color buffer most hardware has fast-z writes. As always profile :)

    I've updated the post to reflect this.
     
    Gamba04 likes this.
  4. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
  5. Paradoks

    Paradoks

    Joined:
    Oct 13, 2009
    Posts:
    436
    How could i make this shader to be allways visible ? and keep the none overlapping transparency ?
    i cant get rid of back faces showing.
     
  6. mouurusai

    mouurusai

    Joined:
    Dec 2, 2011
    Posts:
    350
    You can't, use stencil test instead.
    upd
    I think once again about that, it's possible with separate camera or rendertexture.
     
    Last edited: Nov 18, 2014
  7. Gua

    Gua

    Joined:
    Oct 29, 2012
    Posts:
    455
    Got this error
     
    DezBoyle likes this.
  8. gkillz

    gkillz

    Joined:
    Nov 30, 2012
    Posts:
    4
    This shader doesnt seams to work on mobile devices? (tested it on Android Samsumg s6 )
    what can be done to get it working there?
    Is it the zwrite on and colormask 0 which are causing it?
     
  9. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    You need to change the half4 constructer params....

    Code (csharp):
    1.  
    2. return half4 (0);
    3.  
    4. to
    5.  
    6. return half4 (0,0,0,0);
    7.  
     
    RenanRL and DezBoyle like this.
  10. HonoraryBob

    HonoraryBob

    Joined:
    May 26, 2011
    Posts:
    1,214

    This doesn't seem to work with Unity 5? Overlapping mesh sections are still noticeable even when using the exact code above.
     
    DezBoyle likes this.
  11. HonoraryBob

    HonoraryBob

    Joined:
    May 26, 2011
    Posts:
    1,214
    Or does this work only in Deferred mode?
     
  12. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    If anyone is interested, I have implemented this to work with the Unity Standard Shader (so it's a modified Standard Shader but it has all the same features).
    So it works for Unity 5 and 2017
     
    Salim likes this.
  13. HonoraryBob

    HonoraryBob

    Joined:
    May 26, 2011
    Posts:
    1,214
    I'd be interested.
     
    DezBoyle likes this.
  14. ntstudios

    ntstudios

    Joined:
    Apr 29, 2017
    Posts:
    1
    Very interested, please share with us.
     
    DezBoyle likes this.
  15. Mauri

    Mauri

    Joined:
    Dec 9, 2010
    Posts:
    2,664
  16. dspmania

    dspmania

    Joined:
    Oct 23, 2009
    Posts:
    6
    I'm very interested too. Please share with us.
     
  17. Salim

    Salim

    Joined:
    Apr 25, 2011
    Posts:
    148
    I would be interested as well, thank you.
     
  18. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    I'd be really interested, many thanks!
     
  19. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Sorry I only see your requests today. I will post it when I get home
     
    Salim likes this.
  20. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    Thanks a lot!
     
  21. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    DezBoyle likes this.
  22. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    Thanks!!!
     
  23. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    You're welcome. Hope you find a good use for it !
     
    Salim likes this.
  24. Thibault_Lesaunier

    Thibault_Lesaunier

    Joined:
    Feb 8, 2018
    Posts:
    7
    Hi guys,

    This shader is amazing but I've a problem when I want to apply a Cut Plane to my object.

    A picture to illustrate :

    upload_2018-4-10_11-31-16.png

    We can see when i cut the object, hte "Cut" pixel are always here and hides me the other part of the object that I have to see.

    Any idea ?

    Thanks
     
  25. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Can you share a unity project that shows the problem and maybe an image explaining exactly how you would want it to be ?
     
  26. Thibault_Lesaunier

    Thibault_Lesaunier

    Joined:
    Feb 8, 2018
    Posts:
    7
    Hi,

    I've an object. I'm applying a plane on it. This plane defines which pixels I want to be visible and the other have to be unvisible.

    upload_2018-4-10_13-40-47.png

    Here it's the object with an other point of view. We see the object is cut on his left.

    upload_2018-4-10_13-41-43.png

    Here is the object see from the plane point of view. The plane has already cut some part of the object. But we see that these parts are always here because they hide the other part of the object.


    The big problem is I can't change dynamically the opcaity of an object at runtime without putting an alpha pragma to my shader code. But this alpha pragma break all my object with some artefacts because Unity is unable to manage Fade & ZDepth.

    I just want an "opaque" object (and my CutPlane works well with it) but change the opacity dynamically too.
     
    Last edited: Apr 10, 2018
  27. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    What shader are you using for the cutting ?
     
  28. Thibault_Lesaunier

    Thibault_Lesaunier

    Joined:
    Feb 8, 2018
    Posts:
    7
    This one :
    Code (CSharp):
    1.  
    2. Shader "Custom/Clipping" {
    3.    
    4.    Properties {
    5.       _Conversion ("Conversion (RGB)", 2D) = "white" {}      
    6.       _MainTex ("Main Texture", 2D) = "white" {}          
    7.      _NormalxValue("Normal X Value", Float) = 1
    8.      _Glossiness ("Smoothness", Range(0,1)) = 0.5  
    9.      _Metallic ("Metallic", Range(0,1)) = 0.0
    10.      _Color ("Color", Color) = (0.5,0.5,0.5,1)
    11.     }
    12.     SubShader {
    13.        Tags {  "RenderType" = "TransparentCutout" "DisableBatching" = "True"  }
    14.        LOD 200
    15.      
    16.        Cull Off
    17.  
    18.        Pass
    19.        {
    20.          ZWrite On
    21.          ColorMask 0
    22.  
    23.          CGPROGRAM
    24.          #pragma vertex vert
    25.          #pragma fragment frag
    26.          #include "UnityCG.cginc"
    27.      
    28.          float3 _PlaneNormal;
    29.  
    30.          uniform float _xValue;
    31.          uniform float _yValue;
    32.          uniform float _zValue;
    33.  
    34.          uniform float _NormalxValue;
    35.          uniform float _NormalyValue;
    36.          uniform float _NormalzValue;
    37.  
    38.          uniform half _Radius;
    39.      
    40.          float3 _Origin;
    41.  
    42.          uniform float _xIValue;
    43.          uniform float _yIValue;
    44.          uniform float _zIValue;
    45.  
    46.          uniform float _signe;
    47.          uniform float _InOut;
    48.  
    49.          struct v2f {
    50.            float4 pos : SV_POSITION;
    51.            float4 worldPos : world ;
    52.          };
    53.  
    54.          v2f vert (appdata_base v)
    55.          {
    56.            v2f o;      
    57.            o.worldPos =  mul(unity_ObjectToWorld, v.vertex);
    58.            o.pos = UnityObjectToClipPos (v.vertex); //o.pos = 0 ==> no Depth
    59.            return o;
    60.          }
    61.          half4 frag (v2f i) : COLOR
    62.          {
    63.            _PlaneNormal = float3(_NormalxValue,_NormalyValue,_NormalzValue);
    64.            _PlaneNormal = normalize(_PlaneNormal);
    65.            half dist = (i.worldPos.x * _PlaneNormal.x) + (i.worldPos.y * _PlaneNormal.y) + (i.worldPos.z * _PlaneNormal.z)
    66.                    - (_xValue * _PlaneNormal.x) - (_yValue * _PlaneNormal.y) - (_zValue * _PlaneNormal.z)
    67.                    / sqrt( pow(_PlaneNormal.x, 2) + pow(_PlaneNormal.y, 2) + pow(_PlaneNormal.z,2));
    68.          
    69.            if(any(dist < 0))
    70.            {
    71.                discard;
    72.            }
    73.  
    74.            _Origin = float3( _xIValue,_yIValue, _zIValue);
    75.        
    76.            half distIarea = distance( i.worldPos, _Origin);
    77.  
    78.            if(_InOut == 1)
    79.            {  
    80.                clip (_signe * (distIarea - _Radius));
    81.            }
    82.  
    83.            return half4 (0,0,0,1);
    84.          }
    85.          ENDCG  
    86.        }
    87.        CGPROGRAM
    88.  
    89.          #pragma surface surf Standard alpha
    90.          #pragma target 3.0
    91.        
    92.          struct Input {
    93.              float2 uv_MainTex;    
    94.              float2 uv_BumpMap;      
    95.              float2 uv_Conversion;    
    96.              float3 worldPos;  
    97.          };  
    98.          half _Glossiness;
    99.          half _Metallic;
    100.          fixed4 _Color;
    101.  
    102.          float3 _PlaneNormal;
    103.  
    104.          uniform float _xValue;
    105.          uniform float _yValue;
    106.          uniform float _zValue;
    107.  
    108.          uniform float _NormalxValue;
    109.          uniform float _NormalyValue;
    110.          uniform float _NormalzValue;
    111.  
    112.          uniform half _Radius;
    113.      
    114.          uniform float3 _Origin;
    115.  
    116.          uniform float _xIValue;
    117.          uniform float _yIValue;
    118.          uniform float _zIValue;
    119.  
    120.          uniform float _signe; // 1 ou -1
    121.          uniform float _InOut;  // 0 ou 1
    122.  
    123.          sampler2D _MainTex;
    124.          sampler2D _BumpMap;
    125.          sampler2D _Conversion;
    126.        
    127.          void surf (Input IN, inout SurfaceOutputStandard o) {
    128.  
    129.            _PlaneNormal = float3(_NormalxValue,_NormalyValue,_NormalzValue);
    130.            _PlaneNormal = normalize(_PlaneNormal);
    131.          
    132.            half dist = (IN.worldPos.x * _PlaneNormal.x) + (IN.worldPos.y * _PlaneNormal.y) + (IN.worldPos.z * _PlaneNormal.z)
    133.                        - (_xValue * _PlaneNormal.x) - (_yValue * _PlaneNormal.y) - (_zValue * _PlaneNormal.z)
    134.                / sqrt( pow(_PlaneNormal.x, 2) + pow(_PlaneNormal.y, 2) + pow(_PlaneNormal.z,2));
    135.          
    136.  
    137.  
    138.            if(any(dist < 0))
    139.            {
    140.                discard;
    141.            }
    142.            _Origin = float3( _xIValue,_yIValue, _zIValue);
    143.        
    144.        
    145.            half distIarea = distance( IN.worldPos, _Origin);
    146.  
    147.            if(_InOut == 1)
    148.            {  
    149.                clip (_signe * (distIarea - _Radius));
    150.            }
    151.          
    152.            o.Albedo = _Color;
    153.            o.Metallic = _Metallic;
    154.            o.Smoothness = _Glossiness;
    155.            o.Alpha = _Color.a;  
    156.  
    157.          }
    158.           ENDCG    
    159.     }
    160.     Fallback Off
    161.   }
    162.  

    I'm speaking with this shader with C#script at runtime to give it the plane position. And I calculate the distance to clip or not pixels.



    EDIT : Solution found

    Discard pixel in the first Pass managing the Depth like in the Pass managing the color.

    Use
    o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    to know the world position of the object.


    Thanks
     
    Last edited: Aug 24, 2018
    neshius108 likes this.
  29. neshius108

    neshius108

    Joined:
    Nov 19, 2015
    Posts:
    110
    Amazing stuff, @Tim-C.
    I was really struggling to find a shader which handles transparency and z-depth.

    Only 1 thing: when I use this version of the transparent surface shader, the outlines of my sprites are totally visible. How can I fix that? I tried removing `ColorMask 0` but that turned the edges black.

    edges_shader.png

    Thanks!
     
  30. mradfo21

    mradfo21

    Joined:
    May 16, 2013
    Posts:
    194
    is this possible with surface shaders at all ? i cant quite figure it out
     
  31. MirkoSon

    MirkoSon

    Joined:
    Oct 14, 2015
    Posts:
    24
    Hi everyone,
    is there any way to replicate this in URP? These solutions work flawlessly with the Builtin renderer but is it possible to convert them to the new Render Pipelines?
    I've been reading about issues with OIT (Order Independent Transparency) and also found this thread:
    https://forum.unity.com/threads/soft-clip-free-shader-fade-out-objects-when-close-to-camera.549811/
    which is basically similar to this one, but I can't believe there's not an easy way to do the same in URP.
    Thanks in advance.