Cascaded Shadow Maps - Win32 apps | Microsoft Learn
Cascaded Shadow Maps - Win32 apps
Cascaded shadow maps (CSMs) are the best way to combat one of the most prevalent errors with shadowing perspective aliasing.
learn.microsoft.com
MSDN에 적힌 이론대로 구현이 가능하다.
기본적으로 렌더링 할 장면을 기준으로 거리에 따라 Light Matrix를 다르게 주어 기하 셰이더를 통해 버텍스를 복사하여 여러장의 그림자 맵을 그리는 것이다. 그렇기에 한번 드로우 콜로 여러장의 그림자 맵을 얻을 수 있다.
그런데 여기서 거리가 멀 수록 덜 정확한 그림자맵을 사용한다는 가정을 두고 직사광의 경우에는 Light Matrix를 직교 투영 행렬로 만드는데 직교 투영의 상자 크기를 다르게 주어 거리가 멀어 정밀도가 떨어지는 그림자맵을 그릴때는 상자의 크기를 크게, 거리가 가까워 정밀도가 높은 그림자 맵을 그릴때는 상자의 크기를 작게 주어 그림자 맵을 그린다.
거리에 따른 그림자 맵을 그리기 위해 그림자를 그리는 영역을 4개로 나누었다.
m_cascadeEnd[0] = m_Near;
m_cascadeEnd[1] = 10.0f,
m_cascadeEnd[2] = 20.0f,
m_cascadeEnd[3] = 40.f;
m_cascadeEnd[4] = 100.f;
cascadedLight._cascadeEndClipSpace[0].x = m_cascadeEnd[1];
cascadedLight._cascadeEndClipSpace[1].x = m_cascadeEnd[2];
cascadedLight._cascadeEndClipSpace[2].x = m_cascadeEnd[3];
cascadedLight._cascadeEndClipSpace[3].x = m_cascadeEnd[4];
// Get the inverse of the view transform
Math::Matrix Cam = m_View;
Math::Matrix CamInv = m_View.Invert();
// Get the light space transform
Math::Matrix LightM = Math::Matrix::CreateLookAt(Math::Vector3(0.0f, 0.0f, 0.0f), directionalLightDir, Math::Vector3(0.0f, 1.0f, 0.0f));
float tanHalfHFOV = std::tanf((m_FieldOfView / 2.0f));
float tanHalfVFOV = std::tanf(((m_FieldOfView * m_AspectRadio) / 2.0f));
for (uint i = 0; i < NUM_CASCADES; i++) {
float xn = m_cascadeEnd[i] * tanHalfHFOV;
float xf = m_cascadeEnd[i + 1] * tanHalfHFOV;
float yn = m_cascadeEnd[i] * tanHalfVFOV;
float yf = m_cascadeEnd[i + 1] * tanHalfVFOV;
Math::Vector4 frustumCorners[NUM_FRUSTUM_CORNERS] = {
// near face
Math::Vector4(xn, yn, m_cascadeEnd[i], 1.0),
Math::Vector4(-xn, yn, m_cascadeEnd[i], 1.0),
Math::Vector4(xn, -yn, m_cascadeEnd[i], 1.0),
Math::Vector4(-xn, -yn, m_cascadeEnd[i], 1.0),
// far face
Math::Vector4(xf, yf, m_cascadeEnd[i + 1], 1.0),
Math::Vector4(-xf, yf, m_cascadeEnd[i + 1], 1.0),
Math::Vector4(xf, -yf, m_cascadeEnd[i + 1], 1.0),
Math::Vector4(-xf, -yf, m_cascadeEnd[i + 1], 1.0)
};
Math::Vector4 frustumCornersL[NUM_FRUSTUM_CORNERS];
float minX = std::numeric_limits<float>::max();
float maxX = std::numeric_limits<float>::min();
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::min();
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::min();
for (uint j = 0; j < NUM_FRUSTUM_CORNERS; j++) {
Math::Vector4 vW = Math::Vector4::Transform(frustumCorners[j], CamInv);
frustumCornersL[j] = Math::Vector4::Transform(vW, LightM);
minX = std::min(minX, frustumCornersL[j].x);
maxX = std::max(maxX, frustumCornersL[j].x);
minY = std::min(minY, frustumCornersL[j].y);
maxY = std::max(maxY, frustumCornersL[j].y);
minZ = std::min(minZ, frustumCornersL[j].z);
maxZ = std::max(maxZ, frustumCornersL[j].z);
}
m_ShadowOrthoProjInfo[i]._right = maxX;
m_ShadowOrthoProjInfo[i]._left = minX;
m_ShadowOrthoProjInfo[i]._bottum = minY;
m_ShadowOrthoProjInfo[i]._top = maxY;
m_ShadowOrthoProjInfo[i]._far = maxZ;
m_ShadowOrthoProjInfo[i]._near = minZ;
cascadedLight._lightTransform[i] = LightM * Math::Matrix::CreateOrthographicOffCenter(
m_ShadowOrthoProjInfo[i]._left,
m_ShadowOrthoProjInfo[i]._right,
m_ShadowOrthoProjInfo[i]._bottum,
m_ShadowOrthoProjInfo[i]._top,
m_ShadowOrthoProjInfo[i]._near,
m_ShadowOrthoProjInfo[i]._far
);
}
일단 제대로 그림자 맵을 선택하는지 알기 위해 z거리가 가까운 순으로 빨간색, 초록색, 파란색을 출력하도록 하고 그리면 아래와 같이 된 모습을 볼 수 있다.
'DirectX11' 카테고리의 다른 글
Texture Compression(Block Compression) (0) | 2023.06.27 |
---|---|
GPU Instancing (0) | 2023.06.22 |
Voxel GI - Cone Tracing (0) | 2023.05.25 |
Voxel GI - Voxelize (0) | 2023.05.25 |
Shadow Mapping (0) | 2022.09.27 |