Skocz do zawartości

Cienie dla światła punktowego nie działają prawidłowo. Jak mogę to zdebugować?


SC5Shout

Recommended Posts

Mam problem z cieniami dla światła punktowego. Widziałem sporo artykułów, filmików, pytań zadanych na forach związanych z tym tematem:

i napisałem taki (pseudo) kod

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
{
    //próbowałem input.position zamiast input.worldPosition, efekt ten sam
    //próbowałem rozwiązania z artykułu NVidi - obliczenie kwadratu dystansu
    //próbowałem zwykłego dystansu bez dzielenia przez far plane
    //nic nie pomogło
    return length(input.worldPosition - lightPos) / 25.0f;
}

mapa cieni wygląda nawet dobrze:

1dpbcMpUoRaXTwe5haZJNNSlZv4WAxbpVk2F0Gfn

ale jak odpale apke to cienie już niekoniecznie:

b0amiSy1l0g1cyAQzt4oeQbLEeJTAg44mXE5GPTh

fioletowa kula to "wizualizator" światła

kolejny screenshot, tym razem ze światłem za ścianą:

c1bo5frkRB0lhzxSQJdQTGwBc1omM6Jl8xBmeYkA

funkcja do samplowania tej mapy:

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);
}

Jak już wspominałem wcześniej, wchodziłem w wiele linków na ten temat i nie wiem, nic nie pomagało.

Bufor głębi jest czyszczony na `0`, `depthFunc` jest ustawione na `GREATER`.

Myślałem, że problem może leżeć np. w pozycji światła, ale sprawdziłem i w obu tych przypadkach (shadow pass, color pass), pozycja jest ta sama.

Gdzie może leżeć błąd? Jak go naprawić? Jakie rzeczy powinienem wziąć pod uwagę debugując ten problem?

Próbowałem jeszcze obliczyć `lightPosition - worldPosition` zamiast `worldPosition - lightPosition`, to też nie naprawia cieni. Zmieniałem wartości `up` w funkcji `lookUp` na różne, np. zamiast `glm::vec3(0.0f, 1.0f, 0.0f)` na `glm::vec3(0.0f, -1.0f, 0.0f)`. Negowałem po kolei `x`, `y`, `z` w `fragToLight`, zmieniałem z `row_major` na `column_major`, zmieniałem kolejności mnożenia, używałem funkcji `glm` z postfixami `RH` i `LH`.

Edytowano przez SC5Shout
Link do komentarza
Udostępnij na innych stronach

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gość
Odpowiedz...

×   Wkleiłeś zawartość bez formatowania.   Usuń formatowanie

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Utwórz nowe...