Mam problem z cieniami dla światła punktowego. Widziałem sporo artykułów, filmików, pytań zadanych na forach związanych z tym tematem:
https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows
https://developer.nvidia.com/gpugems/gpugems/part-ii-lighting-and-shadows/chapter-12-omnidirectional-shadow-mapping
https://ogldev.org/www/tutorial43/tutorial43.html
i wiele innych...
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:
ale jak odpale apke to cienie już niekoniecznie:
fioletowa kula to "wizualizator" światła
kolejny screenshot, tym razem ze światłem za ścianą:
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`.