While adding shadows maps for directional light sources (spatially restricted to an AABB), I noticed a weird artifact at the border of the shadow map. The region to the right is not illuminated directly by the directional light source. Though, one can clearly see the contours of the orthographic projection plane projected onto the floor of the scene. (P.S.: blame GameDev.net for the image compression artifacts )
This contour corresponds to normalized texture coordinates outside the [0,1]^2 range (i.e. just outside the orthographic projection plane projected onto the floor of the scene). Since, I use a D3D11_TEXTURE_ADDRESS_BORDER, the PCF sampler state, will use the border color. This border color is equal to my far plane, so obviously the points will never lie inside a shadow.
By using a different border color, I can circumvent the problem:
#ifdef DISABLE_INVERTED_Z_BUFFER
desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
#else // DISABLE_INVERTED_Z_BUFFER
desc.ComparisonFunc = D3D11_COMPARISON_GREATER_EQUAL;
desc.BorderColor[0] = 1.0f; // near instead of far plane = never in shadow
desc.BorderColor[1] = 1.0f; // near instead of far plane = never in shadow
desc.BorderColor[2] = 1.0f; // near instead of far plane = never in shadow
desc.BorderColor[3] = 1.0f; // near instead of far plane = never in shadow
#endif // DISABLE_INVERTED_Z_BUFFER
Unfortunately, this does not solve but rather hides the problem. The incident orthogonal irradiance of the directional light, should be zero. And since the contribution is zero, the shadow factor should not matter (i.e. it does not matter whether outside the shadow map region, points are considered lying inside or outside the shadow).
The incident orthogonal irradiance of the directional light is computed as follows:
void Contribution(DirectionalLight light, float3 p, out float3 l, out float3 E, out float3 p_ndc) {
const float4 p_proj = mul(float4(p, 1.0f), light.world_to_projection);
p_ndc = HomogeneousDivide(p_proj);
l = light.neg_d;
E = (any(1.0f < abs(p_ndc)) || 0.0f > p_ndc.z) ? 0.0f : light.E; // Should be zero outside the shadow map region.
}
The position, p_ndc, expressed in Normalized Device Coordinates is transformed to UV coordinates ([-1,1]x[-1,1]x[0,1] maps to [0,1]x[1,0]). The latter will be used in HLSL's SampleCmpLevelZero.
But am I allowed to use p_ndc as computed above directly? Do I miss a half-texel offset?
Edit: Given that "(any(1.0f < abs(p_ndc)) || 0.0f > p_ndc.z)" is false, the point should be located inside the AABB and thus inside the shadow map region. But in this case, how is it possible that a different border color has any effect at all?