Search Unity

  1. We've closed the job boards. If you're looking for work, or looking to hire check out Unity Connect. You can see more information here.
    Dismiss Notice
  2. Unity 2017.3 has arrived! Read about it here.
    Dismiss Notice
  3. 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:
    1,939
    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):
    [​IMG]

    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:
    [​IMG]

    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:
    1,939
    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:
    377
    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:
    224
    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:
    215
    Got this error
    [​IMG]
     
  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:
    869

    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:
    869
    Or does this work only in Deferred mode?
     
  12. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    204
    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:
    869
    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:
    988
  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:
    67
    I'd be really interested, many thanks!