Search code examples
unity-game-engineshaderhlslcg

How to make 3d object visible behind wall, but invisible if behind wall and under ground


I'd like to start off by saying that my shader coding knowledge is pretty limited. I know the basics and I can eventually understand how a shader works, but that's about it.

Please have a look at the following image: unity scene

I have 4 objects in the scene above. The ground (beige), one red cube on the left side that's above the ground, one red cube on the right side that's half above and half under ground and a wall (blue) in front of the 2 cubes. The red cubes only render if they're behind the wall so if I was to delete the wall they would become invisible.

What I would like to achieve is to have the red cubes be visible when behind the wall, but not the area that's under ground. So the right cube should only be rendering the top red part and not the bottom beige part as well. The bottom beige part I assume is the ground rendering behind the wall in the area where the cube is behind it.

Here are the 3 shaders that I'm using:

Cubes: https://pastebin.com/msuyi1zj

Shader "ZTest Shaders/Cube"
{
    Properties
    {
        ObjectColor("Object Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags {
            "Queue" = "Transparent+10"
        }

        Pass
        {

            ZWrite On
            ZTest Greater

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                fixed4 color : COLOR;
                float4 vertex : SV_POSITION;
            };

            uniform fixed4 ObjectColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = ObjectColor;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}

Ground: https://pastebin.com/ttL45Gjm

Shader "ZTest Shaders/Ground"
{
    Properties
    {
        ObjectColor("Object Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags {
            "Queue" = "Transparent+15"
        }

        Pass
        {

            ZWrite On
            ZTest Less

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                fixed4 color : COLOR;
                float4 vertex : SV_POSITION;
            };

            uniform fixed4 ObjectColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = ObjectColor;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}

Wall: https://pastebin.com/QcL0uY8j

Shader "ZTest Shaders/Wall"
{
    Properties
    {
        ObjectColor("Object Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags {
            "Queue" = "Transparent+5"
        }

        Pass
        {

            ZWrite On

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                fixed4 color : COLOR;
                float4 vertex : SV_POSITION;
            };

            uniform fixed4 ObjectColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = ObjectColor;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}

To gives you a scenario where I would be using something like this, imagine an RTS game where you want units behind walls or other objects to be visible so the cube shader would be like an outline or silhouette shader. Now imagine that you also want these units to hide by going underground. Currently, if they were to go underground but were also behind a wall, they would be visible.

Thank you very much in advance!


Solution

  • If anyone else runs into this issue, the answer has been posted here: https://forum.unity.com/threads/need-help-with-my-outline-shaders-visibility-behind-walls.718499/

    This is the updated shader:

    Shader "ZTest Shaders/Cube"
    {
        Properties
        {
            ObjectColor("Object Color", Color) = (1, 1, 1, 1)
        }
        SubShader
        {
            Tags {
                "Queue" = "Transparent+10"
            }
    
            Pass
            {
    
                ZWrite On
                ZTest Greater
    
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                };
    
                struct v2f
                {
                    fixed4 color : COLOR;
                    float4 vertex : SV_POSITION;
                    float3 worldPos : TEXCOORD0;
                };
    
                uniform fixed4 ObjectColor;
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.color = ObjectColor;
                    o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
                    return o;
                }
    
                fixed4 frag (v2f i) : SV_Target
                {
                    if (i.worldPos.y < 0.0) discard;
                    return i.color;
                }
                ENDCG
            }
        }
    }