<aside> 💡
Point Light 는 6면에 대한 ShadowMap 을 만든다.
Pin Hole 카메라 해당 문제를 보고 곰곰히 생각해보니 위의 Pin Hole 카메라가 떠올랐다. 원근투영변환의 소실점 뒷부분에 상이 뒤집혀서 맺힌 것이다.
<aside> 💡
현재 문제의 근본적인 원인은 6면 ShadowMap을 큐브맵 형태로 만들지 않은 것이었다.
원래는 PointLight 의 6면 ShadowMap 에 대해 큐브맵을 만든 후에 현재 Fragment 위치에서 광원을 향하는 방향벡터로 큐브맵 샘플링을 했어야 했었다.
그러나 처음 해보는 구현해보는 입장에서, 일단 PointLight ShadowMap 이 어떻게 나올지 모르는 상황이다보니, 우선 6면 큐브맵을 안 만들고, 6면에 대해 다 샘플링을 해보고자 한 것이 근본적인 문제의 원인이었다. </aside>
<aside> 💡
<aside> 💡
광원 절두체 밖의 픽셀을 샘플링하는 문제인 것이다.
나는 이 문제를 Shader 안에서 절두체영역인지 아닌지를 검사하는식을 해결하였다.
다만 근본적인 해결방법은 PointLight 의 6면 ShadowMap 에 대해 큐브맵을 만든 후에 현재 Fragment 위치에서 광원을 향하는 방향벡터로 큐브맵 샘플링을 하도록 해야한다.
https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping
<aside> 💡
정상적으로 ShadowMap 을 만들기 위해서는 카메라의 절두체 안쪽에 해당하는 좌표에 대해서만 DepthMap 에 투영시켜 그림자영역인지 아닌지 판단해야한다.
우선 다시 한 번 더 ShadowMap 원리를 점검해보자
어떤 픽셀의 월드좌표계를 LightViewProj (DepthMap 카메라의 투영변환행렬) 로 행렬연산 수행 시, DepthMap 카메라의 View 좌표계상에서의 위치를 얻을 수 있다.
해당 View 좌표계상에서의 위치를 동차좌표계의 w 값으로 나누어 NDC 좌표계상에서의 정규화된 좌표값을 얻는다.
NDC 좌표의 x, y 값의 범위는 -1 ~ 1 이다.
그리고 DirectX 화면좌표계와 텍스쳐 uv의 범위는 0 ~ 1 이다.
따라서 float2 uv = float2(ndc.x, -ndc.y) * 0.5f + 0.5f;
를 통해 uv 좌표로 변환한다. (DepthMap 텍스쳐에 투영됨)
DepthMap 텍스쳐에서 투영된 uv 좌표의 Depth 값과 NDC 좌표계상에서의 Depth값을 비교하여 그림자인지 아닌지를 판단한다.
기존에는 uv 범위검사만 하여 절두체 바깥에 있는 좌표에 대해서도 투영을 하였다.
NDC 좌표계는 절두체영역을 -1~1 사이의 범위로 정규화한 것이다. ( z 는 0~1 로 )
// 절두체 검사 (DirectX NDC 기준)
if (abs(ndc.x) > 1.0f || abs(ndc.y) > 1.0f || ndc.z < 0.0f || ndc.z > 1.0f)
{
return 1.0f; // 절두체 밖은 그림자 없음 처리
}