[그래픽스 최적화] Shadow Map
카테고리: Unity
Main Reference:
- 유니티 그래픽스 최적화
🐳 Shadow Map
라이팅의 요소 중 하나인 그림자는 사실적인 그래픽을 표현할 때 매우 중요하다. 불행히도 그림자는 직접 렌더링 할 수 없고, 광원과 오브젝트들의 상호작용을 통해 만들어지는 매우 비싼 연산이다. 하지만 그림자의 특성 상 아주 구체적일 필요는 없다. 그렇기에 정확도를 조금 손해보더라도 최대한 성능을 끌어올리는 이런저런 최적화 기법도 존재한다.
Shadow | Shadow Map |
---|---|
먼저 그림자를 구현하는 방법에 대해 알아보자. 그림자를 구현하기 위해서는 앞서 G-Buffer에서 다룬 depth map과 새롭게 등장할 shadow map이 필요하다. Depth map이 카메라로부터 각 픽셀까지의 거리를 뜻한다면, shadow map은 광원으로부터 각 픽셀까지의 거리를 뜻한다. 이제 각 픽셀의 world 좌표와, depth 정보를 통해 픽셀과 광원 사이의 거리를 구할 수 있다. Depth map을 통해 구한 거리와 shadow map을 이용해 구한 거리가 다르면 그림자가 생겨야 할 지점인 것이다.
따라서 그림자를 구현하기 위해서 shadow map이 필요한데, shadow map이란 결국 main directional light(가장 빛이 센 directional light)을 카메라로 취급해 depth map을 하나 더 그리는 것이다. 따라서 프레임버퍼만한 버퍼가 추가적으로 필요하고 드로우콜도 거의 두배로 늘어난다. 그림자 연산이 비싼 이유이다.
그림자 연산을 효율적으로 하기 위해서는 mesh별로 존재하는 Cast Shadows 항목을 잘 설정해야 한다. 이 값은 shadow map에 해당 메시를 렌더링 할지말지 결정한다. 따라서 다른 오브젝트에 그림자를 만들 필요가 없는 경우 이 값을 꺼두고 드로우콜을 아껴야 한다. 만약 그림자를 만드는 메시가 아주 무거울 경우, 그림자만을 위한 깡통 메시를 만들고 shadows only 설정을 통해 대신 렌더링할 수 있다.
🐳 Shadow Distance
그림자 구현시 Shadow Distance 값을 조절하여 그림자의 품질을 높일 수 있다. Shadow distance는 카메마로부터 그림자가 그려지는 거리를 뜻한다. 따라서 값이 낮을수록 해당 오브젝트가 차지하는 픽셀의 갯수가 많아지고, 해상도가 높아지는 효과를 낼 수 있다. Shadow distance를 이용한 일반적인 최적화 방식은 다음과 같다. 우선 distance값을 낮게 설정해 카메라와 가까운, 중요한 오브젝트의 그림자 품질을 높인다. 멀리 떨어져 있지만, 그림자가 없으면 어색한 큰 오브젝트의 경우 그림자를 미리 구워둔(baked) 라이트맵을 사용하게 된다. 다만 라이트맵 방식은 static한 오브젝트들에게만 적용이 가능한데, dynamic한 오브젝트의 경우 Cascaded Shadow Maps방식을 이용한다.
🐳 Cascaded Shadow Maps
Camera View Frustum | Cascaded Shadow Maps |
---|---|
멀리 있는 오브젝트의 그림자를 표현하기 어려운 이유는 카메라의 원근법 때문이다. 그림자 퀄리티의 기준을 먼 오브젝트에 맞추게 되면 shadow map 버퍼의 사이즈가 그만큼 커져야 하는 메모리 이슈가 있고, 가까운 오브젝트는 비교적 적은 수의 픽셀로만 커버해야 하는 문제가 발생한다. Cascaded Shadow Map은 이러한 문제를 해결하기 위해 view frustum 영역을 몇 단계로 나누어 그림자를 구현하는 방식이다. 이렇게 하면 멀리 떨어진 오브젝트와 가까운 오브젝트 모두 적절한 해상도로 퀄리티를 유지하며 그림자를 구현할 수 있다. 하지만 단계마다 Shadow Map을 구현하는 방식이므로 N단계라면 xN으로 드로우콜 횟수와 필요한 메모리의 양이 늘어나게 된다.
🐳 Planar Shadow (평면 그림자)
그림자 구현을 최적화하는 방법 중 가장 단순하지만 효율적인 Planar Shadow(평면 그림자)기법이 존재한다. 엄밀히 말하면 shadow planer는 그림자가 아니라 하나의 mesh이다. 즉, 오브젝트의 그림자를 미리 렌더링한 뒤 오브젝트와 함께 움직여주는 것이다. 추가적인 버퍼나 드로우콜이 거의 발생하지 않으므로 매우 효율적인 방법이다. 다만 바닥이 평면이 아니라면 이질감이 느껴진다.
댓글 남기기