UI Text being affected by translucent object behind it

I have an object in the scene which uses transparency and my intention is to have text positioned in the scene and displayed on top of the translucent object. Below you can see this working correctly:

If I move the camera in the scene then sometimes the text appears as if it is behind the transparent object, even though the only thing moving is the camera. Below you can see the exact same scene but from a position ever so slightly to one side:

Why does this happen? And how can I fix this?

Notes:

  • For the translucent object I am using the standard shader and set the rendering mode to transparent
  • I have tried using the fade rendering mode but the issues persists
  • With the opaque rendering mode there is no issue and text is always in front
1 Like

If two surfaces are too close to each other, sometimes the camera has a hard time telling which one is on top. When that is the case, just a little change may make the camera change it’s mind about which is on top. That is called “Z-Fighting”. To fix that, bring your text closer to the camera (or rather bring it away from the object it’s fighting with).

If the issue still persists, it’s because you’re either using particle shaders or UI elements. I won’t get into details on these unless you need me to, but particle depth is guessed a bit more poorly than most objects because it’s easier on the CPU and users typically don’t notice sorting issues on things as small and numerous as particles. UI depth… BAH! UI doesn’t have depth, it uses stenciling so programmers that aren’t graphics programmers can have something to bash their head against a wall about.

It’s not z-fighting, there’s a gap between the text and the object. I am using UI elements.
It seems to be fixed now. I created a sorting layer and set the world canvas with the text on to be in that new sorting layer.

3 Likes

It’s a long time ago now, but I just wanted to say thanks for answering your own question. Many people don’t bother. Thanks for leaving the solution for others to find. Helped a lot :slight_smile:

1 Like

In most cases the solution for this is to create a new material for the UI items and set its Rendering Queue to more than 3000.
Since transparency’s queue value is 3000, I usually set the UI’s materials to 3100. Unity will say “Transparent+100” in the queue enum.

It’s often useful for me to also have a shader that is just a clone of Unity’s UI Default material but with Z-writing turned off.

Here it is:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "UI/Default_OverlayNoZTest"
{
     Properties
     {
         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
         _Color ("Tint", Color) = (1,1,1,1)
       
         _StencilComp ("Stencil Comparison", Float) = 8
         _Stencil ("Stencil ID", Float) = 0
         _StencilOp ("Stencil Operation", Float) = 0
         _StencilWriteMask ("Stencil Write Mask", Float) = 255
         _StencilReadMask ("Stencil Read Mask", Float) = 255
         _ColorMask ("Color Mask", Float) = 15
     }
     SubShader
     {
         Tags
         {
             "Queue"="Overlay"
             "IgnoreProjector"="True"
             "RenderType"="Transparent"
             "PreviewType"="Plane"
             "CanUseSpriteAtlas"="True"
         }
       
         Stencil
         {
             Ref [_Stencil]
             Comp [_StencilComp]
             Pass [_StencilOp]
             ReadMask [_StencilReadMask]
             WriteMask [_StencilWriteMask]
         }
         Cull Off
         Lighting Off
         ZWrite Off
         ZTest Off
         Blend SrcAlpha OneMinusSrcAlpha
         ColorMask [_ColorMask]
         Pass
         {
         CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "UnityCG.cginc"
           
             struct appdata_t
             {
                 float4 vertex   : POSITION;
                 float4 color    : COLOR;
                 float2 texcoord : TEXCOORD0;
             };
             struct v2f
             {
                 float4 vertex   : SV_POSITION;
                 fixed4 color    : COLOR;
                 half2 texcoord  : TEXCOORD0;
             };
           
             fixed4 _Color;
             v2f vert(appdata_t IN)
             {
                 v2f OUT;
                 OUT.vertex = UnityObjectToClipPos(IN.vertex);
                 OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
                 OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
                 OUT.color = IN.color * _Color;
                 return OUT;
             }
             sampler2D _MainTex;
             fixed4 frag(v2f IN) : SV_Target
             {
                 half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
                 clip (color.a - 0.01);
                 return color;
             }
         ENDCG
         }
     }
}

There’s a good explanation of what’s going on here

1 Like