Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Help with stencil created aim circle to be visible behind objects (x-ray)

Discussion in 'General Graphics' started by lagruise, Feb 2, 2022.

  1. lagruise

    lagruise

    Joined:
    Feb 23, 2017
    Posts:
    18
    Dear shader specialists,


    I have the following needs.
    • an aim circle that can move smoothly across the environment
    • those parts of the aim circle passing behind objects should turn into “xray”-color

    I have solved the first part using the following stencil shader code with a “cut out” approach (see attached picture)
    1. place sphere in environment
    2. attach material with stencil shader (queue 3030)
    3. Place another sphere inside the aimcircle
    4. attach material with samet type of stencil shader code but with alpha set to 0 and queue lower (3020)
    As you can see my stencil shader has to be at 3030 for various reasons.

    The objects to be visible through are all at render queue 2000

    Previously I have managed to create x-ray effect on my player - but then I have made use of separate materials using the z-buffer (as I recall it) whereas in this case the zwrite is set to off in order to create the actual aim-circle effect. So I am pretty much clueless…

    Any ideas are greatly appreciated.

    Shader Code for Outer Sphere:

    Code (CSharp):
    1. Shader "Unlit/AimCircleOuter"
    2.  
    3. {
    4.     Properties
    5.     {
    6.         _StencilRef("Stencil Ref", Int) = 4
    7.         _Color("Main Color", Color) = (1, 1, 1, 1)
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Tags
    13.         {
    14.             "Queue" = "Transparent+30"
    15.             "IgnoreProjector" = "True"
    16.         }
    17.         LOD 100
    18.  
    19.                 Pass
    20.         {
    21.             ColorMask 0
    22.             Zwrite On
    23.             Stencil {
    24.             Ref [_Ref]
    25.             Comp NotEqual
    26.  
    27.             Pass  Keep
    28.             }
    29.  
    30.             }
    31.  
    32.         Pass
    33.         {
    34.             ColorMask 0
    35.             ZWrite Off
    36.             ZTest LEqual
    37.             Cull Front
    38.             Blend Off
    39.  
    40.             Stencil
    41.             {
    42.                 Ref [_StencilRef]
    43.                 Comp Greater
    44.                 ZFail Replace
    45.             }
    46.         }
    47.  
    48.         Pass
    49.         {
    50.             ZWrite Off
    51.             ZTest LEqual
    52.             Cull Back
    53.             Blend SrcAlpha OneMinusSrcAlpha
    54.  
    55.             Stencil
    56.             {
    57.                 Ref [_StencilRef]
    58.                 Comp Equal
    59.                 Pass Invert
    60.                 ZFail Keep
    61.             }
    62.  
    63.             CGPROGRAM
    64.             #pragma vertex vert
    65.             #pragma fragment frag
    66.  
    67.             #include "UnityCG.cginc"
    68.  
    69.             struct appdata
    70.             {
    71.                 float4 vertex : POSITION;
    72.                 float2 uv : TEXCOORD0;
    73.             };
    74.  
    75.             struct v2f
    76.             {
    77.                 float2 uv : TEXCOORD0;
    78.                 UNITY_FOG_COORDS(1)
    79.                 float4 vertex : SV_POSITION;
    80.             };
    81.  
    82.             fixed4 _Color;
    83.  
    84.             v2f vert (appdata v)
    85.             {
    86.                 v2f o;
    87.                 o.vertex = UnityObjectToClipPos(v.vertex);
    88.                 return o;
    89.             }
    90.  
    91.             fixed4 frag (v2f i) : SV_Target
    92.             {
    93.                 fixed4 col = _Color;
    94.                 return col;
    95.             }
    96.             ENDCG
    97.         }
    98.     }
    99. }
    Shader Code for Inner CutOut Sphere:
    Code (CSharp):
    1. Shader "Unlit/AimCircleInnerCutOut"
    2.  
    3. {
    4.     Properties
    5.     {
    6.         _StencilRef("Stencil Ref", Int) = 4
    7.         _Color("Main Color", Color) = (1, 1, 1, 0)
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Tags
    13.         {
    14.             "Queue" = "Transparent+20"
    15.             "IgnoreProjector" = "True"
    16.         }
    17.         LOD 100
    18.  
    19.         Pass
    20.         {
    21.             ColorMask 0
    22.             ZWrite Off
    23.             ZTest LEqual
    24.             Cull Front
    25.             Blend Off
    26.  
    27.             Stencil
    28.             {
    29.                 Ref [_StencilRef]
    30.                 Comp Greater
    31.                 ZFail Replace
    32.             }
    33.         }
    34.  
    35.         Pass
    36.         {
    37.             ZWrite Off
    38.             ZTest LEqual
    39.             Cull Back
    40.             Blend SrcAlpha OneMinusSrcAlpha
    41.  
    42.             Stencil
    43.             {
    44.                 Ref [_StencilRef]
    45.                 Comp Equal
    46.                 Pass Invert
    47.                 ZFail Keep
    48.             }  
    49.  
    50.             CGPROGRAM
    51.             #pragma vertex vert
    52.             #pragma fragment frag
    53.  
    54.             #include "UnityCG.cginc"
    55.  
    56.             struct appdata
    57.             {
    58.                 float4 vertex : POSITION;
    59.                 float2 uv : TEXCOORD0;
    60.             };
    61.  
    62.             struct v2f
    63.             {
    64.                 float2 uv : TEXCOORD0;
    65.                 UNITY_FOG_COORDS(1)
    66.                 float4 vertex : SV_POSITION;
    67.             };
    68.  
    69.             fixed4 _Color;
    70.  
    71.             v2f vert (appdata v)
    72.             {
    73.                 v2f o;
    74.                 o.vertex = UnityObjectToClipPos(v.vertex);
    75.                 return o;
    76.             }
    77.  
    78.             fixed4 frag (v2f i) : SV_Target
    79.             {
    80.                 fixed4 col = _Color;
    81.                 col.a = 0;
    82.                 return col;
    83.             }
    84.             ENDCG
    85.         }
    86.     }
    87. }
     

    Attached Files:

  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    The short version to your question is it’s not possible.

    The longer answer is the circle effect you’re doing relies on what’s previously been rendered to the depth buffer to let you get the “projected” look you’re getting. In areas of the screen that are behind other objects, the depth buffer is only of that foreground object. By the time you’re rendering your targeting spheres you no longer have any information about the surface those spheres are intersecting.

    If you want to render something else when it’s behind a surface, that’s totally doable. Something like a screen space circle. Though you’ll have to be careful otherwise it’ll slow up under the ground.
     
  3. lagruise

    lagruise

    Joined:
    Feb 23, 2017
    Posts:
    18
    Thanks for a rapid and insightful reply.
    Good to know that my lack of progress was rooted in realities...

    "Screen space circle" sounds interesting. Would it be possible to shed some light on this approach or link me onwards? I did a quick search and could not immediately find anything that made sense to me.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    By "screen space circle" I really just mean draw a circle on the screen. Could be done with texture of a circle on a screen facing quad mesh, or using a billboard shader that's calculating a circle from the UVs. Place it in the same position as your targeting spheres, scaled the same size as the larger sphere, and render it before either of the above materials using
    ZTest Greater
    ZWrite Off
    and an alpha blend. Could even have the dotted line like in your mockup. That'll show up behind the foreground objects, but also show up under the floor a little since, like the sphere, it'll be intersecting with the ground.

    Something like this:
    upload_2022-2-3_15-31-3.png

    The only way to get the original projected shape you're going for would be to use a Unity projector, or something similar that renders a mesh that conforms to the surface shape you want. The Unity projector renders whatever object it's frustum overlaps again with the projector's material. Render that with a shader with two passes (one w/
    ZTest Greater
    , one
    ZTest LEqual
    ) that draws the line based on the distance to the projector. But that also can be problematic since if you have any hidden overlapping geometry you'll see that show up too.
     
  5. lagruise

    lagruise

    Joined:
    Feb 23, 2017
    Posts:
    18
    Thanks a lot!

    I will test the different options.