Search Unity

Using world position to sample shadows in ForwardBase pass

Discussion in 'General Graphics' started by heinn_, Mar 23, 2021.

  1. heinn_

    heinn_

    Joined:
    Jul 30, 2015
    Posts:
    3
    I'm trying to use the UNITY_LIGHT_ATTENUATION macro but it's not working as (I) expected in the ForwardBase pass. In the ForwardAdd pass it works fine, with the third parameter affect the resulting light/shadow, while in the ForwardBase pass it seems to be ignored.

    Am I doing something wrong? Is there anothger way to sample the shadows using world position?


    I'm not using the URP, just the default one (SRP?). This is the stripped down shader:
    Code (CSharp):
    1. Shader "Custom/Test"
    2. {
    3.  
    4.     Properties
    5.     {
    6.         _MainTex("Base (RGB)", 2D) = "white" {}
    7.     }
    8.  
    9.     CGINCLUDE
    10.     #include "UnityStandardCore.cginc"
    11.     ENDCG
    12.  
    13.     SubShader
    14.     {
    15.  
    16.         Tags {"LightMode" = "ForwardBase"}
    17.      
    18.         Pass {
    19.             Fog { Mode Off }
    20.             Lighting On
    21.          
    22.             CGPROGRAM
    23.          
    24.             #pragma multi_compile_instancing
    25.             #pragma multi_compile_fwdbase
    26.             #pragma multi_compile_fog
    27.          
    28.             #pragma target 3.0
    29.             #pragma vertex vert
    30.             #pragma fragment frag
    31.  
    32.             float4 _MainTex_TexelSize;
    33.  
    34.             struct appdata_t
    35.             {
    36.                 float4 vertex            : POSITION;
    37.                 float3 normal            : NORMAL;
    38.                 float2 texcoord            : TEXCOORD0;
    39.             };
    40.          
    41.             struct v2f
    42.             {
    43.                 float4 pos                : POSITION;
    44.                 float3 texcoord         : TEXCOORD0;
    45.                 float3 posWorld            : TEXCOORD1;
    46.                 LIGHTING_COORDS(4,5)
    47.             };
    48.          
    49.             v2f vert (appdata_t v)
    50.             {
    51.              
    52.                 v2f o;
    53.              
    54.                 o.pos = UnityObjectToClipPos(v.vertex);
    55.                 o.texcoord = float3((TRANSFORM_TEX(v.texcoord.xy, _MainTex)).xy,1);
    56.                 o.posWorld =   mul(unity_ObjectToWorld, v.vertex);
    57.  
    58.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    59.              
    60.                 return o;
    61.             }
    62.          
    63.             fixed4 frag (v2f i) : COLOR
    64.             {
    65.                 float2 uv = i.texcoord.xy;
    66.              
    67.                 float4 col = tex2D(_MainTex, uv);
    68.              
    69.                 UNITY_LIGHT_ATTENUATION(atten, i, 0) // worldPos parameter ignored, shadows work fine even when set to zero, seems to be using only _ShadowCoords
    70.  
    71.                 return atten;
    72.             }
    73.          
    74.             ENDCG
    75.          
    76.         }
    77.  
    78.         Pass {
    79.             Tags {"LightMode" = "ForwardAdd" }
    80.             Fog { Mode Off }
    81.             Lighting On
    82.             ZWrite Off
    83.  
    84.             //Blend Zero One        // hide add
    85.             Blend One Zero    // hide base
    86.  
    87.             CGPROGRAM
    88.             #pragma target 3.0
    89.  
    90.             #pragma multi_compile_fwdadd_fullshadows
    91.             #pragma multi_compile_fog
    92.          
    93.             #pragma target 3.0
    94.             #pragma vertex vert
    95.             #pragma fragment frag
    96.          
    97.             float4 _MainTex_TexelSize;
    98.  
    99.             struct appdata_t
    100.             {
    101.                 float4 vertex            : POSITION;
    102.                 float3 normal            : NORMAL;
    103.                 float2 uv                : TEXCOORD0;
    104.             };
    105.  
    106.             struct v2f
    107.             {
    108.                 float4 pos                    : POSITION;
    109.                 float3 tex                    : TEXCOORD0;
    110.                 fixed3 normalWorld            : TEXCOORD1;
    111.                 LIGHTING_COORDS(2,3)
    112.                 float3 posWorld                : TEXCOORD4;
    113.             };
    114.          
    115.             v2f vert (appdata_t v)
    116.             {
    117.                 v2f o;
    118.              
    119.                 o.pos = UnityObjectToClipPos(v.vertex);
    120.                 o.tex = float3((TRANSFORM_TEX(v.uv.xy, _MainTex)).xy,1);
    121.              
    122.                 o.posWorld =   mul(unity_ObjectToWorld, v.vertex);
    123.                 o.normalWorld = mul( unity_ObjectToWorld, float4( v.normal, 0.0 ) ).xyz;
    124.  
    125.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    126.              
    127.                 return o;
    128.             }
    129.          
    130.             fixed4 frag (v2f i) : COLOR
    131.             {
    132.                 float2 uv = i.tex.xy;
    133.                 UNITY_LIGHT_ATTENUATION(atten, i, i.posWorld + ...) // changing posWorld paramenter affects lighting
    134.                   return atten;
    135.             }
    136.          
    137.             ENDCG
    138.          
    139.         }
    140.  
    141.          Pass
    142.         {
    143.             ZWrite On
    144.              Tags {"LightMode"="ShadowCaster"}
    145.          
    146.              CGPROGRAM
    147.              #pragma vertex vert
    148.              #pragma fragment frag
    149.              #pragma multi_compile_shadowcaster
    150.              #include "UnityCG.cginc"
    151.          
    152.              struct v2f {
    153.                  V2F_SHADOW_CASTER;
    154.              };
    155.          
    156.              v2f vert(appdata_base v)
    157.              {
    158.                  v2f o;
    159.                  TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    160.                  return o;
    161.              }
    162.          
    163.              float4 frag(v2f i) : SV_Target
    164.              {
    165.                  SHADOW_CASTER_FRAGMENT(i)
    166.              }
    167.          
    168.              ENDCG
    169.          }
    170.      
    171.     }
    172. }
     
    Last edited: Mar 23, 2021
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,338
    Unity's directional light shadows, the only kind of light supported by the base pass, don't pass the actual shadow map(s) to the shader. Instead it renders the directional shadows to a screen space texture that uses the camera depth texture to reconstruct the world space position from from. The short of that is there's nothing you can do to offset the world space position used to sample the shadows on a per object basis. At least not if you rely on Unity's built in shadow systems.

    There is a solution though, at least for one directional light. You can make the shadow maps be globally accessible, and then use custom shader code that samples them. There's a project on Github that shows exactly how to do that here:
    https://github.com/Gaxil/Unity-Inte...Assets/Scripts/SetShadowMapAsGlobalTexture.cs
    https://github.com/Gaxil/Unity-InteriorMapping/blob/master/Assets/Shaders/Shadows.cginc

    But, as I mentioned, this only works for the "main" directional light in the scene. If you have multiple shadow casting directional lights, there's no real way to match a light to a specific pass. The screen space shadow textures for all shadow casting directional lights are calculated at the start before the main camera rendering, and there's nothing in the add pass to say which light it is that's running outside of crazy hackery like comparing the light direction to a list in the shader and having access to the shadow maps for all directional lights in all directional light passes.
     
    MohammadAlizadeh and heinn_ like this.
  3. heinn_

    heinn_

    Joined:
    Jul 30, 2015
    Posts:
    3
    I kind of went on that route already and thought "this feels too hacky, surely I'm doing something wrong" but one shadow casting directional light is all I really need so that will do.

    Thank you for your help!
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,338
    The 'slightly' less hacky feeling option would be to disable screen space shadows on desktop, but that stopped working years ago and if you try to disable screen space shadows on desktop now all directional shadows just stop working entirely. So this remains the only real option.
     
    astracat111 and heinn_ like this.
  5. heinn_

    heinn_

    Joined:
    Jul 30, 2015
    Posts:
    3
    The link you posted works like a charm, I'd rather not make my life more complicated by fighting with deprecated features.

    Thanks again :)