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

Question How to determine which shadow cascade an object lies in?

Discussion in 'General Graphics' started by Gameslinx, Mar 26, 2023.

  1. Gameslinx

    Gameslinx

    Joined:
    Jun 24, 2017
    Posts:
    7
    Hi,
    I am using DrawMeshInstancedIndirect in order to draw a lot of objects on a terrain mesh. Small issue - This draws objects in every shadow cascade, regardless of whether the object actually lies within that cascade. This is leading to each object being rendered 4 times for the shadow pass instead of just once.

    In order to solve this, I would like to sort the objects into 4 groups based on which shadow cascade they are in.
    I already have a framework set up which uses a CommandBuffer to draw each set of objects in the correct order in the correct shadow cascade.

    My issue is with the calculation to determine which shadow cascade my objects lie in. The rest works fine.
    My initial understanding was that in order to calculate which shadow cascade a position is in, it's a simple distance check:

    Code (CSharp):
    1.  
    2. float percentageToShadowDistance = distance(_WorldSpaceCameraPosition, worldPos) / shadowDistance;
    3. if (percentageToShadowDistance < _CascadeSplits.x)
    4. {
    5.      //We are in cascade 0
    6. }
    7. //And so on
    But it appears that this is not the case. When comparing the true shadow cascade transition to the one computed from distance, it is slightly off. There is a gap where the cascade changes after where I compute that transition to be. This gap changes shape based on camera position and FOV. I suspect it has something to do with one of the camera's matrices, but I don't know where to go from here.



    Any assistance would be greatly appreciated, as I've not been able to find anything on this.
    Cheers!
     
  2. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    I can't directly suggest a solution, but is there something being overlooked? An object could easily be in more than one cascade, such as if it is large, and depending on the render pipeline or custom lighting shaders, etc., there can also be a transition overlap between cascades.
     
  3. Gameslinx

    Gameslinx

    Joined:
    Jun 24, 2017
    Posts:
    7
    I'll need to introduce a slight overlap or investigate performing some form of more precise culling in the vertex shader so that objects don't pop in and out at cascade edges. This could mean rendering two objects at cascade borders, but still a significant performance improvement over rendering 4 objects everywhere!
     
  4. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    Oh, for sure, I was just pointing out that this will necessarily involve more nuance than "which shadow cascade my objects lie in", which you may have already known anyway.
     
    Gameslinx likes this.
  5. Gameslinx

    Gameslinx

    Joined:
    Jun 24, 2017
    Posts:
    7
    I managed to figure this out. For anyone else wondering:
    From here: https://github.com/TwoTailsGames/Un...494e8328915621/CGIncludes/UnityCG.cginc#L1158
    Code (CSharp):
    1. float4 GetCascades(float3 wpos, float overlapFactor)
    2. {
    3.     float3 fromCenter0 = wpos - unity_ShadowSplitSpheres[0].xyz;
    4.     float3 fromCenter1 = wpos - unity_ShadowSplitSpheres[1].xyz;
    5.     float3 fromCenter2 = wpos - unity_ShadowSplitSpheres[2].xyz;
    6.     float3 fromCenter3 = wpos - unity_ShadowSplitSpheres[3].xyz;
    7.  
    8.     float4 distances2 = float4(dot(fromCenter0,fromCenter0), dot(fromCenter1,fromCenter1), dot(fromCenter2,fromCenter2), dot(fromCenter3,fromCenter3));
    9.     fixed4 weights = float4(distances2 < unity_ShadowSplitSqRadii - overlapFactor);
    10.     fixed4 weights2 = float4(distances2 < unity_ShadowSplitSqRadii + overlapFactor);
    11.  
    12.     weights.yzw = saturate(weights.yzw - weights.xyz);
    13.     weights2.yzw = saturate(weights2.yzw - weights2.xyz);
    14.  
    15.     return weights + weights2;
    16. }
    "Weights" is a boolean mask where only one component (x, y, z or w) will be greater than 0 at once. The component that is greater than 0 corresponds to the shadow cascade the world position lies in.
    I introduced an overlap factor (set to 10, currently) which allows world positions to lie within two shadow cascades at the border. In 'weights + weights2', two components can be greater than 0 at a time.

    upload_2023-4-6_0-14-30.png
    In this image, red corresponds to the nearest shadow cascade. Green corresponds to the second, and blue corresponds to the third. You can see that the yellow area is red + green, so objects here will be rendered in two shadow cascades to provide the overlap I require.

    Cyan (green + blue) is the same.

    The code automatically adjusts to two cascades or just 1 cascade without issue.
    Hope this helps anyone in the future :)
     
    lilacsky824 likes this.