Depth buffer with orthographic camera?

I came across this cool shader effect (shader code inside the link). The depth buffer fading effect works well on perspective camera, but not with orthographic camera mode I’m using.

Is there an alternative way to recreate the Y-depth effect on the ortho camera? I couldn’t find anything on Google but I may be using the wrong keywords.

Any ideas or keywords to search for would help. Thanks!

Why this happens:
Perspective cameras show objects larger up close and smaller as they get farther away. Because of this, you get a closer look at objects close to the camera, so you want better depth quality on them. For this reason, you have logarithmic depth, which gives better accuracy up-close at the cost of lower depth-quality farther away from the camera. (Nobody will notice those 2 pixels z-fighting in the distance anyway)

Orthographic cameras use linear depth because you can see object just the same no matter how far away they are from the camera, so there’s no reason to have better accuracy on closer objects.

With logarithmic depth, you end up using depth values 0 through 0.7 in just the first 10% of the frustum and distribute the rest throughout the remaining 90% of the frustum. Linear depth goes from 0 to 1 linearly. (Let me know if you’d like me to explain any of this better)

How to fix it:
Line 39 of that shader is:

float depth = Linear01Depth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r);

“Linear01Depth” converts logarythmic depth into linear depth, but you already have linear depth on an orthographic camera, so just remove that function call. The result should be this:

float depth = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r;

I have not tested this. Let me know how it goes!

1 Like

Thanks for the explanation Gambit_MSplitz! I get what you mean. :slight_smile:

I’ve tested the changes, but the result remains the same for ortho camera (the effect no longer works in perspective camera though, as expected). The ortho camera depth buffer seems to be either 0 or 1, but nothing in between? :face_with_spiral_eyes:

I’ll try fiddling with it more. Thanks!

Update:
Lowering the ortho camera’s Clipping Planes to between 0.2 and 3.5 helped somewhat (no effect on the sphere though), but now the scene objects clip really badly. Any ideas? Or should I just drop it?

with LinearEyeDepth
2302330--155021--depth.jpg

without LinearEyeDepth
2302330--155023--depth.jpg

Update 2:
Getting close! Made some code changes to make it work with normal Clipping Planes value of 0.01 to 1000.
Code changes

float sceneZ = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r * 40; //multiply with value

float partZ = i.projPos.y; //changed to Y-axis

There’s still an issue of the effect being more apparent when object is at the bottom of the screen. Will try to figure this out…
2302330--155025--depth.jpg

I did a hackish fix to reduce the Z-axis fading on ortho camera. I’ll just consider this solved unless there’s a better solution. :slight_smile:

Shader code

//Highlights intersections with other objects
Shader "Custom/IntersectionHighlights"
{
    Properties
    {
        _RegularColor("Main Color", Color) = (1, 1, 1, .5) //Color when not intersecting
        _HighlightColor("Highlight Color", Color) = (1, 1, 1, .5) //Color when intersecting
        _HighlightThresholdMax("Highlight Threshold Max", Float) = 1 //Max difference for intersections
        _ZBias("Highlight Z Bias", Float) = 2.5    //Balance out the Z-axis fading
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType"="Transparent"  }
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            Cull Off
            CGPROGRAM
            #pragma target 3.0
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            uniform sampler2D _CameraDepthTexture; //Depth Texture
            uniform float4 _RegularColor;
            uniform float4 _HighlightColor;
            uniform float _HighlightThresholdMax;
            uniform float _ZBias;
            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 projPos : TEXCOORD1; //Screen position of pos
            };
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.projPos = ComputeScreenPos(o.pos);
                return o;
            }
            half4 frag(v2f i) : COLOR
            {
                float4 finalColor = _RegularColor;
                //Get the distance to the camera from the depth buffer for this point
                float sceneZ = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r * 400;

                //Actual distance to the camera
                float partY = i.projPos.y + (i.projPos.y/_ZBias);

                //If the two are similar, then there is an object intersecting with our object
                float diff = (abs(sceneZ - partY)) / _HighlightThresholdMax;
                if (diff <= 1)
                {
                    finalColor = lerp(_HighlightColor, _RegularColor, diff);
                }
               
                half4 c;
                c.r = finalColor.r;
                c.g = finalColor.g;
                c.b = finalColor.b;
                c.a = finalColor.a;
                return c;
            }
            ENDCG
        }
    }
    FallBack "VertexLit"
}

So in your “update 2” you say the effect is darker towards the bottom of the screen… that’s because you have your camera at an angle so the objects at the bottom of the screen are closer to the camera.

I could be wrong, but it seems the only problem you have now is the rate at which you fade from pink to transparent. Since the falloff is not linear, I think something in the calculation is still expecting you to use a perspective camera.

This is just a wild guess, but try changing:

float depth = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r;

to

float depth = tex2D(_CameraDepthTexture, (i.projPos)).r;

Sadly the Z-distance fading is still there after replacing that code.
Thanks for helping out though! :slight_smile:

Just realized the effect gets messed up whenever you have different Clipping Planes values, so I guess the shader is a bit finicky. :face_with_spiral_eyes:

Well that makes sense, the depth value 0 to 1 tells you how far between the near and far plane you are where 0 is the near plane and 1 is the far plane.

1 Like

It’s been a number of years, but would anyone have any ideas or insight into this? Trying to wrap my head around shaderforge and the above comments is leading me nowhere!

Sorry for the non-answer, but Shaderforge isn’t supported in Unity 2017 or later, so you may want to consider switching to Amplify shader editor or the built-in shader graph.

If you must have an answer, you may want to start your own thread to get new eyes on the question, or ask in the shader forge forums.

This 3-line code snippet seems the best start for dealing with these issues:

… it essentially replaces LinearEyeDepth with a more intelligent decision based on the camera’s current mode.

It is not perfect - UnityEditor makes some guesses when converting to/from ortho camera that aren’t always correct - but it’s a good start and is easily tweakable.