# "ZTest Always" with Sorting?

Hi! I’m looking for some tips on how I can solve the issue below.

The project I’m working on uses its own gizmo controls. These are placed at the center of the selected object(s). This of course means you can’t see it because it is sorting correctly. I’ve set ZTest to Always in the shader so the gizmo now renders over everything else but now there is no depth information and the arrows won’t sort against itself.

(Notice the arrow heads are rendering over the cylinders).

How can I have the Gizmo sort with its own geometry but render on top of everything else?

Caveat 1: We were rendering the gizmo with a second camera and then overlaying the result but our lead programmer said this method was too costly.

Caveat 2: The arrows fade as the camera looks down them to disable movement on that axis’ view plane. So I think that would eliminate stencils. (Arrow fades, hole is still in the mesh).

The technique I use to handle this kind of effect is something I call z punching. The idea is to basically put a “hole” in the depth buffer before rendering your object.

You need a shader with two passes. The first one is set to:

ZTest Always
ZWrite True

This means it doesn’t test against the depth buffer, but still writes to the depth buffer, and only the depth buffer. The extra bit of magic is in the vertex shader you set the output clip space position to be at the far plane.

``````float4 vert (float4 vertex : POSITION) : SV_Position
{
float4 pos = UnityObjectToClipPos(vertex);

// borrowed from Aras's blog post on "infinite sky"

#if defined(UNITY_REVERSED_Z)
// when using reversed-Z, make the Z be just a tiny
// bit above 0.0
pos.z = 1.0e-9f;
#else
// when not using reversed-Z, make Z/W be just a tiny
// bit below 1.0
pos.z = pos.w - 1.0e-6f;
#endif

return pos;
}

void frag() {} // yes, you can have a fragment shader that does nothing
``````

The result is the area your mesh covers the depth buffer is effectively cleared. Your second pass is then just totally normal. No need for `ZTest Always` as the previous pass put a big hole in the depth buffer for the second pass to render into.

Thank you for the reply bgolus (I’m a big fan).

Adding the second pass with “ZTest Always, ZWrite On and ColorMask 0” certainly got things moving forward.

I’m still getting tripped up on Caveat 2. When my arrows go transparent, they are causing some issues. I wasn’t expecting this but as they go transparent they do render correctly with the opaque geometry, which is great (I was expeting it to cut an arrow shaped hole in the object). The issue now is they do cut arrow shaped holes in the other arms of the gizmo when they are transparent.

I also had to add an extra pass between so the alpha’ed gizmo would sort correctly.

``````Shader "Custom/Gizmo"
{
Properties
{

_XColour("X-Axis Colour", Color) = (1,1,1,1)
_YColour("Y-Axis Colour", Color) = (1,1,1,1)
_ZColour("Z-Axis Colour", Color) = (1,1,1,1)
_UniSColour("Uniform Scale Colour", Color) = (1,1,1,1)

_XEmissive("X-Axis Emissive", Color) = (1,1,1,1)
_YEmissive("Y-Axis Emissive", Color) = (1,1,1,1)
_ZEmissive("Z-Axis Emissive", Color) = (1,1,1,1)
_UniSEmissive("Uniform Scale Emissive", Color) = (1,1,1,1)

_XAxisAlpha("X-Axis Alpha", Range(0,1)) = 0.0
_YAxisAlpha("Y-Axis Alpha", Range(0,1)) = 0.0
_ZAxisAlpha("Z-Axis Alpha", Range(0,1)) = 0.0
_UniSAlpha("Uniform Scale Alpha", Range(0,1)) = 0.0

}
{
Tags { "RenderType"="Transparent" "Queue"="Transparent+10"}
LOD 150

Pass{
ZTest Always
ZWrite On
/*
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

float4 vert(float4 vertex : POSITION) : SV_Position
{
float4 pos = UnityObjectToClipPos(vertex);

// borrowed from Aras's blog post on "infinite sky"

#if defined(UNITY_REVERSED_Z)
// when using reversed-Z, make the Z be just a tiny
// bit above 0.0
pos.z = 1.0e-9f;
#else
// when not using reversed-Z, make Z/W be just a tiny
// bit below 1.0
//pos.z = pos.w - 1.0e-6f;
pos.z = pos.w - 1.0e-6f;
#endif

return pos;
}

void frag() {}
ENDCG
*/
}

Pass{
ZWrite On
}

CGPROGRAM
#pragma target 3.0

half _Glossiness;
half _Metallic;

fixed4 _XColour;
fixed4 _YColour;
fixed4 _ZColour;
fixed4 _UniSColour;

fixed4 _XEmissive;
fixed4 _YEmissive;
fixed4 _ZEmissive;
fixed4 _UniSEmissive;

half _spec;

half _XAxisAlpha;
half _YAxisAlpha;
half _ZAxisAlpha;
half _UniSAlpha;

struct Input
{
};
void surf (Input IN, inout SurfaceOutputStandard o)
{

//Gizmo Colour
half4 xColour = lerp(0, _XColour, c.r);
half4 yColour = lerp(0, _YColour, c.g);
half4 zColour = lerp(0, _ZColour, c.b);
half4 uniSColour = lerp(0, _UniSColour, (1 - c.a));

half4 axisColourResult = xColour + yColour + zColour + uniSColour;

o.Albedo = axisColourResult;

//Gizmo Emissive
half4 xEmissive = lerp(0, _XEmissive, c.r);
half4 yEmissive = lerp(0, _YEmissive, c.g);
half4 zEmissive = lerp(0, _ZEmissive, c.b);
half4 uniSEmissive = lerp(0, _UniSEmissive, (1 - c.a));

half4 axisEmissiveResult = xEmissive + yEmissive + zEmissive + uniSEmissive;

o.Emission = axisEmissiveResult;

//Gizmo Alpha
fixed3 xfade = lerp(1, 0, (c.r * _XAxisAlpha));
fixed3 yfade = lerp(1, 0, (c.g * _YAxisAlpha));
fixed3 zfade = lerp(1, 0, (c.b * _ZAxisAlpha));
fixed3 uniSfade = lerp(1, 0, ((1 - c.a) * _UniSAlpha));

o.Alpha = axisAlphaResult;
}
ENDCG
}
FallBack "Diffuse"
}
``````

Technically they are cutting a hole in the object, but only in the depth buffer and not the already rendered color buffer.

To understand what’s happening, you probably need to understand the depth buffer better. This article goes into good detail on the topic:
https://simonschreibt.de/gat/black-flag-waterplane/
https://data.simonschreibt.de/gat064/depthbuffer_example.webm

The basic idea is the depth buffer is generally used to help sort opaque geometry, and prevent transparent geometry from rendering over opaque geometry that has been drawn before it. We’re messing with that by blowing away the depth buffer, and then in your new shader’s case rendering the depth of otherwise transparent geometry. When you render the color for your arrows, it’s testing its z depth against itself, so it’ll only draw where it was the closest to the camera.

But why were you having problems before you added that second ColorMask 0 pass? Because efficient and correct sorting of real time transparent geometry is an unsolved problem.
https://discussions.unity.com/t/599385/2

If you have ZWrite On enabled for a transparent shader, it doesn’t prevent the triangles from drawing out of order, it just prevents triangles that are drawn later from drawing over triangles that were previously drawn closer. So in the case of triangles that are correctly sorted for transparency to the current view (back to front, aka Painter’s Algorithm), the depth buffer isn’t actually causing anything to be different as each triangle being drawn will be closer than the previous one. If the triangles are in the wrong order (front to back), then if they write to the depth (ZWrite On) later the pixels of triangles will be clipped if they’re behind the previous triangles, or if you don’t write to the depth (ZWrite Off) later triangles simply draw on top. But sorting triangles is expensive.

If you want to be able to see the arrows through each other, you’ll want to render each arrow of the gizmo separately so they can be sorted individually. For the image above, using the existing shader, that would mean you’d be able to see the blue arrow through the red one as you would expect, but the shaft of the red arrow would still be occluded by the head. The other option would be to use an additive blend mode so the draw order is irrelevant.

Bit of a delay but! Thank you very much for the information bgolus. Very useful.