unity-game-engineshaderhlsl

Why is the grid visible through my custom shader in Unity?


In Unity's Editor Scene view, the grid is not visible through an object with the default Lit URP shader, but an object with my custom simple shader appears transparent to this grid. I don’t understand why this is happening and how to fix it.

I’ve already added the following settings to my shader to make it opaque and write to the Z-buffer:

ZWrite On
ZTest LEqual
Blend Off

These are the settings I typically see for opaque materials in the Universal Render Pipeline (URP), but the grid is still visible through my object. Disabling the grid in the Scene view is not an option, as it doesn’t solve the underlying issue. How can I make my custom shader block the grid like the default Lit URP shader does?

left sphere - URPLit shader, right sphere - my custom shader

here is my code:

Shader "Custom/SimpleShader"
{
    Properties
    {
        base_color ("Base Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags {
            "RenderType"="Opaque"
            "Queue"="Geometry"
            "RenderPipeline"="UniversalPipeline"
        }
        LOD 100
        
        Pass
        {            
            ZWrite On
            ZTest LEqual
            Blend Off
            
            HLSLPROGRAM
            #pragma target 2.0
            
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            
            struct attributes
            {
                float4 position_os : POSITION;
                float3 normal_os : NORMAL;          
            };
            
            struct varyings
            {
                float4 position_cs : SV_POSITION;
                float3 position_ws : TEXCOORD0;
                float3 normal_ws : TEXCOORD1;
            };
            
            CBUFFER_START(UnityPerMaterial)
            float4 base_color;
            CBUFFER_END
            
            varyings vert (attributes input)
            {
                varyings output;
                
                output.position_cs = TransformObjectToHClip(input.position_os.xyz);
                output.position_ws = TransformObjectToWorld(input.position_os).xyz;
                output.normal_ws = TransformObjectToWorldNormal(input.normal_os);
                
                return output;
            }
            
            half4 frag (varyings input) : SV_Target
            {
                Light main_light = GetMainLight();
                float3 light_color = main_light.color.rgb;
                float3 light_direction_ws = normalize(main_light.direction.xyz);
                
                float3 normal_ws = normalize(input.normal_ws);
                float normal_dot_light = max(0,dot(normal_ws, light_direction_ws));
                
                float3 diffuse = light_color * normal_dot_light;
                float3 ambient = SampleSH(input.normal_ws);
                
                float4 lighting = float4(diffuse + ambient, 1);
                
                float4 final_color = base_color * lighting;
                
                return final_color;
            }
            
            ENDHLSL
        }
    }
}

P.S.

to me it looks like the right sphere is behind the grid – BugFinder

Yes, it does look that way, but both spheres have the same Y position (0). I only shifted the right sphere by 1 along the X-axis.


Solution

  • Update: I found a solution to the issue with the grid being visible through my custom shader. Here's what I discovered:

    The issue occurs because the shader lacks a DepthOnly pass, which is required to properly write depth into the depth buffer. Without it, the object does not participate in URP's depth prepass, causing artifacts like the grid showing through.

    Solution: Add a DepthOnly Pass for Correct Depth Rendering

    To fix this, add a Pass with LightMode = "DepthOnly", and ensure that in the vertex shader, you transform the object's position to clip space using the following line:

    output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
    

    This ensures proper depth rendering, similar to how a ShadowCaster pass is required for shadow casting.