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

Logarithmic Z Buffer Issue

Discussion in 'Shaders' started by KingOfColly, Dec 1, 2013.

  1. KingOfColly

    KingOfColly

    Joined:
    May 25, 2013
    Posts:
    15
    Hi,

    I'm trying to implement logarithmic depth buffering in a Unity surface shader as explained in this Outerra blog http://outerra.blogspot.co.uk/2009/08/logarithmic-z-buffer.html.

    In the first pic I'm using the standard unity diffuse shader and in the second I'm using my logarithmic shader: -
    $diffuse.gif
    $log..gif

    As you can see the planet has been flattened on the z axis and the terrain skirts are z fighting with the planet surface. Has anyone else experienced these kind of problems? Any help would be muchly appreciated.

    Here is the shader code: -
    Code (csharp):
    1. Shader "Custom/LogDiffuseSurf"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Base (RGB)", 2D) = "white" {}
    6.     }
    7.    
    8.     SubShader
    9.     {  
    10.         Tags { "RenderType"="Opaque" }
    11.         LOD 200
    12.        
    13.         CGPROGRAM
    14.         #pragma exclude_renderers gles
    15.         #pragma surface surf Lambert vertex:vert
    16.  
    17.         sampler2D _MainTex;
    18.  
    19.         struct Input
    20.         {
    21.             float2 uv_MainTex;
    22.         };
    23.        
    24.         void vert(inout appdata_full v)
    25.         {
    26.             float c = 1.0;
    27.             float far = 1000000;
    28.        
    29.             v.vertex.z = log(v.vertex.w * c + 1) / log(far * c + 1);
    30.             v.vertex.z *= v.vertex.w;
    31.         }
    32.  
    33.         void surf(Input IN, inout SurfaceOutput o)
    34.         {
    35.             half4 c = tex2D(_MainTex, IN.uv_MainTex);
    36.             o.Albedo = c.rgb;
    37.             o.Alpha = c.a;
    38.         }
    39.         ENDCG
    40.     }
    41.    
    42.     FallBack "Diffuse"
    43. }
    Further info: -
    - The camera is sitting at the origin with 0.3f near and 100000 far clipping planes.
    - The planet is sitting at x0 y0 z10,000 and is constructed of multiple 'chunk' gameobjects
     
    Last edited: Dec 1, 2013
    jason-fisher likes this.
  2. brianasu

    brianasu

    Joined:
    Mar 9, 2010
    Posts:
    369
    I think he says Using the following equation to modify depth value after it's been transformed by the projection matrix: The coordinates you are modifying are still in object space.
     
  3. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    He sure is saying that. The adjustment should be in clip space instead of object space.

    I'm not sure whether this is possible when combined with surface shaders. With a full shader it should be easy. I'm not sure where you would have to output you own custom clip space position and whether that would actually work with a surface shader. The whole idea of a surface shader is that you focus on the appearance of the material and let unity handle the normal decoding and z-buffer writing.
     
  4. brianasu

    brianasu

    Joined:
    Mar 9, 2010
    Posts:
    369
    It's a pretty interesting concept. I used to read that guys blog a lot it's quite impressive. Yeah you are right on the surface shader stuff. I heard in Unity 4 you can actually modify the z-buffer. So rather than transforming the verts you might be able to just modify the z-buffer directly. Well I guess that might be what you are doing by moving the vertex coordinates:)
     
  5. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    The color of the pixel is actually the only mandatory thing a pixel/fragment shader must write out. There is an optional depth value in for example HLSL. Writing a depth value in a surface shader should work just fine. (Wouldn't break the generated prepasses, forward passes, etc.) The only place it probably would break things is in the depth passes for the shadow maps. But I'm guessing not many people are using custom depth shaders for the shadow maps anyway.
     
  6. KingOfColly

    KingOfColly

    Joined:
    May 25, 2013
    Posts:
    15
    Thanks for the replies. I did try the same thing in a basic vert/frag shader but it produces the same results (apart from the lack of diffuse lighting ofc). I'm not clear on why as in this one you can see it is transforming to clip space first (or at least that is my understanding of multiplying by UNITY_MATRIX_MVP).

    I'll try editing the depth value in the frag shader and see what happens.

    Code (csharp):
    1.  
    2. Shader "Custom/LogDiffuse"
    3. {
    4.     Properties
    5.     {
    6.         _MainTex ("Base (RGB)", 2D) = "white" {}
    7.     }
    8.    
    9.     SubShader
    10.     {
    11.         Pass
    12.         {
    13.             Tags { "RenderType"="Opaque" }
    14.             LOD 200
    15.            
    16.             CGPROGRAM
    17.             #pragma target 3.0
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             #include "UnityCG.cginc"
    21.                
    22.             sampler2D _MainTex;
    23.             float4 _MainTex_ST;
    24.             float c = 1.0;
    25.             float far = 1000000.0;
    26.             float offy = 1.0;
    27.            
    28.             struct v2f
    29.             {
    30.                 float4 pos : SV_POSITION;
    31.                 float2 uv : TEXCOORD0;
    32.             };
    33.                    
    34.             v2f vert(appdata_base v)
    35.             {
    36.                 v2f o;
    37.                
    38.                 o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    39.                 o.pos.z = log(c * o.pos.z + 1) / log(c * far + 1) * o.pos.w;
    40.                 o.uv = v.texcoord;
    41.                
    42.                 return o;
    43.             }
    44.    
    45.             half4 frag(v2f i) : COLOR
    46.             {  
    47.                 half4 c = tex2D(_MainTex, i.uv);
    48.            
    49.                 return c;
    50.             }
    51.             ENDCG
    52.         }
    53.     }
    54.    
    55.     FallBack "Diffuse"
    56. }
    57.  
     
    Last edited: Dec 7, 2013
    jason-fisher likes this.
  7. KingOfColly

    KingOfColly

    Joined:
    May 25, 2013
    Posts:
    15
    Here's first attempt messing with depth. Creates some interesting results! I tried placing two spheres slightly apart scaled to xyz100,000 at z100,000 from camera. One sphere appears to overlap the other. What's strange is moving the sphere that appears behind towards the camera does not cause it to draw in front of the other one even if you bring it really close to the camera!

    Code (csharp):
    1.  
    2. Shader "Custom/LogDepth"
    3. {
    4.     Properties
    5.     {
    6.         _MainTex ("Base (RGB)", 2D) = "white" {}
    7.     }
    8.    
    9.     SubShader
    10.     {
    11.         Pass
    12.         {
    13.             Tags { "RenderType"="Opaque" }
    14.             LOD 200
    15.            
    16.             CGPROGRAM
    17.             #pragma target 4.0
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             #include "UnityCG.cginc"
    21.                
    22.             sampler2D _MainTex;
    23.             float4 _MainTex_ST;
    24.             float c = 1.0;
    25.             float far = 1000000.0;
    26.             float offy = 1.0;
    27.            
    28.             struct v2f
    29.             {
    30.                 float4 position : POSITION;
    31.                 float2 uv : TEXCOORD0;
    32.             };
    33.                    
    34.             v2f vert(appdata_base v)
    35.             {
    36.                 v2f o;
    37.                
    38.                 o.position = mul(UNITY_MATRIX_MVP, v.vertex);
    39.                 o.uv = v.texcoord;
    40.                
    41.                 return o;
    42.             }
    43.    
    44.             void frag(in v2f i, out half4 col:COLOR, out float depth:DEPTH)
    45.             {  
    46.                 half4 c = tex2D(_MainTex, i.uv);
    47.            
    48.                 col = c;
    49.                 depth = log(c * i.position.z + 1) / log(c * far + 1) * i.position.w;
    50.             }
    51.             ENDCG
    52.         }
    53.     }
    54.    
    55.     FallBack "Diffuse"
    56. }
    57.  
    58.  
     
  8. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
  9. KingOfColly

    KingOfColly

    Joined:
    May 25, 2013
    Posts:
    15
    I'll give that a read thanks.

    Further testing shows that there is a problem writing depth values in general as setting the depth to any value even a fixed one e.g. 0 or 1 produces the same output as if it is ignoring the depth completely.
     
    Last edited: Dec 8, 2013
  10. Buckslice

    Buckslice

    Joined:
    Sep 12, 2014
    Posts:
    5
    I'm reviving this thread to see if anyone has figured out how to do this in Unity yet. Here's another thread where someone was able to get it working, but his solution didn't entirely work for me. http://forum.unity3d.com/threads/custom-z-buffer-problem-on-mac-and-windows.146710/ . When I tried it, using .01-1bill on the cameras culling planes, I didn't have normal Z-tearing issues like you would expect. However the draw order seemed backwards, like it was drawing back to front, so I could see faces that should have been blocked by closer geometry.

    I would like to get it working with surface shaders though (like OP was trying to do here) because I am still learning shaders and it seems like surface shaders do a lot of the boilerplate stuff for you. It seems like you can only edit the input vertices for the surface shader though.

    I'm making a space exploration game and my current setup is with two cameras, one from .3-1000 and the second from 1000-1million, but I would like to reduce the overdraw of objects in both cameras (among other reasons). That is why I am searching for alternatives.

    Thanks,
    -Buck
     
  11. SkavenPlanet

    SkavenPlanet

    Joined:
    Jan 20, 2014
    Posts:
    33
    Here's my implementation of Outerra's solution in a planetary terrain shader, it works flawlessly for me so far.
    Code (CSharp):
    1.  
    2.             struct a2v
    3.             {
    4.                 float4 vertex : POSITION;
    5.                 float4 texcoord : TEXCOORD0;
    6.  
    7.             };
    8.  
    9.             struct v2f
    10.             {
    11.                 float4 pos : POSITION;
    12.                 float2 uv : TEXCOORD0;
    13.                 float2 uv2 : TEXCOORD1;
    14.                 float flogz : TEXCOORD2;
    15.                 float3 lightDirection;
    16.             };
    17.      
    18.             struct Output {
    19.                 float4 col:COLOR;
    20.                 float dep:DEPTH;
    21.             };
    22.  
    23.             v2f vert (a2v v)
    24.             {
    25.                 v2f o;
    26.            
    27.                 o.lightDirection = WorldSpaceLightDir(v.vertex);
    28.                 o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
    29.            
    30.                 o.pos.z = log2(max(1e-6, 1.0 + o.pos.w)) * (2.0 / log2(_ProjectionParams.z + 1.0)) - 1.0;
    31.                 o.pos.z *= o.pos.w;
    32.                 o.flogz = 1.0 + o.pos.w;
    33.                 return o;
    34.             }
    35.  
    36.             Output frag(v2f i)
    37.             {
    38.                 Output o;
    39.                 o.col = color;
    40.                 o.dep = log2(i.flogz) * (0.5 * (2.0 / log2(_ProjectionParams.z + 1.0)));
    41.                 return o;
    42.             }
    I cut out almost everything that wasn't essential to the log depth buffer.
     
    Last edited: Nov 19, 2014
    RomBinDaHouse and jason-fisher like this.
  12. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,589
    Can't you use the _ProjectionParams built-in shader variable instead?

    Code (CSharp):
    1. // x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix),
    2. // y is the camera’s near plane,
    3. // z is the camera’s far plane
    4. // w is 1/FarPlane.
    5. float4 _ProjectionParams;
     
  13. SkavenPlanet

    SkavenPlanet

    Joined:
    Jan 20, 2014
    Posts:
    33
    Yes, you absolutely can. Updated my post above.
     
    Last edited: Feb 23, 2015
  14. jason-fisher

    jason-fisher

    Joined:
    Mar 19, 2014
    Posts:
    133
    I don't believe it did, but it does now in the Unity 5.5 beta, so logarithmic z-buffer modifications should no longer be necessary for most scenes:

    Graphics: Improve shadows precision for large worlds

    Use reverse-float depth buffer for improved depth buffer precision on modern GPUs. Particularly improves directional light shadowing for large view distances.​
     
  15. Manul

    Manul

    Joined:
    Nov 30, 2012
    Posts:
    18
    According to Aras inverse-z is in 5.5, but only for DirectX 10+ ( DX9 has no floating point depth buffers and relevant GL-extensions are not available on Mac / mobile, so no inverse-z for DX9 and OpenGL)
     
    jason-fisher likes this.
  16. jason-fisher

    jason-fisher

    Joined:
    Mar 19, 2014
    Posts:
    133
    Thanks for the clarification there.