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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Clipping pixel based on lighting / shadow

Discussion in 'Shaders' started by Adurnha, Mar 2, 2020.

  1. Adurnha

    Adurnha

    Joined:
    Feb 28, 2019
    Posts:
    7
    Hello there !
    I am trying to write two shaders who work like this : the first one have to clip everything that is lighted, and the second one have to clip everything that is not lighted, using Point Light.

    I looked for some ways to do this and stumbled on different answer, and especially this one : https://forum.unity.com/threads/trying-to-clip-unlit-pixels-can-only-manage-to-clip-lit-ones.377326/

    This shader is working as I want it to work to clip unlit pixels.
    However, when trying to clip lit pixels, I have a problem with the way the rendering is made. The pixels too far away from the point light are not rendered at all. I tried adding a ForwardBase pass to render them, but the clipping from the ForwardAdd pass is then overwritten by the ForwardBase pass.

    I there a way to do what I'm trying to do ? Maybe to render both the directionnal and the point light in a single pass in order to be able to clip while still rendering pixels not in range of the light ?

    Here is a little video showing my issue :

    And here is the shader I am using :

    Code (CSharp):
    1. Shader "FromTheShadows/ShadowPlatformShader"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Main Color", Color) = (1,1,1,1)
    6.         _Cutoff("Alpha cutoff", Range(0,1)) = 0.5
    7.     }
    8.  
    9.     SubShader
    10.     {
    11.         Cull off
    12.        
    13.         Pass
    14.         {
    15.  
    16.             Tags{ "LightMode" = "ForwardAdd" "RenderType" = "TransparentCutout" }
    17.  
    18.             CGPROGRAM
    19.  
    20.             #pragma vertex vert
    21.             #pragma fragment frag
    22.             #include "UnityCG.cginc"
    23.  
    24.             #pragma multi_compile_fwdadd
    25.             #pragma multi_compile_fwadd_fullshadows
    26.  
    27.             #include "AutoLight.cginc"
    28.  
    29.             struct v2f
    30.             {
    31.                 float4 pos : SV_POSITION;
    32.  
    33.                 LIGHTING_COORDS(0,1)
    34.  
    35.             };
    36.  
    37.  
    38.             v2f vert(appdata_base v)
    39.             {
    40.                 v2f o;
    41.                 o.pos = UnityObjectToClipPos(v.vertex);
    42.  
    43.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    44.  
    45.                 return o;
    46.             }
    47.  
    48.             uniform fixed _Cutoff;
    49.  
    50.             fixed4 frag(v2f i) : SV_Target
    51.             {            
    52.                 float attenuation = LIGHT_ATTENUATION(i);
    53.  
    54.                 if ((attenuation - _Cutoff) > 0)
    55.                 {
    56.                     clip(-1);
    57.                 }
    58.  
    59.                 return fixed4(1.0,0.0,0.05,0.5);
    60.             }
    61.             ENDCG
    62.         }
    63.     }
    64.     Fallback "VertexLit"
    65. }
    Thank you !
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,256
    ForwardAdd passes use screen scissoring to limit the pixels of the object that is being drawn to the rectangular bounds of the light's range. This is done for performance since normally you only need to render the area the light covers, so skipping areas outside of that can be a significant performance benefit. This is obviously an issue for what you're doing.

    The solution is ... don't do what you're doing. Specifically, don't rely on Unity's lighting system to do something it wasn't made to do. Use a script to track the point light in question (or any arbitrary transform) and pass it's position & radius (which can be a single Vector4 / float4 value with the position xyz and radius w) to your materials, or as a shader global. Then you don't have to worry about different passes not getting the data needed.
     
  3. Adurnha

    Adurnha

    Joined:
    Feb 28, 2019
    Posts:
    7
    Thank you for your answer ! I see what the problem is.
    My first try was to pass the object position to the shader as you describbed, and clip everything too far away. However, with this, we loose the clipping based on shadow, as an object behind a wall would still be in range of the light even though it is not lighted.

    Might it be possible to have a first ForwardBase pass doing this, clipping everything in range of the light, and then a ForwardAdd pass clipping the lighted part and rendering the shadowed part ?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,256
    The ForwardBase pass knows nothing of your point light if it is going to be rendered as a ForwardAdd pass later. And you can only have the ForwardBase pass know about point lights if the shader has no ForwardAdd pass or you set the light to be “non-important”, which passes info to the ForwardBase pass as a “per vertex” light without shadows.

    So unfortunately, no.

    To do what you want to do requires you calculate and sample the shadow map manually.