Search Unity

Adding Shadows to Image Effect Shader

Discussion in 'Shaders' started by Agent0023, Jan 5, 2021.

  1. Agent0023

    Agent0023

    Joined:
    Jul 31, 2017
    Posts:
    11
    I'm very new to Image effect shaders and part of an atmospheric image effect shader I'm working on (adapting, from Simeon's atmospheric shader simeonradivoev/GPU-Planetary-Rendering: GPU atmosphertic scattering and planet generation in Unity 3D (github.com)) I want to cast shadows onto the atmosphere.

    Most of the hard work is already done for me thanks to Simeon's great work, but I figured to accomplish this I can take several approaches. My first approach was to add a command buffer to the singular light (directional) in the scene, capturing the shadowmap, and sampling that in my fragment shader using the shadow cascade coordinates. This didn't work out at all and I was disappointed to see that the shadowmask texture that was being sent to the shader from my light was always black (no visible shadows). This is why I suspect this approach didn't work.

    Here's my shadow computation code in the frag shader for this approach:

    Code (CSharp):
    1.  
    2.                 float vDepth = distance(_WorldSpaceCameraPos, float4(wpos, 1.));
    3.                 float4 near = float4(vDepth >= _LightSplitsNear);
    4.                 float4 far = float4(vDepth < _LightSplitsFar);
    5.                 float4 weights = near * far;
    6.                 float3 shadowCoord0 = mul(unity_WorldToShadow[0], float4(wpos, 1.)).xyz;
    7.                 float3 shadowCoord1 = mul(unity_WorldToShadow[1], float4(wpos, 1.)).xyz;
    8.                 float3 shadowCoord2 = mul(unity_WorldToShadow[2], float4(wpos, 1.)).xyz;
    9.                 float3 shadowCoord3 = mul(unity_WorldToShadow[3], float4(wpos, 1.)).xyz;
    10.                 float3 coord =
    11.                     shadowCoord0 * weights.x +
    12.                     shadowCoord1 * weights.y +
    13.                     shadowCoord2 * weights.z +
    14.                     shadowCoord3 * weights.w;
    15.              
    16.                 float shadowmask = tex2D(_DirectionalShadowMask, coord.xy).r;
    17.                 shadowmask = shadowmask > coord.z;
    18.                 shadowmask = 1.0 - shadowmask;
    19.                 return half4(shadowmask, shadowmask, shadowmask, 1);
    20.  

    wpos
    is calculated by sampling the camera depth here

    Code (CSharp):
    1.  
    2.                 float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
    3.                 depth = Linear01Depth(depth);
    4.                 float4 normal = tex2D (_CameraDepthNormalsTexture, i.uv);
    5.                 half3 ray = float3(i.uv.x*2-1, i.uv.y*2-1, 1);
    6.              
    7.                 ray *= _CamScreenDir.xyz;
    8.                 ray = ray * (_FarPlane / ray.z);
    9.                 float3 viewNorm = mul(_CameraInv,normal);
    10.                 float4 vpos = float4(ray * depth,0.995);
    11.                 float3 wpos = mul (unity_CameraToWorld, vpos).xyz;
    12.  

    I can also provide the code that adds the command buffer and sends it to shaders, but I doubt that's the issue since it works in another scene.

    My second idea was to use Unity's Autolight.cginc macros to calculate the shadow attenuation, but I had problems with this too. I've never messed around with light computation macros, but in all the examples I saw, TRANSFER_SHADOW() being called in the vert shader, passing in the vert output, but I don't think this works on image effect shaders, since v.vertex is in screen space. This discrepancy then leads to the shadow coordinates calculated using the wrong position I assume, and when I call SHADOW_ATTENUATION() in the frag shader, the output is not what I need.

    I'm unsure whether either of these approaches are correct to accomplish what I'm setting out to, but if either make sense to anyone or you're familiar with an alternative method, please let me know as I'm really lost on both.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    The built in macros in AutoLight.cginc are going to be useless for you. Those are all designed to work with the screen space shadow texture, which is just a texture of the shadow maps projected onto the depth texture. You need the actual shadow maps.

    You might try looking at a volumetric lighting system like this one:
    https://github.com/SlightlyMad/VolumetricLights

    Or check out the code in this project that makes the main directional light's shadow maps accessible globally and has custom code to sample them directly.
    https://github.com/Gaxil/Unity-Inte...Assets/Scripts/SetShadowMapAsGlobalTexture.cs
    https://github.com/Gaxil/Unity-InteriorMapping/blob/master/Assets/Shaders/Shadows.cginc
     
    Agent0023 likes this.