Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Accessing Screenspace ShadowMap?

Discussion in 'Shaders' started by SunnySunshine, Sep 1, 2019.

  1. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    974
    I want to use the screenspace shadow map in an Image Effect (post render), but I'm having trouble finding a way to access it. The _ShadowMapTexture will be automatically set if you include such a variable in your shader, but unlike regular objects, this texture will not be the screen space shadow map but instead... just a completely black 4k x 2k texture? I thought that maybe you could copy the shadowmap into your own render texture using a command buffer, and then use that copy for the image effect later on. However, I haven't been able to achieve this either. There's no BuiltinRenderTextureType that corresponds to the screenspace shadow map.

    I thought that maybe the _ShadowMapTexture would be automagically set if used in a command buffer instead with the BeforeForwardOpaque camera event, but alas, this doesn't work. It's the same completely black texture being set - not the screen space one.

    Code (CSharp):
    1.         var mat = new Material(Shader.Find("Hidden/CopyShadowMap"));
    2.         cmd.Blit(null, shadowMapCopyRT, mat);
    3.  
    4.         cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, cmd);
    Code (CSharp):
    1. Shader "Hidden/CopyShadowMap"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         //_ShadowMapTexture("Texture", 2D) = "white" {}
    7.     }
    8.         SubShader
    9.     {
    10.         // No culling or depth
    11.         Cull Off ZWrite Off ZTest Always
    12.  
    13.         Pass
    14.         {
    15.             CGPROGRAM
    16.             #pragma vertex vert
    17.             #pragma fragment frag
    18.  
    19.             #include "UnityCG.cginc"
    20.  
    21.             sampler2D _ShadowMapTexture;
    22.  
    23.             struct appdata
    24.             {
    25.                 float4 vertex : POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.             };
    28.  
    29.             struct v2f
    30.             {
    31.                 float2 uv : TEXCOORD0;
    32.                 float4 vertex : SV_POSITION;
    33.             };
    34.  
    35.             v2f vert (appdata v)
    36.             {
    37.                 v2f o;
    38.                 o.vertex = UnityObjectToClipPos(v.vertex);
    39.                 o.uv = v.uv;
    40.                 return o;
    41.             }
    42.            
    43.             sampler2D _MainTex;
    44.  
    45.             fixed4 frag (v2f i) : SV_Target
    46.             {
    47.                 fixed4 col = tex2D(_ShadowMapTexture, i.uv);
    48.  
    49.                 return col;
    50.             }
    51.             ENDCG
    52.         }
    53.     }
    54. }
    55.  
    Any ideas?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    The screen space shadow texture is only made available to shader passes that are using “LightMode”=“ForwardBase”, are part of the opaque queue range, and are being rendered via some path that could be plausibly lit. A Blit() is always assumed to be unlit, so it will never get that texture assigned, regardless of what you do in the shader. So this technique isn’t really viable.

    But there’s an alternative. Don’t make a copy, just use the original texture.

    If you use a command buffer attached to the directional light, you can assign the screen space shadow texture to a global texture with a custom name that will always be available.
    Code (csharp):
    1. cmd.SetGlobalTexture(“_MyScreenSpaceShadows”, BuiltinRenderTextureType.CurrentActive);
    2. light.AddCommandBuffer(LightEvent.AfterScreenspaceMask, cmd);
     
    Autarkis, KMDeclius, cgDoofus and 3 others like this.
  3. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    974
    Awesome @bgolus, that's exactly what I'm looking for. I'll try this out tomorrow. :)
     
  4. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    974
    Works 100%. Thanks again!
     
  5. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    974
    @bgolus
    Out of curiosity - is there any way to render a mesh using a command buffer, that will have the screen space shadow map texture automagically set? I just tried this and it doesn't work:

    Code (CSharp):
    1.         var mat = new Material(Shader.Find("Standard"));
    2.         mat.EnableKeyword("SHADOWS_SCREEN");
    3.         cmd.DrawMesh(mesh, Matrix4x4.identity, mat, 0, 0);
    4.      
    5.         //cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, cmd);
    6.         cam.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cmd);
    It will just get the completely black 4k x 2k shadowmap texture.

    Another weird thing I noticed - when using the CameraEvent.BeforeForwardOpaque, the light direction would be inversed for some reason. That seems really weird to me.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    No.

    DrawMesh just draws the mesh. It doesn’t do any of the lighting setup for that shader pass, so the values will be whatever the current state was left by the previous pass rendered.
     
  7. cgDoofus

    cgDoofus

    Joined:
    Jun 15, 2021
    Posts:
    48
    Apologies for bumping this old thread, but is it possible to access the screenspace _ShadowMapTexture in deferred mode? I want to access it in a full screen shader

    This method worked but only for forward
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    If you're trying to access the shadow map texture in the deferred pass, no, because the shadow map doesn't exist yet. When using the forward renderer the shadow map is rendered before the opaque passes. In deferred it's rendered after deferred opaques, just before that light's deferred lighting pass. All forward passes rendered after that will have access to the shadow map if you use the above technique.
     
  9. cgDoofus

    cgDoofus

    Joined:
    Jun 15, 2021
    Posts:
    48
    Hmm, so it's not possible to access the screenspace shadowmap when camera's renderpath is set to deferred, therefore I'd need to render the scene again from a camera that uses forward path? is that even possible?
    I'm trying to access it in a compute shader that I'm using as a full screen shader