Search Unity

  1. Get all the Unite Berlin 2018 news on the blog.
    Dismiss Notice
  2. Unity 2018.2 has arrived! Read about it here.
    Dismiss Notice
  3. Improve your Unity skills with a certified instructor in a private, interactive classroom. Learn more.
    Dismiss Notice
  4. ARCore is out of developer preview! Read about it here.
    Dismiss Notice
  5. Magic Leap’s Lumin SDK Technical Preview for Unity lets you get started creating content for Magic Leap One™. Find more information on our blog!
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

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,026
    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.
     
    MJHughes likes 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,026
    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.
     
  4. Jessy

    Jessy

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

    Paradoks

    Joined:
    Oct 13, 2009
    Posts:
    393
    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:
    227
    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:
    222
    Got this error
     
  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.  
     
  10. HonoraryBob

    HonoraryBob

    Joined:
    May 26, 2011
    Posts:
    957

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

    HonoraryBob

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

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    246
    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:
    957
    I'd be interested.
     
  14. ntstudios

    ntstudios

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

    Mauri

    Joined:
    Dec 9, 2010
    Posts:
    1,171
  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:
    136
    I would be interested as well, thank you.
     
  18. danielesuppo

    danielesuppo

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

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    246
    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:
    109
    Thanks a lot!
     
  21. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    246
  22. danielesuppo

    danielesuppo

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

    unisip

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

    Thibault_Lesaunier

    Joined:
    Feb 8, 2018
    Posts:
    5
    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:
    246
    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:
    5
    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:
    246
    What shader are you using for the cutting ?
     
  28. Thibault_Lesaunier

    Thibault_Lesaunier

    Joined:
    Feb 8, 2018
    Posts:
    5
    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.  
    14.       Tags { "Queue" = "Transparent"  "RenderType" = "TransparentCutout" "DisableBatching" = "True" }
    15.       LOD 200
    16.       Cull Off
    17.  
    18.  Pass {
    19.         ZWrite On
    20.         ColorMask 0
    21.  
    22.         CGPROGRAM
    23.         #pragma vertex vert
    24.         #pragma fragment frag
    25.         #include "UnityCG.cginc"
    26.         struct v2f {
    27.             float4 pos : SV_POSITION;
    28.         };
    29.         v2f vert (appdata_base v)
    30.         {
    31.             v2f o;
    32.             o.pos = UnityObjectToClipPos (v.vertex);
    33.             return o;
    34.         }
    35.         half4 frag (v2f i) : COLOR
    36.         {
    37.             return half4 (0,0,0,0);
    38.         }
    39.         ENDCG
    40.     }
    41. CGPROGRAM
    42.  
    43.       #pragma surface surf Standard alpha
    44.       #pragma target 3.0
    45.  
    46.       struct Input {
    47.           float2 uv_MainTex;
    48.           float2 uv_BumpMap;
    49.           float2 uv_Conversion;
    50.           float3 worldPos;    
    51.       };
    52.  
    53.       half _Glossiness;
    54.       half _Metallic;
    55.       half _ConvertDistance;
    56.       half _ConvertEmission;
    57.       fixed4 _Color;
    58.  
    59.       //////////////////////
    60.       /// <Cut Area
    61.       //////////////////////
    62.       float3 _PlaneNormal;
    63.  
    64.       //Plane position
    65.       float _xValue;
    66.       float _yValue;
    67.       float _zValue;
    68.  
    69.       float _NormalxValue;
    70.       float _NormalyValue;
    71.       float _NormalzValue;
    72.       //////////////////////
    73.       /// Cut Area />
    74.       //////////////////////
    75.  
    76.       sampler2D _MainTex;
    77.       sampler2D _BumpMap;
    78.       sampler2D _Conversion;
    79.  
    80.       void surf (Input IN, inout SurfaceOutputStandard o) {
    81.  
    82.         _ConvertDistance = 0.4;
    83.         _ConvertEmission = 0.45;
    84.         _PlaneNormal = float3(_NormalxValue,_NormalyValue,_NormalzValue);
    85.         _PlaneNormal = normalize(_PlaneNormal);
    86.    
    87.         //Plane distance
    88.         half dist = (IN.worldPos.x * _PlaneNormal.x) + (IN.worldPos.y * _PlaneNormal.y) + (IN.worldPos.z * _PlaneNormal.z)
    89.                     - (_xValue * _PlaneNormal.x) - (_yValue * _PlaneNormal.y) - (_zValue * _PlaneNormal.z)
    90.             / sqrt( pow(_PlaneNormal.x, 2) + pow(_PlaneNormal.y, 2) + pow(_PlaneNormal.z,2));
    91.    
    92.         float convert_mask = dist / _ConvertDistance;
    93.  
    94.         //Discard if distance < 0
    95.         if(any(dist < 0))
    96.         {
    97.             discard;
    98.         }
    99.         ///////////////////////////////////////////////////////
    100.    
    101.  
    102.         o.Albedo = _Color;
    103.         o.Metallic = _Metallic;
    104.         o.Smoothness = _Glossiness;
    105.         o.Alpha = _Color.a;
    106.  
    107.       }
    108.        ENDCG
    109.     }
    110.  
    111.     Fallback "Diffuse"
    112.   }
    113.  

    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 theworl position of the object.


    Thanks
     
    Last edited: Apr 13, 2018