Due to some material features, I need to create my own directional shadow map.

Everything seems to work ok, until the moment where I compare depths.

First I add a camera component to the directional light with a RenderTexture:

light camera setup

texture setup

Then in code, I set this texture and the camera projection matrix as global variables for shaders:

Camera shadowCaptureCamera = GetComponent<Camera>();
Shader.SetGlobalTexture("_CustomShadowsTex", shadowCaptureCamera.targetTexture, UnityEngine.Rendering.RenderTextureSubElement.Depth);
Matrix4x4 worldToClipMatrix = shadowCaptureCamera.projectionMatrix * shadowCaptureCamera.worldToCameraMatrix;
Shader.SetGlobalMatrix("_CustomShadowsMatrix", worldToClipMatrix);

Then in shaders I do this:

v2f vert(appdata v)
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);

    float4 worldPos = mul( unity_ObjectToWorld, float4( v.vertex.xyz, 1.0f) );
    float4 shadowPosition = mul( _CustomShadowsMatrix, worldPos );
    shadowPosition.xyz /= shadowPosition.w;
    shadowPosition.xy = shadowPosition.xy * 0.5f + 0.5f;
    o.shadowMapUV = shadowPosition;
    return o;

fixed4 frag(v2f i) : SV_Target


    float shadowDepth = tex2D( _CustomShadowsTex, i.shadowMapUV.xy ).r;             
    float atten = shadowDepth > abs(i.shadowMapUV.z) ? 1.0f : 0.0f;
    return atten;

But everything gets a value of 1.0f.

Any idea what's wrong? It samples the shadow map at the correct place, but something is wrong with the values. I'm also not sure why i.shadowMapUV.z is <0.f. Looking at the matrix's values, it shouldn't be.


2 Answers 2


Ok, I got it. i.shadowMapUV.z is < 0.f because it's mapped to -1;1 between near;far. Forward is also at -Z, so correct way to calculate shadowPosition is:

float4 shadowPosition= mul( _CustomShadowsMatrix, float4(worldPos, 1.f) );
shadowPosition.xyz /= shadowPosition.w;
shadowPosition.xy = shadowPosition.xy * 0.5f + 0.5f;
shadowPosition.z = -shadowPosition.z * 0.5f + 0.5f;

It's not a good method.Because there are different projection matrix with different platforms. So you should use:

Matrix4x4 posToUV = new Matrix4x4();
posToUV.SetRow(0, new Vector4(0.5f, 0, 0, 0.5f));
posToUV.SetRow(1, new Vector4(0, 0.5f, 0, 0.5f));
posToUV.SetRow(2, new Vector4(0, 0, 1, 0));
posToUV.SetRow(3, new Vector4(0, 0, 0, 1));
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, false);
Matrix4x4 matrixVP = posToUV * projectionMatrix * _camera.worldToCameraMatrix;

instead of:

Matrix4x4 worldToClipMatrix = shadowCaptureCamera.projectionMatrix * shadowCaptureCamera.worldToCameraMatrix;

it will apply vp matrix and correct the uv from [-1,1] to [0,1].
and you should handle the map manually in receive shadow shader's fragment:

float depth = i.shadowCoord.z / i.shadowCoord.w;
#if defined (SHADER_TARGET_GLSL)
    depth = depth * 0.5 + 0.5; //(-1, 1)-->(0, 1)
#elif defined (UNITY_REVERSED_Z)
    depth = 1 - depth;       //(1, 0)-->(0, 1)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .