Search Unity

Sprite Zwrite on only visible in z depth pass from one direction.

Discussion in 'Shaders' started by fkenned1, Mar 22, 2021.

  1. fkenned1

    fkenned1

    Joined:
    Aug 14, 2019
    Posts:
    2
    I have a 2.5d animated character built from sprite layers. I want the sprites to react to 3d lighting, to cast shadows, and to be seen by the depth pass so that I can use effects like Depth of field. I essentially want to treat the sprite as a mesh rendered plane. I have a two pass shader built so that when the character flips directions, the sprite normal flips as well. This was done so that the sprites react correctly to light direction as the character flips from left to right. So far, lighting works

    I have Zwrite set to On for both passes. When the character faces right, the zdepth is being written correctly and DOF effects work perfectly. When the character flips, however, the DOF no longer sees the character in the depth pass. I have a suspicion that the Z direction is not flipping along with the Sprite, so it's essentially invisible to the Z writing. My shader is as follows. I will admit that I'm pretty new to this and googling/copy/pasting my way through things. I have an idea of how things should work in theory but I'm not super experienced with writing shaders. Any help would be much appreciated.

    // Sprite Shadow Shader

    Shader "Sprites/Custom/SpriteShadow"
    {
    Properties
    {
    [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
    _Color("Tint", Color) = (1,1,1,1)
    [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
    [HideInInspector] _RendererColor("RendererColor", Color) = (1,1,1,1)
    [HideInInspector] _Flip("Flip", Vector) = (1,1,1,1)
    [PerRendererData] _AlphaTex("External Alpha", 2D) = "white" {}
    [PerRendererData] _EnableExternalAlpha("Enable External Alpha", Float) = 0
    _Cutoff("Alpha Cutoff", Range(0,1)) = 0.5
    }

    SubShader
    {
    Tags
    {
    "Queue" = "Geometry"
    "IgnoreProjector" = "True"
    "RenderType" = "AlphaTest"
    "PreviewType" = "Plane"
    "CanUseSpriteAtlas" = "True"
    }

    Cull Back
    Lighting Off
    ZWrite On
    Blend One OneMinusSrcAlpha


    CGPROGRAM
    #pragma surface surf Lambert vertex:vert alphatest:_Cutoff addshadow nofog nolightmap nodynlightmap keepalpha noinstancing
    #pragma multi_compile_local _ PIXELSNAP_ON
    #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    #include "UnitySprites.cginc"


    struct Input
    {
    float2 uv_MainTex;
    fixed4 color;
    };

    void vert(inout appdata_full v, out Input o)
    {
    v.vertex = UnityFlipSprite(v.vertex, _Flip);

    #if defined(PIXELSNAP_ON)
    v.vertex = UnityPixelSnap(v.vertex);
    #endif

    UNITY_INITIALIZE_OUTPUT(Input, o);
    o.color = v.color * _Color * _RendererColor;
    }



    void surf(Input IN, inout SurfaceOutput o) {
    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    o.Albedo = c.rgb * c.a;
    o.Alpha = c.a;
    }


    ENDCG

    Cull Front
    Lighting Off
    ZWrite On
    Blend One OneMinusSrcAlpha

    CGPROGRAM
    #pragma surface surf Lambert vertex:vert alphatest:_Cutoff addshadow nofog nolightmap nodynlightmap keepalpha noinstancing
    #pragma multi_compile_local _ PIXELSNAP_ON
    #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
    #include "UnitySprites.cginc"


    struct Input
    {
    float2 uv_MainTex;
    fixed4 color;
    };

    void vert(inout appdata_full v, out Input o)
    {
    v.normal *= -1;

    #if defined(PIXELSNAP_ON)
    v.vertex = UnityPixelSnap(v.vertex);
    #endif

    UNITY_INITIALIZE_OUTPUT(Input, o);
    o.color = v.color * _Color * _RendererColor;
    }


    void surf(Input IN, inout SurfaceOutput o) {
    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    o.Albedo = c.rgb * c.a;
    o.Alpha = c.a;
    }




    ENDCG

    }

    Fallback "Transparent/VertexLit"
    }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    The _CameraDepthTexture that's used by post processing or anything that needs to sample the scene depth is generated by rendering all opaque objects using their shadow caster shader pass using a render pass that occurs before rendering the main camera view. For various technical reasons, if a shader has multiple shadow caster passes, only the first valid shadow caster pass is used. In your shader's case both your Surface Shaders are using the
    addshadow
    option which is generating a shadow caster pass for each, but only the first is being used. And since they're both single sided you're getting a single sided shadow.

    The solution is don't use two Surface Shaders. There's no reason to need to either. Use a single Surface Shader with
    Cull Off
    and your depth problem will be solved. To solve the lighting problem you can use a special
    facing : VFACE
    variable in the
    Input
    struct to know which side of the face you're currently rendering and flip the normal accordingly. This is oddly undocumented.
    https://forum.unity.com/threads/sta...y-shiny-on-the-underside.393068/#post-2572693
     
  3. fkenned1

    fkenned1

    Joined:
    Aug 14, 2019
    Posts:
    2
    Killer! I mixed and matched some of my shader with the "Cull Off" and VFACE tips you gave me. Fixed everything. Light/shadows work correctly, and depth writes for both directions. Thank you thank you!
     
  4. Graigy1337

    Graigy1337

    Joined:
    Oct 26, 2019
    Posts:
    9
    Hey man I am also working on a 2.5D game and am having this exact problem, would you be able to post your fixed shader code so that I may try it out? The other problem i'm having is getting the scene depth node sprite to recognize when a sprite is interacting with it, any ideas? Its basically for a 3D water shader to effect the sprite when it jumps inside it.