Skocz do zawartości

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


Recommended Posts

Napisano (edytowany)

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

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...