Shout
Shout

Reputation: 683

Point light shadows work wrong, how can I debug it?

I've got a problem with my point light shadows.

I've seen several articles, videos, questions:

and I wrote this (pseudo) code:

struct PointShadowMatrix
{
    glm::mat4 viewProjection[6];
};
const glm::mat4 shadowProj = glm::perspective(Math::HALF_PI, 1.0f, 25.0f, 1.0f);

const glm::vec3 lightPos = light.getPosition();

PointShadowMatrix shadowData;
shadowData.viewProjection[0] = shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(1.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
shadowData.viewProjection[1] = shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0));
shadowData.viewProjection[2] = shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, -1.0));
shadowData.viewProjection[3] = shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, -1.0, 0.0), glm::vec3(0.0, 0.0, 1.0));
shadowData.viewProjection[4] = shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 0.0, 1.0), glm::vec3(0.0, 1.0, 0.0));
shadowData.viewProjection[5] = shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 0.0, -1.0), glm::vec3(0.0, 1.0, 0.0));

shadowConstantBuffer.AddData(shadowData);

auto textureCube = TextureCube(
    .Width = 1024,
    .Height = 1024,
    .MipLevels = 1,
    .Format = DXGI_FORMAT_R24G8_TYPELESS,
    .BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE
);

auto cubeDepthRenderTarget = CubeRenderTarget(
    .resource = textureCube,
    .FirstArraySlice = 0,
    .MipSlice = 0
);

auto shader = Shader(.path = "PointShadowShader.hlsl");

shader.Bind();
cubeDepthRenderTarget.Bind();
shadowConstantBuffer.Bind();

SetViewport(.w = 1024, .h = 1024);
ClearDepth(cubeDepthRenderTarget, 0.0f);

DrawDepth();

PointShadowShader:

struct VertexInput
{
    float3 position : POSITION;
    
    nointerpolation float4 modelToWorld1 : I_MODEL_TO_WORLD_ONE;
    nointerpolation float4 modelToWorld2 : I_MODEL_TO_WORLD_TWO;
    nointerpolation float4 modelToWorld3 : I_MODEL_TO_WORLD_THREE;
    nointerpolation float4 modelToWorld4 : I_MODEL_TO_WORLD_FOUR;
};

float4 VSMain(VertexInput input) : SV_POSITION
{
    float4x4 model = float4x4(input.modelToWorld1, input.modelToWorld2, input.modelToWorld3, input.modelToWorld4);
    return mul(float4(input.position, 1.0f), model);
}

cbuffer PointShadowData : register(b4)
{
    row_major float4x4 pointShadowViewProjection[6];
};

struct GS_OUTPUT
{
    float4 position : SV_POSITION;
    float3 worldPosition : WORLD_POSITION;
    uint RTIndex : SV_RenderTargetArrayIndex;
};

[maxvertexcount(18)]
void GSMain(triangle float4 position[3] : SV_POSITION, inout TriangleStream<GS_OUTPUT> OutStream)
{
    for (uint face = 0; face < 6; ++face)
    {
        GS_OUTPUT output;

        output.RTIndex = face;

        for (uint v = 0; v < 3; ++v)
        {
            output.worldPosition = position[v].xyz;
            output.position = mul(position[v], pointShadowViewProjection[face]);
            OutStream.Append(output);
        }
        OutStream.RestartStrip();
    }
}

cbuffer LightInfo : register(b1)
{
    float3 lightPos;
};

struct PixelInput
{
    float4 position : SV_POSITION;
    float3 worldPosition : WORLD_POSITION;
};

float PSMain(PixelInput input) : SV_DEPTH
{   
    //I tried input.position instead of input.worldPosition, it didn't fixed the problem
    //I tried NVidia's approach to calculate squared distance instead of length
    //I tried using just a length, without dividing by far plane
    //Nothing fixed the problem
    return length(input.worldPosition - lightPos) / 25.0f;
}

The depth pass looks good to me (light space perspective): enter image description here

but the shading version does not:

enter image description here

the shpere in the middle is a light "visualizer".

Another screenshot, now from behind of the wall: enter image description here

the way I use the depth map to calculate shadows:

float PointLightShadows(float3 worldPosition, float3 lightPosition)
{
    float3 fragToLight = worldPosition - lightPosition;
    float distance = length(fragToLight);
    
    return t_pointShadowMap.SampleCmpLevelZero(g_shadowBorder, float4(fragToLight, lightIndex), distance / 25.0f);}

sampler:

Sampler shadowBorder {
    D3D11_SAMPLER_DESC
    {
        .Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT,
        .AddressU = D3D11_TEXTURE_ADDRESS_BORDER,
        .AddressV = D3D11_TEXTURE_ADDRESS_BORDER,
        .AddressW = D3D11_TEXTURE_ADDRESS_BORDER,
        .ComparisonFunction = D3D11_COMPARISON_GREATER,
        .BorderColor = 1.0f
    }
};

As I wrote before, I've seen all these articles and I tried every approach authors show there, nothing worked.

Depth buffer is cleared to 0, depth function is GREATER

what is wrong with my code? How can I fix it? What things should I consider checking while debugging this problem?

Upvotes: 1

Views: 97

Answers (1)

Shout
Shout

Reputation: 683

I've cleared the depth buffer to 1.0f, set depthFunc to LESS, and sampler ComparisonFunction to LESS and it works now. My question is why? What was wrong with the reversed depth version?

Upvotes: 0

Related Questions