Search Unity

Reading a depth value from Unity's shadow map?

Discussion in 'Shaders' started by golonac, Apr 28, 2014.

  1. golonac

    golonac

    Joined:
    Apr 28, 2014
    Posts:
    4
    Hi! I'm wondering if there's some way to get the occluder depth value from Unity's shadow map, to determine the occluder's distance to the occluded pixel? I managed to sample the shadow map, but each channel contains different values and I'm not sure which, if any, is the occluder depth value. It would be ideal if there were some built in function for getting the depth value in a cross-platform manner, but I don't know if one exists?

    Of course it's possible to do an extra depth-only pass to a custom rendertarget and use that, but I suspect the information should already exist in some format in Unity's shadow map (and the alternative isn't available on the free license).

    As a background, I'm looking to create a shader for an object that doesn't receive sharp shadows, but instead the light attenuation should be determined by the distance to the occluder. I'm rendering a cloud, with only 1st order scattering, assuming only self-shadows and constant density, and approximating that a ray entering the cloud will remain within the cloud until exiting at the rendered pixel. In this scenario, light extinction is a function of the distance between the rendered position and the occluder. I'm looking to precompute higher orders of scattering once I get this direct component working.

    If anyone has insight to this, I'd appreciate any help!
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
  3. golonac

    golonac

    Joined:
    Apr 28, 2014
    Posts:
    4
    Thanks for the reply! That's not exactly what I'm after - I'm looking for a way to get the occluder distance from the light source, not from the camera.
     
  4. AdBar

    AdBar

    Joined:
    Feb 1, 2014
    Posts:
    17
    What you could do is place a camera at your lightsource and point it in its direction, render depth texture from that camera and your main camera and compare those.

    Edit : Saw you already suggested that, I dont know if you can do this through the shadow map, I think your gonna need a second camera. But gl with your issue hehe.
     
  5. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Ah, right, I replied a bit too quickly. I see this in the help:

    UNITY_SAMPLE_SHADOW(tex,uv) - samples shadowmap texture "tex" at given "uv" coordinate (XY components are texture location, Z component is depth to compare with). Returns single float value with the shadow term in 0..1 range.

    So that could help you.
     
  6. golonac

    golonac

    Joined:
    Apr 28, 2014
    Posts:
    4
    Thanks for the help!

    AdBar: Yeah, that is the solution I'm trying to avoid since it's a lot more complicated than just sampling a pre-existing shadow map would be. I wonder if it's possible to use the other camera's automatically generated _CameraDepthTexture on another camera?

    jvo3dc: I did a quick test, the implementation of UNITY_SAMPLE_SHADOW is in HLSLSupport.cginc (I'm using DX11):
    Code (csharp):
    1.  
    2. #if defined(SHADOWS_NATIVE)  (defined(SHADER_API_D3D11) || defined(SHADER_API_D3D11_9X))
    3.  
    4. #define UNITY_DECLARE_SHADOWMAP(tex) Texture2D tex; SamplerComparisonState sampler##tex
    5. #define UNITY_SAMPLE_SHADOW(tex,coord) tex.SampleCmpLevelZero (sampler##tex,(coord).xy,(coord).z)
    6.  
    7. #elif ...
    8.  
    It samples the texture and returns either 0 or 1 depending on if the value is greater than coord.z. Looking at this it seems it would be possible to override this to return the depth value, something like this in the beginning of my shader:
    Code (csharp):
    1.  
    2. #if defined(SHADOWS_NATIVE)  (defined(SHADER_API_D3D11) || defined(SHADER_API_D3D11_9X))
    3.  
    4. #undef UNITY_DECLARE_SHADOWMAP
    5. // Turn "SamplerComparisonState" into "SamplerState"
    6. #define UNITY_DECLARE_SHADOWMAP(tex) Texture2D tex; SamplerState sampler##tex
    7.  
    8. #undef UNITY_SAMPLE_SHADOW
    9. // Turn "SampleCmpLevelZero" into regular sampling
    10. #define UNITY_SAMPLE_SHADOW(tex,coord) (1 - saturate(tex.Sample ((coord).z - sampler##tex,(coord).xy).r))
    11.  
    12. #elif ...
    13.  
    So instead of a hard 0 or 1, this should return an attenuation value moving from 1 to 0 as the distance between the occluder and the pixel grows to 1.

    But that caused some compiler errors and I'm don't know HLSL enough to understand why. I'll post the specific errors when I get back to the computer. Do you think this approach is possible? Has anyone done this?
     
  7. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I was also looking at the cginc files and came across those defines. I would not undefine the original, but define a new function instead and use that one explicitly.

    What I see is that with the DX11 path, the depth compare is done in UNITY_SAMPLE_SHADOW. Other paths don't seem to have that option and just return the depth and it is later compared. I think you should copy that path. Here's a part from autolight.cginc for the non-native soft shadow lookup:
    Code (csharp):
    1.  
    2. shadowVals.x = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[0].xy ));
    3. shadowVals.y = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[1].xy ));
    4. shadowVals.z = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[2].xy ));
    5. shadowVals.w = UNITY_SAMPLE_DEPTH (tex2D( _ShadowMapTexture, coord + _ShadowOffsets[3].xy ));
    6. half4 shadows = (shadowVals < coord.zzzz) ? _LightShadowData.rrrr : 1.0f;
    7.  
    And from UnityCG.cginc:
    Code (csharp):
    1.  
    2. float shadow = UNITY_SAMPLE_DEPTH(tex2D( _ShadowMapTexture, coord.xy )) < coord.z ? _LightShadowData.r : 1.0;
    3.  
    So, UNITY_SAMPLE_DEPTH(tex2D( _ShadowMapTexture, coord.xy )) should be the (platform independent) occluder distance.
     
  8. golonac

    golonac

    Joined:
    Apr 28, 2014
    Posts:
    4
    That looks good! But it's giving me an error, which probably has to do with the way the shadow map texture is defined in the DX11 path - I guess it's not a sampler2D? I have no idea how to fix this...

    Code (csharp):
    1.  
    2. 'tex2D': no matching 2 parameter intrinsic function (compiling for d3d11)
    3. Possible intrinsic functions are:
    4. tex2D(sampler2D, float2|half2|min10float2|min16float2)
    5. tex2D(sampler2D, float2|half2|min10float2|min16float2, float2|half2|min10float2|min16float2, float2|half2|min10float2|min16float2)
    6.  
     
  9. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    @golonac: that would be because you need to use tex2Dproj, of which can take a float4 for UV coordinate. using tex2D limits you to float2 for second argument.
     
  10. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    Actually... sorry to dig this up! :D

    I have a custom shadowmap implementation working here (Using unity's shadowmap, not a custom one) but I have found that on a mobile device, the shadow is lagging behind my cars! Anyone know why this is? o_O
     
  11. OlliQueck

    OlliQueck

    Joined:
    Apr 11, 2013
    Posts:
    49
    i suspect it uses the last frame's zbuffer for every calculation, if you have very high FPS like on pc you wouldnt notice but on 30fps you will notice.... i'm currently working on my own shadow mapping technique with stencil shadows but i'm stuck because unity clips away my shadowcasters even though they're visible, so now i'm starting with a camera-shadowmapping. Can i See your shadowmap code or some link to it? I'd take a look.
     
  12. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
    @Wurghi: Sure, I can gather my shadow mapping assets together and send them to you if you like. :) Noting though; atm, there isn't a way to depth-test them yet, as I haven't quite worked out the math for that... :D

    And I fixed my shadow lag by having an orthographic camera render the shadows before the main viewport starts. :) (After getting Unity 5, I switched to using my own shadow-mapping scripts, since us free users now have RTs. ;))
     
  13. FuzzyQuills

    FuzzyQuills

    Joined:
    Jun 8, 2013
    Posts:
    2,871
  14. jjxtra

    jjxtra

    Joined:
    Aug 30, 2013
    Posts:
    1,464
    Did you ever find a solution to read a depth value from the Unity shadowmap using a macro? None of the suggestions posted here worked and all resulted in a shader compile error.
     
  15. tspk91

    tspk91

    Joined:
    Nov 19, 2014
    Posts:
    131
    This is a bit necro but I ran into this thread when looking for the answer so it may be useful to some people (I looked everywhere, it's funny how simple it was in the end). The way to do it is using commandbuffers:

    CommandBuffer.SetShadowSamplingMode(null, ShadowSamplingMode.RawDepth).

    Then in the shader you sample it like this:

    tex2D(_Shadowmap, uv).r;
     
  16. mizhe09

    mizhe09

    Joined:
    Sep 22, 2017
    Posts:
    2
    When I am in lwrp, the shadow has a problem. don't like the doc says:
    // Sampling mode is restored automatically after this command buffer completes, so shadows will render normally.