So, I wanted to add cascaded shadow maps just to allow some additional shadowing mode for directional lights. And here I hit a wall with HLSL.
This code is seemingly alright:
DirectionalLight dirLight = (DirectionalLight)lightsData[lightInput.id];
...
// Just for the check - assign red/green/blue/yellow color to each cascade (along with storing index)
float3 cascadeMul = float3(0.0f, 0.0f, 0.0f);
int cascadeID = -1;
if (posz < dirLight.cascadeShadowClip[0])
{
cascadeID = 0;
cascadeMul = float3(1.0f, 0.0f, 0.0f);
}
else if (posz < dirLight.cascadeShadowClip[1])
{
cascadeID = 1;
cascadeMul = float3(0.0f, 1.0f, 0.0f);
}
else if (posz < dirLight.cascadeShadowClip[2])
{
cascadeID = 2;
cascadeMul = float3(0.0f, 0.0f, 1.0f);
}
else if (posz < dirLight.cascadeShadowClip[3])
{
cascadeID = 3;
cascadeMul = float3(1.0f, 1.0f, 0.0f);
}
// Index of shadowmap in shadow map atlas
int index = dirLight.cascadeShadowID[cascadeID];
// Shadow mapping
if (cascadeID != -1 && index >= 0)
{
float4 positionProj = mul(shadowAtlasData[index].shadowMatrix, positionWS);
positionProj.xy /= positionProj.w;
positionProj.y = -positionProj.y;
positionProj.x *= 0.5f;
positionProj.x += 0.5f;
positionProj.y *= 0.5f;
positionProj.y += 0.5f;
positionProj.z -= lightsData[lightInput.id].offset;
positionProj.xy *= shadowAtlasData[index].size;
positionProj.xy += shadowAtlasData[index].offset;
if (positionProj.x > 0.0f && positionProj.x < 1.0f && positionProj.y > 0.0f && positionProj.y < 1.0f && positionProj.z < 1.0f)
{
shadowMask *= ShadowMap(shadowMap, shadowSampler, positionProj.xyz);
}
}
shadowMask = clamp(shadowMask, 0.0f, 1.0f);
...
Now, theoretically this code is correct (I did try it with hard setting index to 0, 1, 2, 3 and it does work properly (if I hard code indexes into shadowAtlasData it also works … but that kind of removes the purpose of texture atlas, and makes multiple directional lights kind of impossible to do). The problem appears when I want to set index dynamically based on values stored in structured buffer.
For further information - lightsData is a structured buffer, shadowAtlasData is also structured buffer.
Also further information - this will end up in having divergent index in draw call, I know that. One could say that on the line where I initialize index I should use NonUniformResourceIndex(cascadeID) and NonUniformResourceIndex (index) further on. I have tried that and it does not work. The whole code executes as if index was 0 all the time.
I'd prefer to avoid doing multiple render passes for directional lights (one for each cascade), but it still looks like the only option (unless I would really want to compute shadow N times - for each slice in cascaded shadow map … which is huge overkill).
And yet more information - I was originally using D3DCompileFromFile which allowed only up to Shader Model 5.1, I suspected that old compiler make break this down, so I've switched to dxc (DirectX Shader Compiler), and I'm using the recent build (binary distribution). I have tried with Shader Model 6.0 and Shader Model 6.1 profiles, without any change.
So, my questions are:
- Why NonUniformResourceIndex does not work here (is this a compiler bug?)?
- Is there any other reasonable workaround apart from doing N-passes?