멈추지 않고 끈질기게
[Graphics][Unity] 쉐도우 매핑(Shadow Mapping) 본문
※ 해당 포스팅은 개인의 공부 정리용 글입니다. 틀린 내용이 있다면 추후 수정될 수 있습니다.
※ 해당 포스팅은 Unity 2021.3.15f1 버전을 기준으로 작성되었습니다.
※ 해당 포스팅은 하기 출처들을 참조하였습니다.
- 오지현, 유니티 그래픽스 최적화 스타트업, 비엘북스, 2019
이번 포스팅에서는 그림자를 그리는 방식인 쉐도우 매핑에 대해 알아보겠습니다.
1. 쉐도우 매핑의 원리
쉐도우 매핑은 카메라에서 측정한 깊이값(Depth)과 광원에서 측정한 깊이값이 다를 경우 이를 이용하여 그림자를 그리는 방식입니다. 우선 카메라로부터 픽셀의 위치까지의 거리를 렌더링하여 뎁스 텍스처(Depth Texture)를 생성합니다. 한마디로 카메라를 기준으로 픽셀까지의 거리(깊이)를 저장합니다.
여기에 추가로 광원에서 바라보는 오브젝트의 픽셀들의 깊이를 별도의 버퍼에 저장하며, 이 버퍼를 쉐도우맵이라 합니다.
뎁스 텍스처와 쉐도우맵이 준비되고 나면 뎁스 텍스처의 픽셀들을 순회하며 각 픽셀의 광원으로부터의 거리를 계산하며, 이 계산된 거리가 쉐도우맵에 저장된 거리보다 크다면 다른 오브젝트에 의해 차폐된 것으로 판단, 즉 그림자를 그려야 하는 픽셀로 판단합니다.
쉐도우 매핑 기법을 통해 실시간으로 그림자를 표현할 수 있지만, 성능적인 부담이 만만치 않습니다. 뎁스 텍스처와 쉐도우맵을 렌더링하기 위해 추가적인 드로우 콜이 필요하며, 화면상의 모든 픽셀에 대해 뎁스 텍스처와 쉐도우 맵의 정보를 비교, 그림자 영역을 계산하는 과정이 들어가기 때문에 픽셀 쉐이더의 부담이 늘어납니다. 따라서 이러한 실시간 그림자 처리는 CPU, GPU 모두에게 성능적인 부담을 가합니다. 우선적으로 그림자 처리가 필요 없는 오브젝트를 걸러냄으로서 드로우콜을 감소시킬 수 있습니다. 유니티의 경우 Mesh Renderer 컴포넌트에서 Cast Shadows 항목을 off로 하면 쉐도우맵 생성 시 렌더링 대상에서 제외시킬 수 있습니다.
2. 그림자 품질의 조정
유니티에서는 광원의 Light 컴포넌트의 Shadow Type을 통해 그림자의 품질을 조정할 수 있습니다. Soft Shadow 선택 시 테두리가 부드러운 고품질의 그림자를 얻을 수 있으나, 픽셀 쉐이더의 부담을 증가시킵니다(GPU 부담 상승). 따라서 Soft Shadow를 선택하려면 타겟 디바이스의 GPU 성능을 고려하여야 합니다. Hard Shadow 선택 시 GPU 부담은 비교적 줄어들지만 그림자 테두리에 계단현상(Aliasing)이 나타납니다.
Resolution 속성으로 쉐도우 맵의 해상도를 조절할 수 있으며, 높은 해상도를 선택할수록 고품질의 그림자를 얻을 수 있습니다. 특히 Hard Shadow의 경우 해상도가 높을수록 확대하지 않고선 체감하기 어려울 정도로 계단현상이 완화됩니다. 단, 쉐도우 맵의 해상도 증가는 한마디로 버퍼의 크기 증가, 즉 메모리 사용량의 증가로 이어지므로 주의해야 합니다.
Shadow Distance를 조절하여 그림자의 시각적인 품질을 높일 수도 있습니다. 해당 속성은 그림자가 그려지는 거리를 의미하며, 값이 낮을 수록 멀리 있는 곳은 그림자가 그려지지 않게 됩니다. 대신 값이 낮을수록 오브젝트의 디테일한 부분이 그려지지 않아 오히려 그림자 품질이 상승하는 듯한 효과를 얻을 수 있습니다. 물론 이 값이 너무 낮으면 그림자가 그려지다 도중에 끊기는 현상이 발생하기 때문에, 게임의 특징을 고려하여 그림자가 끊기지 않는 선에서 적절한 값을 찾아 설정해야 합니다.
상기 사진을 보면 Shadow Distance 20 쪽이 그림자가 깔끔하게 노출되지만, 우측 상단에 그림자가 끊기는 부분이 있음을 확인할 수 있습니다.
3. 캐스케이드 쉐도우 맵(Cascaded Shadow Map)
쉐도우 매핑 방식은 카메라의 원근법에 의한 해상도 이슈가 발생합니다. 기본적으로 오브젝트는 원근법이 적용된 카메라의 뷰 프러스텀 영역 안에 렌더링 되어 원근감을 표현하며, 쉐도우 맵은 카메라의 뷰 프러스텀 영역을 커버해야 합니다.
다만 쉐도우맵은 직사각형의 버퍼이므로, 프러스텀 끝부분의 픽셀 수와 앞부분의 픽셀 수가 차이나게 됩니다. 위 그림은 뷰 프러스텀 내 구체가 있는 상황을 도식화한 것으로, 위 예시에서 프러스텀 뒷부분은 20픽셀이지만 앞부분은 6픽셀밖에 되지 않습니다. 이와 같은 이유로 일반적인 쉐도우 매핑으로는 카메라와 가까울수록 그림자의 해상도가 떨어지게 됩니다. 쉐도우 맵의 전체 해상도를 높여서 해결할 수도 있지만, 이는 메모리 사용량을 크게 증가시키므로 비용이 높은 해결 방법입니다.
이런 문제를 해결하기 위해 프러스텀 영역을 단계별로 나누어 다른 해상도의 쉐도우맵을 사용하는 기법을 캐스케이드 쉐도우 맵(CSM, Cascaded Shadow Map)이라 합니다. 해상도가 떨어지는 프러스텀 앞쪽 영역에는 고해상도의 쉐도우 맵을 사용하여 품질을 높이고, 뒤쪽 영역일수록 화면 상 멀리있는 물체이기 때문에 상대적으로 낮은 해상도의 쉐도우 맵을 사용하는 것입니다. 전체 해상도를 높이는 방법보다는 메모리를 덜 사용하면서 그림자의 품질을 개선할 수 있습니다. 유니티에서는 Edit -> Project Settings -> Quality 의 Shadow 항목에서 분할 개수 및 영역별 비율을 설정할 수 있습니다.
다만 이 기법은 영역별로 별도의 쉐도우 맵을 사용하여 따로따로 렌더링하기 때문에 드로우 콜이 증가하며, 영역을 분할하여 연산하는 추가 처리때문에 픽셀 쉐이더의 부담 또한 증가하게 됩니다. 한마디로 CPU와 GPU 모두에게 성능 부담을 주게 됩니다. 따라서 캐스케이드 쉐도우 맵을 사용하더라도 영역을 몇 단계로 분할했을 때 품질과 성능(속도)의 균형이 가장 잘 맞는지 테스트가 필요합니다.
'Graphics' 카테고리의 다른 글
[Graphic][Unity] 라이트 프로브(Light Probe), 리플렉션 프로브(Reflection Probe) (0) | 2023.03.21 |
---|---|
[Graphics][Unity] 라이트 맵(Light Map) (1) | 2023.03.10 |
[Graphics] 포워드 렌더링 vs 디퍼드 렌더링 (0) | 2023.03.02 |
[Graphics] 드로우 콜(DrawCall) (0) | 2023.02.20 |
[Graphics] 렌더링 파이프라인(Rendering Pipeline) (0) | 2023.02.12 |