NPR Project Demo

07. Shadow Map, Caster, Recevier(1)_[NPR Project]

NyumMa 2026. 3. 27. 17:10

그림자(Shadow)

그림자

그림자는 이미지에서 물체의 위치, 공간감, 형태감, 그리고 빛의 방향에 대한 정보를

시각적으로 파악하는데 있어서 중요한 요소 중 하나이다.

위의 두 사진을 비교해 보았을 때,

 

왼쪽 사진은

그림자 정보 없이 오브젝트가 받는 직사광, 반사광 정보만을 갖고 있어 

사진이 평면적이고 단순해 보이는 느낌을 받는다. 

두 오브젝트 중 어느 오브젝트가 앞에 있고 뒤에 있는지, 

카메라가 오브젝트를 어떤 시점에서 바라보고 있는지, 

뒤에 있는 녹색 배경이 바닥인지 벽면인지 

등을 직관적으로 인지하기 어렵다.

 

반면, 오른쪽의 사진은 

오브젝트에 의해 가려지는 그림자와

투영되는 그림자 정보를 함께 나타냄으로써 

오브젝트의 직사광, 반사광과 더불어 오브젝트가 공간에 대해서 

어느 위치에 존재하는지, 어느 시점에서 바라보고 있는지, 

어느 오브젝트가 앞에 있고 뒤에 있는지, 

빛이 어느 방향에서 들어오고 있는지 등에 대한 정보를 자연스럽게 인지하고 파악할 수 있다.

 

즉, 그림자 정보는 3차원 3D 공간을 나타내고 인식하는데 있어서

중요한 인지적 요소 중 하나라고 할 수 있으며,

보다 사실적인 묘사를 가능하도록 하는 요소이다.


 

그림자의 생성 원리, 종류

가상의 3차원 공간에서 그림자를 구현하기 위해서는

먼저 현실에서 그림자가 어떤 조건에서 생기며 어떤 기준으로 분류되는지를 정리할 필요가 있다.


그림자의 생성 원리

그림자는 원초적으로 어두움 그 자체가 아니라,

광원이 존재하는 상황에서 빛이 어떤 지점까지 도달하지 못하는 결과, 즉 가시성의 차단으로 이해할 수 있다.

즉, 빛을 받는 영역과 받지 못하는 영역이 동시에 존재할 때, 그 차이와 대비가 우리가 말하는 그림자로 인식된다.

 

예를 들어 광원이 전혀 없는 어두운 방을 떠올리면,

방 전체는 어둡지만 우리는 그것을 거대한 그림자라고 부르기보다는 단순히 조명이 없는 상태로 받아들인다.

이는 곧 그림자가 광원과의 대비가 있을 때 의미가 생기는 현상임을 보여준다.

 

그림을 그릴 때는 보통 물체의 원색을 기준으로 어두운 색을 쌓아 묘사하는 방식이 익숙하지만,

물리적인 관점에서 보면 순서는 반대에 가깝다.

즉 밝은 상태에 어둠이 칠해진다기보다, 기본적으로 어두운 환경에서 광원에 의해 밝은 영역이 생기고,

그 과정에서 빛이 닿지 못한 영역이 상대적으로 어둡게 남는 것이 그림자라고 볼 수 있다.

 

정리하면, 그림자가 형성되기 위해서는 광원이 필요하며,

그 빛이 오브젝트에 의해 가려지거나(occlusion) 공간적으로 빛이 닿지 못하는 위치가 생기면서

해당 영역이 주변의 밝은 영역에 비해 상대적으로 어둡게 관측되는 현상을 그림자라고 한다.


그림자의 종류

우리는 이미 Ch.2 Diffuse Lighting과 Ch.4 Specular에서

Directional Light에 의해 형성되는

직접광(Direct Light)의 확산광(Diffuse)과 반사광(Specular) 처리를 다루었다.

 

Shadow 챕터에서 다루고자 하는 것은

장면에서 보이는 그림자 요소 중에서도

 

형태 그림자(Cast Shadow),

수신 그림자(Receive Shadow),

그리고 본그림자와 반그림자(Umbra, Penumbra)이다.

 

a. 형태 그림자 (Cast Shadow)

오브젝트가 광원을 가리면서, 그 결과가 다른 표면(바닥이나 벽 등) 위에

투영되어 나타나는 그림자이다.

그림자를 만드는 요인인 caster와, caster에 의해 광원으로부터

빛을 받지 못하여 다른 오브젝트에 그림자가 생성되는 receiver가 형태(cast) 그림자 생성의 구성 성분이다.

 

b. 수신 그림자 (Receive Shadow)

오브젝트의 한 부분이 다른 오브젝트의 전체 또는 일부분 부분을 가려서,

해당 오브젝트 표면 위에서 빛을 받지 못하는 영역이 생기는 현상이다.

예를 들어 머리카락이 얼굴을 가리는 그림자나,

모자가 얼굴을 가리는 그림자,

팔이 몸통에 의해 형성되는 음영 등이 있다.

 

c. 본그림자, 반그림자 (Umbra, Penumbra)

그림자는 완전히 어두운 영역만 있는 것이 아니라

광원의 형태(직사광, 점광원, 면광원 등)와 차폐 정도에 따라 두 영역으로 나뉜다.

 

Umbra는 광원이 오브젝트에 의해 완전히 가려져 빛이 도달하지 않는 영역이고,

Penumbra는 광원이 부분적으로만 가려져 밝기가 점진적으로 전이되는 영역이다.

 

일반적으로 직사광(Directional Light)에 대해서는

Umbra만 형성된다고 단순화해서 생각할 수 있지만,

태양을 제외한 대부분의 광원은

점광원(Spot Light) 또는 면광원(Area Light)으로 해석할 수 있으므로

반그림자(Penumbra) 영역이 함께 형성된다.


CG에서 그림자 생성 방법

지금까지는 그림자가 형성되는 원리와, 그림자를 구성하는 요소들에 대해 간단히 정리해 보았다.

 

이제 본론으로 들어가서, 가상의 3차원 컴퓨터 그래픽스 공간에서는

그림자를 어떤 방식으로 생성하는지 정리해보려고 한다.

 

컴퓨터 그래픽스에서 오브젝트에 그림자를 표현하기 위해서는

오래전부터 여러 가지 기법과 트릭들이 사용되어 왔다.

하드웨어 성능이 제한적이던 시기에는 최대한 저렴한 비용으로

그림자 느낌을 만들어내는 방식이 주로 사용되었고,

시간이 지나면서 광원 방향과 오브젝트의 형상, 환경 정보를 잘 반영할 수 있는 방식들이 개선되어 사용되기 시작했다.

 

해당 섹션에서는 먼저 비교적 단순한 전통적 그림자 표현 방식들을 살펴보고,

이후 현대 실시간 그래픽스에서 널리 사용되는 Shadow Map 방식으로 넘어가 보려고 한다.


1. Fake Shadow

그림자를 구현하기 위한 가장 간단한 형태는

오브젝트 아래 지면에 그림자스러운 메쉬를 추가로 덧그려서 나타내는 것이다.

이러한 방법을 Fake Shadow라고 한다.

 

Fake Shadow는 실제 광원, 오브젝트와 환경의 가시성을 정확하게 계산하지 않고,

오브젝트 아래 또는 표면에 그림자처럼 보이는 메쉬나 텍스처를

추가로 렌더링해서 그림자 느낌을 만드는 기법이다.

 

Fake Shadow는 특정 형태의 그림자 메쉬를 오브젝트 아래에 한 번 그리는 동작만을 수행하므로

그림자 계산을 매우 싸게 처리하면서 캐릭터의 공간감과 입체감을 확보할 수 있다.

이는 과거 하드웨어 성능 및 제약이 따르는 저사양 콘솔, 기기들에서 게임을 구동하기 위해

리소스를 최대한 줄이면서 설득력 있는 그래픽을 구현하는데 유리했다.

 

하지만 Fake Shadow는 Cast Shadow로서

오브젝트의 정확한 실루엣이나 광원 방향을 표현하기 어렵다.

오브젝트의 요철이 많아지거나 형태가 복잡할수록 shadow mesh가

해당 오브젝트의 그림자를 대변하는데 있어서 부자연스러워진다.

또한 그 자체로 Self Shadow, Occlusion과 같은 가시성 차단에 대한 정보는 제공하지 못한다.

 

따라서,  Fake Shadow는 사실적인 그림자 계산이라기보다는,

최근에는 가시성과 접지감을 위한 연출용(Stylized) 그림자에 가깝게 작용한다.


2. Planar Shadow

Planar Shadow는 오브젝트의 형상을 특정 평면 위로 투영(Projection)하여

그림자처럼 보이게 만드는 방식이다.

 

즉, 광원 방향을 기준으로 오브젝트의 기하를 바닥이나 벽 같은 평면에 투영해서,

그 결과를 그림자처럼 렌더링하는 구조라고 볼 수 있다.

 

이 방식은 Fake Shadow보다 훨씬 그럴듯한 결과를 만들 수 있다.

오브젝트의 실루엣이 어느 정도 반영되고, 광원의 방향에 따라

그림자가 늘어나거나 기울어지는 표현도 가능하기 때문이다.

따라서 그림자가 물체의 형태를 따라 바닥에 맺힌다는 직관적인 개념을 설명할 때는

Planar Shadow가 좋은 예시가 된다.

 

하지만 Planar Shadow 역시 한계가 분명하다.

기본적으로 그림자를 받아낼 평면이 명확해야 하므로,

복잡한 지형이나 울퉁불퉁한 표면, 다층 구조의 환경에서는 자연스럽게 적용하기 어렵다.

또한 오브젝트가 다른 오브젝트 위에 드리우는 그림자,

혹은 자기 자신에 의해 생기는 Self Shadow 같은 복잡한 가시성 관계를 정확하게 처리하기 힘들다.

 

즉, Planar Shadow는 그림자를 투영해서 만든다는 직관을 이해하기에는 좋은 방식이지만,

현대 실시간 그래픽스에서 요구하는

복잡한 환경과 정확한 가시성 표현을 모두 해결하기에는 한계가 있다.


3. Shadow Map

앞서 본 Fake Shadow와 Planar Shadow는 각각 장점이 있으나,

광원 방향, 오브젝트의 실제 형상, 그리고 주변 환경까지 반영한 정교한 그림자를 만들기에는 한계가 있다.

 

이러한 한계를 해결하기 위해 실시간 그래픽스(RTR)에서는

일반적으로 Shadow Map 방식이 널리 사용된다.

 

해당 방식은 1978년 Lance Williams가 고안한 방식으로,

Shadow Map은 planar shadow처럼

오브젝트의 그림자 모양을 바닥(Plane)에 직접 투영해서 그린다는 방식과는 조금 다르다.

Shadow Map의 핵심 아이디어는 현재 그리고자 하는 지점이

광원 입장에서 보이는지,

혹은 다른 표면에 의해 가려져 있는지를 판정하여

그림자 여부를 결정하는 방식이다.

 

그림자를 처음 생각하면 선형대수에서 배우는 projection처럼

형상을 평면 위에 떨어뜨리는 이미지를 떠올리기 쉽지만,

Shadow Map의 핵심은 그림자 형상을 직접 그린다기보다는

광원 시점에서의 가시성(visibility)을 기준으로 빛의 도달 여부를 판정하는 데 있다.

 

이때 오브젝트에 의해 가려져 있는지를 판단하기 위해 거리 기반의 비교 방법을 사용하게 되며,

이를 위해 광원 시점에서 장면을 렌더링하고 각 방향에서 가장 먼저 보이는 표면의 깊이 값을 깊이 텍스처에 저장한다.

이 광원 기반 깊이 텍스처를 Shadow Map이라고 한다.

 

그림자를 특정 Frame에서 처리하기 위해서는 카메라 시점에서 한 번만 렌더링되는 것이 아니라

광원 시점에서 한 번 더 렌더링된다.

다른 말로는 Shadow Map 방식의 그림자 생성은 Render Pass가 추가되는 작업이라고 이야기할 수 있다.

실제로 RenderDoc이나 FrameDebugger 등에서

Shadow Map이 광원을 기준으로 생성되는 모습을 확인해 볼 수 있다.

참고로 위의 사진에서는 Shadow Map이 여러개 생성되어 있는 모습을 볼 수 있는데,

이는 Cascade Level에 의한 결과이며

해당 방식은 Shadow Map의 최적화를 위한 처리로 추후에 다루게 된다.


Shadow Map의 처리 과정

Shadow Map 방식에서는 윗 섹션에서 말한 것처럼 장면을 카메라 시점이 아니라

광원 시점에서 한 번 렌더링한다.

 

이때 렌더링 되어 텍스쳐에(RT) 저장되는 것은 색(Color) 정보가 아니라,

광원으로부터 가장 먼저 보이는 표면까지의 깊이(Depth, Z value) 정보이다.

이 깊이 텍스처를 Shadow Map이라고 한다.

 

카메라 시점에서 최종 장면을 렌더링할 때는
현재 카메라 시점을 기준으로 그리고 있는 픽셀의 위치에 대해서

이를 광원 기준 좌표계(light basis) 로 다시 변환한 뒤,
해당 위치에 대응하는 Shadow Map의 깊이 값과 비교하여 그림자 영역을 판정한다.

 

만약 현재 픽셀의 깊이가 Shadow Map에 기록된 깊이보다 더 뒤에 있다면,
그 픽셀은 광원 입장에서 이미 다른 표면에 의해 가려진 것으로 판단할 수 있을 것이고
이 경우 해당 지점은 그림자 영역으로 처리된다.

 

반대로 깊이 값이 Shadow Map에 기록된 표면과 일치하거나 더 앞에 있다면,
그 지점은 광원에 직접 노출된 영역으로 간주된다.

 

이러한 처리 방식에 대한 알고리즘을 수식으로 나타내면 다음과 같다.

여기서 0은 그림자를 받는 부분이고, 1은 그림자를 받지 않는 lit의 상태로써 쓰인 식이다.

 

Z_map은 Shadow Map의 픽셀 값을 의미하며

Z_Current는 카메라에서 바라보고 있는 위치의 픽셀 정보를

Light Space에서 보았을때의 z값을 의미한다.

 

위 수식을 다시한번 이해해 본다면,

Z_current가 Z_map 보다 값이 크다는 의미는(광원 기저 기준으로 더 멀다면)

Z_current가 현재 ShadowMap의 샘플링 되어야 하는 위치보다 더 깊다는 뜻이며

따라서 가려지는 영역이 발생한다는 판정으로 그림자가 형성되도록 한다.

 

한편, Z_current가 Z_map보다 작은 상황이려면(광원 기저 기준으로 가깝다면)

생성된 Shadow Map이 Z_Current를 가리고 있다는 뜻이며

즉, 그림자를 받지 않는 lit의 상태라고 의미할 수 있다.

 

위와 같은 방식으로 모든 fragment에 대하여

그림자가 형성되기 위한 영역을 판정을 하게 된다.

 

실제 게임, 혹은 상용 3D엔진에서는

해당 영역이 그림자 영역이라는 것이 계산된 정보를

통해 이 결과가 shadow factor 또는 attenuation 형태로 조명 계산에 반영된다.

 

한편, 해당 방식은 실시간 렌더링에서 매우 실용적이지만,

결국은 텍스처(RT) 해상도와 깊이 값의 부동소수점 정밀도에 의존하기 때문에

Shadow Acne, Peter Panning , Aliasing 등과 같은 여러 아티팩트가 발생할 수 있다.

 

또한, 쉐이더에서 render pass를 추가하는 작업이자 RT에 대한 메모리를 차지하여

장면 복잡도와 광원 수에 따라서

렌더링 비용과 메모리 사용량, drawcall 등을 증가시킬 수 있다.

 

즉, Shadow Map은 그림자 처리에 있어서

강력하고 일반적으로 사용되는 방법이지만,

동시에 depth texture 기반의 이산적(digital) 표현이라는 한계도 함께 가진다.

 

Shadow Map에서는 위의 현상들을 처리하기 위해서

다양한 기법들을 처리하게 되는데

이에 대해서 다음에 설명해 본다.


Shadow Map Artifact(1) - Shadow Acne

먼저, Shadow Map의 아티팩트에 있어서

첫번째로 다루어야 할 부분은 Shadow Acne이라는 현상이다.

Acne은 여드름이라는 뜻인데,

그림자 이외의 영역에 줄무늬와 같은 현상이 생기는 것을 말한다.

위의 사진을 보면 표면에 그림자가 형성되지 말아야 할 곳에

줄무늬 모습의 그림자가 형성되어 있는 것을 확인해 볼 수 있다.

 

이러한 현상의 본질적인 이유는 Shadow Map 자체가

연속적인 표면을 유한한 해상도의 depth texel로 저장하므로,

실제 표면의 깊이와 shadow map에 기록된 깊이가 완전히 일치하지 않을 수 있기 때문이다.

 

이 미세한 오차 때문에 원래는 빛을 받아야 할 표면 일부가

자기 자신에 의해 가려진 것처럼 판정되며,

그 결과 줄무늬 같은 Shadow Acne가 발생하게 되는 것이다.

 

z_current 값은 카메라에서의 depth 값을 광원시점에서 바라본 연속적인 부동소수 값으로

상대적으로 끊김 없는 신호이지만

Shadow Map의 z_map은 해상도에 대한 텍셀 크기(Texel Size)와 영역에 따라

값이 이산적(discrete)으로 끊겨 있기 때문에,

같은 지점에 대해서 depth compare를 할 시에 판정이 달라져서

이와 같은 줄무늬가 발생하게 되는 것이다.

위의 그림에서,

자주색은 Shadow Map의 이산적인 표본(Texel) 값을 의미하며

검은색은 광원에서 바라본 위치의 Z 값을 의미한다.

 

정상적인 판정으로는 만약 z_current 와 z_map이

서로 같은 값을 의미하고 있다고 기대한다면

z_current > z_map은 false 이므로(같은 값은 true로 처리하지 않기에)

해당 영역은 그림자가 생성되어서는 안된다.

 

그림상으로는 현재 위의 그림에서

자주색과 검은색의 교점에 대한 영역을 의미한다.

하지만, Shadow Map은 discrete 하기 때문에

같은 지점을 비교해도

위의 그림처럼 파란색 영역과 빨간색 영역이 공존하게 된다.

 

파란색 영역은 그림자 판정이 되지 않는 영역이지만

빨간색 영역은 그림자 판정이 되는 영역이므로

빨간색 부위에 그림자 판정이 되어 줄무니와 같은 모습이 생겨난다.

이를 Shadow Acne(그림자 여드름)이라고 하는 것이다.


Depth Bias

이러한 아티팩트를 해결하기 위한 방법으로는 해당 비교 항에

상수 항의 Bias를 추가하는 방법이 가장 간단한 해결책이다.

이를 일반적으로 Depth Bias라고 부른다.

 

생각해 본다면 Shadow Map의 이산적인 범위 처리를 무시할 만한 값으로

Bias를 주어 Margin을 둔다면 이러한 줄무늬 현상이 해결될 것을 기대해 볼 수 있다.

위의 그림과 같이, Shadow Map에서는

z 값이 texel 단위로 이산적으로 저장되기 때문에
현재 카메라에서 그리고 있는 픽셀의 깊이 비교 과정에 작은 bias를 추가하여
Shadow가 잘못 형성되지 않도록 함으로써

Shadow Acne 문제를 해결해 볼 수 있다.

 

이는 깊이 비교 시 아주 작은 margin을 두어,

같은 표면에서 발생하는 미세한 오차를 곧바로 그림자로 판정하지 않도록 한다.

 

위의 식과 같이 Shadow Map에 기록된 깊이 값에 Bias를 추가하여

약간 더 깊게 샘플링 함으로써 두 값이 완전히 같아지지 않도록 처리하도록 해

문제를 해결한다고 해석해 볼 수 있을 것이다.

실제로 상용 게임 엔진에는 그림자가 형성에 있어서

depth bias를 얼마나 줄 지에 대한 옵션이 존재한다.

 

해당 옵션에서 Bias를 조절하여 Acne이 발생하지 않을 정도로 처리하여

해당 아티팩트를 해결할 수 있다.


Peter Panning

한편, 문제를 해결하기 위해서 Bias를 많이 주게 되면

의도한 위치에서 그림자가 형성되지 않고

약간 벗어나서 밀린 위치에 그림자가 형성될 수 있는데

이러한 현상을 Peter Panning 이라고 한다.

 

Peter Panning은 디즈니 애니메이션 피터팬에서 따온 표현으로써

작품에서 피터팬의 그림자가 살아 움직이듯이

마치 자신의 그림자와 다르게 행동하는 모습을 보인다.

자기 그림자가 몸에서 떨어져 따로 노는 모습에 기인하여

그림자가 바닥이나 접촉면에서 떨어져 떠 보이는 모습을

어원 그대로 peter panning 이라는 말이 붙여졌다.

위의 사진에서 관찰해 보면

각 사진의 왼쪽은 적당한 bias를 설정한 것이고, 오른쪽은 필요 이상의 bias를 설정한 것이다.

 

왼쪽 사진과 같이 bias를 크게 줌으로써

정육면체 큐브의 아랫 부분에 그림자가 형성되는 위치에

그림자가 형성되지 않아 이질감이 느껴지는 것을 확인해 볼 수 있다.

 

따라서 Acne을 해결하기 위해서 Bias를 필요 이상으로 주게 된다면 오히려

역효과가 발생할 수 있으므로 여러 상황에 따라

관찰하여 적당한 값을 주는 것이 바람직하다.


Normal Bias

한편, 위와 같은 상수항 Bias는

Shadow Acne을 해결하기 위한 간단한 해결책이지만

해당 방식 또한 명백한 한계점이 있다.

만약 표면이 광원 방향과 서로 일치하지 않을때는

단순히 상수 bias 만으로는 해결하기 어려울 수 있다.

 

예를 들어, 경사면(slope)에서 상수 bias를 사용하기 위해서는

표면이 광원에서 멀어질수록, 경사가 깊어질수록 bias 값이 커지게 되는데

따라서 경사면에 따라서 부여해야 하는 bias 가 달라질 수 있으므로 일관성을 해칠 수 있다.

 

이러한 문제를 해결하기 위해 GDC 2011에서 Daniel Holbert는

광원에 대한 Recevier에 대하여 단순 깊이에 대한 상수 값이 아닌

노말 방향으로 Bias를 부여하여

문제를 해결하고자 하였고 해당 방식을 통해

Shadow Acne과 Peter Panning 모두

문제를 효과적으로 해결할 수 있다는 가능성을 제시하였다.

다만 Normal Bias 역시 모든 상황에서 완전한 해결책은 아니며,

해당 bias 또한 과도하게 적용할 경우 접촉부가 붕 뜨거나

세부 그림자가 약해지는 문제가 생길 수 있다.

 

즉, Shadow Map의 아티팩트를 완화하는 것은

하나의 값을 통해서 간단하게 해결되는 문제는 아니며

장면 특성과 광원 조건에 따라 여러 시행착오와 파라메터 조절, 보정 기법 등을

적절히 함께 조절해야 하는 문제이므로 여러 시나리오에서 관찰을 위한

기준을 마련해보는 것이 중요하다.


Shadow Map Artifact(2) - Resolution, Aliasing

두번째로 다루어 볼 Shadow Map의 아티팩트는

Shadow Map의 해상도와 샘플링에 의해 발생하는 aliasing 문제이다.

 

앞선 Shadow Acne이 깊이 비교 과정의 오차에서 비롯된 문제였다면,

이번에는 Shadow Map 자체가 텍셀 기반의 이산적인 자료라는 점에서 오는 한계를 다룬다.

위의 이미지를 보면 그림자가 형성된 경계 부근에서

계단 현상(aliasing)이 나타나는 모습을 확인할 수 있다.

 

이는 그림자 경계가 본래 연속적인 형태를 가져야 함에도 불구하고,

Shadow Map에서는 이를 텍셀 단위로 저장하고 판정하기 때문에 발생한다.

 

특히, Shadow Map은 광원 시점에서 생성되지만,

최종적으로는 카메라 시점의 화면 위에 재구성되어 사용된다.

이 과정에서 하나의 shadow texel이

카메라 기준으로 넓은 영역을 담당하게 되면

그림자 경계는 거칠고 계단진 형태로 나타날 수 있다.

 

즉, 해상도가 낮은 Shadow Map으로 넓은 영역을 커버하려고 할수록

각 shadow texel이 차지하는 월드 공간 범위가 커지게 되고,

결국 그림자 경계의 세밀한 형상을 표현하지 못하게 되는 것이다.

 

이러한 문제를 해결하는 단순한 방법은

Shadow Map의 해상도를 높이는 것을 생각해 볼 수 있다.

실제로 shadow map resolution을 높이게 되면

같은 장면 범위를 더 많은 texel로 나누어 저장할 수 있으므로

그림자 경계의 계단 현상을 어느 정도 완화할 수 있다.

 

하지만 이 방식은 해상도를 높일수록

메모리 사용량과 렌더링 비용이 함께 증가하므로

시스템 제약이 있고 항상 근본적인 해결책이 되지는 못한다.

 

또한, 카메라와 광원의 위치 관계에 따라서는

해상도를 높였음에도 aliasing이 충분히 완화되지 않는 경우도 존재한다.

예를 들어 카메라 근처의 좁은 영역과 먼 거리의 넓은 영역을

하나의 Shadow Map으로 동시에 처리하려고 하면,

근거리에서 필요한 shadow texel density를 확보하기 어렵다.

 

즉, 단순한 해상도 증가만으로는

카메라 근처에서 민감하게 보이는 그림자 품질까지 충분히 확보하기 어려운 상황이 생길 수 있다.

이러한 문제는 결국 Shadow Map의 한정된 해상도를

어떤 거리 범위에 어떻게 분배할 것인가 하는 문제로 이어지며,

이를 해결하기 위한 방법으로

Shadow DistanceCascade Shadow Map(CSM)과 같은 방식이 사용된다.


Shadow Distance

Directional Light처럼 넓은 영역에 그림자를 투영해야 하는 경우,

하나의 Shadow Map으로 전체 장면을 커버하면

가까운 영역에서 필요한 충분한 texel 밀도를 확보하기 어렵다.

 

이를 해결하기 위한 가장 단순한 방법 중 하나는

카메라로부터 일정 거리까지만 그림자를 렌더링하는 것이다.

상용 게임 엔진에서도 Shadow Distance와 같은 항목을 통해

카메라로부터 어느 거리까지 그림자를 생성할 것인지 조절할 수 있다.

 

위 방식은 그림자를 무한히 먼 영역까지 모두 그리려 하지 않고,

카메라 view frustrum의 일정 범위에 대해서만 Shadow Map을 생성하도록 제한하는 것이다.

이렇게 하면 Shadow Map이 담당해야 하는 범위가 줄어들게 되므로,

같은 해상도에서도 더 높은 shadow texel density를 확보할 수 있다.

결국 제한된 shadow map의 해상도 안에서

가까운 거리의 그림자 품질을 더 효과적으로 높일 수 있게 된다.

하지만 해당 방식은 텍셀 밀도를 확보하기 위해  distance를 너무 줄여버리면

일정 거리 이상이 되었을 때 그림자가 갑자기 사라진다는 단점이 있다.

 

즉, Shadow Distance는 단순하고 효과적인 최적화 방법이지만,

거리 제한에 의해 시각적인 이질감이 나타날 수 있다는 한계가 있다.


Matrix Warping

Shadow Map의 aliasing 문제는

단순히 shadow texel 수의 부족만으로 설명되지 않는다.

카메라의 시점과 광원 시점이 서로 다르기 때문에,

광원 시점에서는 균일하게 배치된 shadow texel이

카메라 화면 위에서는 매우 불균일한 크기로 투영될 수 있다.

 

특히 카메라에 가까운 영역일수록 화면 위에서 차지하는 비중이 크기 때문에,

같은 shadow map의 texel이라도 카메라 시점에서는 근거리가 더 거칠고 크게 보이게 된다.

이러한 현상을 보통 perspective aliasing이라고 부른다.

 

즉, Shadow Map의 샘플링 밀도가 광원 시점에서는 일정하더라도

카메라 시점에서는 근거리와 원거리의 중요도가 달라지므로,

실제로는 카메라 근처에서 품질 문제가 더욱 두드러지게 나타나는 것이다.

이를 해결하기 위한 접근 중 하나로

광원 시점의 투영 공간을 원근투영(Orthographic Projection)에서 비선형적으로 변형하여

카메라 근처에 더 많은 shadow texel이 배정되도록 만드는 방법이 제안되기도 하였다.

 

이러한 방식을 Matrix Warping 라고 하며 대표적으로

 

PSM(Perspective Shadow Map)

TSM(Trapezoidal Shadow Map)

LiSPSM(Light Space Perspective Shadow Map)

 

등의 계열이 존재한다.

 

다만 해당 방식은

구현 복잡도와 안정성, 장면 일반성 등의 문제로 인해

현대 상용 엔진에서는 보다 단순하고 안정적인 CSM 방식이 널리 사용되는 편이다.

 

따라서 본 글에서는 Matrix Warping 계열의 방식은

위와 같은 간단한 맥락 정도만 언급하고,

보다 일반적으로 사용되는 Cascade Shadow Map(CSM)을 중심으로 설명을 한다.


Cascade Shadow Map

Directional Light처럼 넓은 영역에 그림자를 투영해야 하는 경우,

하나의 Shadow Map으로 전체 장면을 커버하면

가까운 영역에서 필요한 충분한 texel 밀도를 확보하기 어렵다.

 

이를 해결하기 위해 앞서 설명한 Shadow Distance를 두어

Frustrum의 한정된 거리에 대해서만 그림자가 렌더링 되도록 할 수 있었다.

 

해당 방식은 그림자가 커버하는 영역을 줄임으로써

Texel Density를 높혀 제한단 shadow map 해상도에서

그림자 품질을 효과적으로 높힐 수 있었으나,

성능 제약으로 distance를 줄여버리면 일정 거리 이상이 되었을 때

그림자가 갑자가 사라진다는 단점이 있었다.

 

이러한 문제를 추가로 해결하기 위한 대표적인 방법이

Cascade Shadow Map(CSM)이다.

해당 방식은 카메라의 view frustum을 여러 구간으로 나눈 뒤,

각 구간마다 여러 Shadow Map 또는 Shadow Atlas 상의 별도 영역을 할당한다.

이 방식을 사용하는 이유는 앞서 말했듯이

하나의 shadow map으로 카메라 근거리와 원거리를 모두 동시에 커버하려고 하면
가까운 영역에서 shadow texel 밀도가 부족해져 그림자 경계가 쉽게 깨지거나 aliasing이 두드러지기 때문에

공간 분할을 통한 texel density를 조절하자는 아이디어에서 기인한다.

 

한편, shadow map을 4개의 텍스쳐로 나누는 것이 아닌

하나의 텍스쳐 atlas로 관리함으로써 주는 이점은

서로 다른 그림자 맵을 하나의 텍스쳐에 배치하여

resource 관리와 샘플링 구성을 단순하게 하고,

메모리 활용 측면에서도 유리할 수 있기 때문이다.

 

CSM을 설정함으로써 카메라에 가까운 중요한 영역에는

더 높은 밀도의 shadow texel을 배치하고

상대적으로 먼 원거리에서는 그림자의 품질이

Frame의 전체 품질에 큰 영향을 주지 않으므로

상대적으로 낮은 밀도의 shadow texel을 배치 함으로써

품질 저하를 효과적으로 줄이게 할 수 있다.

Frame Debugger에서도 cascade count에 따라

그려지는 shadow map의 개수가 늘어나는 모습과

각 cascade가 담당하는 월드 공간 범위와,

그에 따라 달라지는 shadow texel 밀도의 차이를 확인할 수 있다.

 

많은 상용 엔진에서는 보통 2~4개 수준의 cascade를 설정할 수 있으며,

필요에 따라 더 많은 구간으로 확장한 커스텀 파이프라인을 구성하기도 한다.

실제로 목적에 따라서 렌더링하고자 하는 장면의 상황에 맞추어

5개, 6개 등등까지 렌더링 파이프라인을 변형하여

Cascade Level을 조절할 수 있다.

 

실제로 MiHoYo가 2020 Unite Seoul에서 소개한 PS4용 원신 렌더링 파이프라인에서는

총 8개의 cascade 구간을 사용했다고 한다.

이 중 4개는 근거리 영역, 나머지 4개는 원거리 영역을 담당하도록 나누어

그림자 품질과 비용 사이의 균형을 조절한 것으로 보인다.

(해당 내용에는 메모리 병목을 해결하기 위한 여러 trick들이 소개되어 있지만 본 글의 목적과 약간 벗어나므로 생략하도록 한다.)

 

해당 방식은 view frustrum 내부를 근거리와 원거리로 나누어

여러개의 shadow map으로 관리해 간단한 방식으로 그림자 품질을 적절히 조절하면서

높은 품질을 얻을 수 있다는 장점을 갖는다.

 

한편, 각 구간의 거리에 대한 비율을 어느정도로 설정해야 할지에 대해서

궁금증을 가질 수도 있을 것이다.

 

당연하게도, 각 구간의 경계를 어떤 비율로 배치할 것인지에 따라서도 품질이 크게 달라질 수 있다.

하나의 cascade가 근거리부터 원거리까지 너무 넓은 범위를 담당하게 되면,

가까운 거리에서의 shadow texel 밀도가 낮아져

그림자 경계가 쉽게 깨지거나 aliasing이 두드러질 수 있다.

 

따라서 근거리에서는 더 촘촘하게,

원거리로 갈수록 점진적으로 더 넓은 범위를 담당하도록

split을 배치하는 것이 적합할 것이다.


Linear Split

가장 첫번째로 생각 해 볼 수 있는 방법은

Cascade 간격을 선형적(linear)으로 나누는 것이다.

해당 방식은 간단하지만 카메라의 거리에 따른 텍셀 밀도를 반영하지 못하기 때문에

CSM을 활용하는 이점을 크게 얻지 못하게 된다.

 

따라서 Cascade 처리의 범위는 근거리에서 촘촘하고

원거리로 갈수록 Spacing이 커지는 점진적으로 증가하는 형태가

CSM을 가장 적합하게 활용할 수 있는 방안이 될 수 있다.

추가로, 당연하게도 왼쪽 그림과 같이

카메라와 가까운 영역에서 shadow map이 커버하는 범위가 커진다면

텍셀 밀도가 낮아져서 위 사진과 같은 앨리어싱 아티팩트가 쉽게 발생하므로

오른쪽과 같은 모양을 유지하는데의 정당성을 다시 한 번 생각해 볼 수 있을 것이다.


Logarithmic Split

그렇다면 점진적으로 영역을 증가하게 보이도록 하기 위한

cascade level을 조절하는 기준은 무엇일까?

 

이를 생각하기 위한 가장 쉬운 방법은

각 구간과 다음 구간에 대해 같은 비율로써 증가를 시켜 처리를 해 본다는 생각을 할 수 있다.

 

view frustrum에서

커버할 수 있는 가장 가까운 거리는 근시평면의 n이고

커버할 수 있는 가장 먼 거리는 원시 평면의 f이다.

 

이 view frustrum 범위의 n~f사이에 공간을 분할하는데 있어서

같은 비율로써의 증가분을 어떻게 설정해야 할지 생각을 해 본다면

 

비율 r에 대하여

n 다음의 증가분은 nr,

nr다음의 증가분은 nr^2이고

최종적으로 nr^c (여기서 c는 cascade level)의 결과가

원시평면의 값인 f이 되어야 한다는 것을 가정했을때,

의 관계식을 얻을 수 있다.

 

이러한 방식을 로그 분할(logarithmic split)이라고 한다.

지수 관계처럼 보이지만 해당 방식을 로그 분할 방식이라고 하는 이유는

공간 분할에 대해서 같은 비율의 증가분으로 확장하게 되는데

에 대하여

현재의 단계와 다음 단계의 관계가 같은 비율을 유지하고

이를 로그를 취했을 때

이와 같이 등간격으로 표현이 되기 때문이다.

 

즉, 원래 거리 공간에서는 지수적으로 증가하는 형태를 보이지만

로그 공간에서는 균등한 간격으로 분할되는 셈이므로

이와 같이 부른다. 


Practical Split

반면, 위의 로그 분할 방식도 문제점이 있는데

해당 방식을 그대로 사용한다면

cascade level에 따라서

카메라의 근거리에 공간을 너무 촘촘하게 만들 수 있다는 점이다.

 

해당 방식은 근거리 품질에는 좋을 수 있으나,

다르게 말하면 원거리 cascade에 너무 적은 자원이 배정될 수 있다는 단점이 있다.

 

카메라에서 항상 근거리의 오브젝트만이 중요한 대상이 될 수는 없다.

예를 들어, DoF(Depth of Field)를 나타내는데 있어서

중간 위치의 피사체를 포커싱 해야 하는 상황이 있을수도 있기에

로그 분할 방식을 취하는 것은 납득할 수 있으나,

무조건적으로 해당 방식이 옳다고는 말할 수 없을 것이다.

 

마지막으로 view frustrum의 near plane과 far plane의 거리에 의존하게 되기 때문에

만약 near plane의 값이 매우 작거나 far plane의 값이 매우 크다면

비율이 상당히 높게 측정될 수 있다는 문제가 발생한다.

 

따라서, 이러한 문제를 해결하기 위해

그래픽스 학자들은 로그 분할 방식과 선형 분할 방식을 적절히 혼합한

practical split이라는 방식을 사용하게 된다.

 

위의 식과 같이 로그 분할 방식과 선형 분할 방식의 선형 결합으로

적절한 blending을 통해 값을 조절하는 방식이다.


그래서 어떤 분할 방법을 사용하는가?

사실 이렇게 거창하게 설명해도

게임엔진에서는 이를 자동으로 조절하게끔 하지 않으며 대부분 사용자에게

수동적으로 조절하도록 대신 역할을 위임하는 경우가 많다.

 

게임 엔진이 cascade split이나 shadow distance를 사용자에게 노출하는 이유는

그냥 이 값들에 대해서 정해져 있는 적절한 보편적인 정답이 없기 때문이다.

 

카메라 방식, 월드 스케일, 장면 구성, 요구되는 시각적 우선순위가

씬이나 프로젝트마다 다르기 때문에,

가장 적절한 shadow 분배 역시 상황에 따라 달라진다.

 

결국 CSM은 수학적으로 계산되는 시스템이면서도

동시에 아티스트와 테크니컬 아티스트가 시각적 결과를 보며

조정해야 하는 파라메터로 보는 것이 적절하다.

 

linear split, logarithmic split, practical split은 적어도

이 파라메터를 적절하게 조절하기 위한 기준이 될 수 있다는데에 의의가 있다고 생각해 볼 수 있다.


CSM의 한계

하지만 렌더링 파이프라인에서 cascade shadow map의 수가 많아질수록,

shadow map을 저장하기 위한 메모리 사용량과 렌더링 비용이 함께 증가하게 된다.

 

또한 광원 시점에서 여러 구간을 별도로 처리해야 하므로,

그리고자 하는 장면에 따라서 추가적인 drawcall등이 발생하게 된다.

 

더불어, CSM은 view frustum을 이산적인 구간으로 나누어

단계적으로 shadow map을 적용하는 방식이기 때문에,

cascade 경계 부근에서는 서로 다른 shadow map 사이의 전이를

얼마나 자연스럽게 처리할 것인지가 중요한 문제가 된다.

 

또한, 카메라 이동에 따라

경계가 눈에 띄거나 shimmering이 발생할 수 있으므로,

split 설계뿐 아니라 cascade 안정화(stabilization)와 transition 처리 또한

함께 고려해야 하는 까다로움이 어느정도 존재한다.


Shadow Filters

cascade split을 아무리 잘 조절하더라도, shadow map은

결국 유한한 해상도를 가지는 depth texture이기 때문에

그림자 경계의 계단 현상(aliasing) 자체를 완전히 없앨 수는 없다.

 

CSM은 제한된 shadow texel을 근거리와 같이

더 중요한 거리 구간에 효율적으로 배분하여 텍셀 밀도를 높히는 목적이지,
최종적으로 화면에 나타나는 그림자 경계를

얼마나 부드럽게 보이게 할 것인지까지 직접 해결해 주는 방법은 아니기 때문이다.

 

이 때문에 shadow map에서는 경계의 aliasing 효과를

시각적으로 완화하기 위한 여러 filtering기법들이 함께 사용된다.

 

그림자 경계를 부드럽게 만들기 위한 방법으로,

처음에는 Convolution 기반의 Blur나 Gaussian filter 같은

일반적으로 영상처리 등에서 많이 사용되는 이미지 필터를 떠올릴 수도 있다.

 

실제로 이러한 방식은 결과 이미지를 부드럽게 만드는 데에는 효과가 있지만,

Shadow Map에 직접 적용하기에는 몇 가지 한계가 있다.

 

먼저, Shadow Map은 단순한 색 이미지가 아닌 깊이 비교를 통해

가시성을 판정하는 역할로써 사용되기 때문에,

계산이 끝난 결과를 사후적으로 흐리기만 하면

원래의 가림 관계가 부정확해질 수 있다.

 

이 경우 그림자 경계가 자연스럽게 부드러워지기보다는,

빛이 새어 들어오거나(leaking) 경계가 번지는 형태의 부자연스러운 결과가 나타날 수 있다.

 

다음으로는, Gaussian blur와 같은 방식은

Kernel에 다수의 샘플을 요구하므로

실시간 렌더링 비용도 커질 수 있다.

 

Shadow Map은 광원 시점 렌더 패스를 추가로 사용하는 방법인데,

여기에 별도의 blur pass까지 더하면

전체 렌더링 비용과 메모리 접근 비용이 빠르게 증가할 수 있다.

 

따라서, 실시간 그림자에서는 shadow map에 blur를 그대로 적용하는 방식보다도

shadow map과 대상 픽셀의 depth compare를 여러 샘플에 대하여 수행한 뒤, 그 결과를 평균내는

PCF(Percentage Closer Filtering)

보다 불규칙적인 샘플을 사용하는 방식인

Poisson Disk

같은 방식이 더 널리 사용된다.


PCF(Percentage Closer Filtering)

PCF는 주변의 여러 shadow texel에 대해

depth compare를 각각 수행한 뒤 그 결과를 평균내는 방식이다.

 

즉, 결과 이미지를 사후적으로 blur하는 것이 아니라,

visibility test 자체를 여러 번 수행한 뒤

그 판정 결과를 평균내어 보다 부드러운 shadow factor를 얻는다고 볼 수 있다.

 

예를 들어, 주변 샘플들 중 일부는 그림자로, 일부는 비그림자로 판정된다면

PCF를 적용한 최종 결과는 완전히 검거나 완전히 밝은 값이 아닌 중간적인 음영값으로 표현된다.

 

이 때문에 이진적인 Aliasing 경계에서 보다 자연스럽고 부드러운 전이처럼 보이게 되며

그림자 경계에 대해 의사적인(pseudo) 반그림자(penumbra)의 효과도

함께 보이게 된다.

위의 사진을 통해 보다 자세한 처리 과정에 대해서 다루어 본다.

기본 shadow map은 한 픽셀에 대하여 한번의 depth compare을 통해

그림자의 0(shadow),1(lit)의 판정을 내렸다.

여기서 uv는 해당 shadow map texel 포인트를 의미하며,

0은 shadow, 1은 lit 상태를 의미한다.

 

반면, PCF에서는

한 점에서 끝내지 않고, 주변 여러 shadow texel 위치(Closer)에서

depth compare를 각각 수행한 뒤 그 결과를 평균(Percentage)을 내게 된다.

 

즉, PCF에서는 기존 depth compare 결과를 하나만 사용하는 것이 아니라

주변 여러 샘플에 대해서 반복 수행한 뒤 그 결과를 평균낸 값을 사용하게 된다.

이를 수식으로 표현하면 다음과 같다.

다음과 같이 N개의 샘플에 대해서 기존 텍셀에 대한 Δi으로 계산된

판정 결과를 평균낸 결과를 사용하는 것이다.

 

일반적으로 PCF를 간단하게 설명할 때는 샘플링을 픽셀 주변의 4개의 텍셀에 대해

샘플링하여 결과를 내는 방식이 흔하며 이를 4 tap PCF 이라고 한다.

 

4 tap PCF에서는 샘플 개수가 4개일 것이고,

주변 인접 텍셀 또한 균등한 offset을 두어 평균을 내게 된다.

이를 식으로 나타내면 다음과 같다.

Percentage는 주변 여러 compare 결과에 대해서(0이냐 1이냐에 따라)

bilinear weight로 blending되어 처리되고

이러한 계산된 값이 결과에 반영되여 나타난다.

 

즉, 주변 4개 texel의 visibility 결과가 각각 v00,v10,v01,v11이고

현재 샘플 위치가 텍셀 내부에서 α,β 만큼 떨어져 있다고 보면,

최종 결과는 bilinear interpolation으로 다음과 같이 해석할 수 있다.

각 샘플에 대한 총 합의 결과는 기존 방식의 0(shadow), 1(lit)에 대한 수치 뿐만 아니라

0.25, 0.5, 0.75와 같은 중간값으로도 나타날 수 있고,

이러한 결과가 그림자 경계를 보다 부드럽게 보이도록 만든게 된다.


Unity URP, HDRP의 기본값의 렌더링 파이프라인에서는

환경 설정에 따라 PCF의 샘플 수와 필터 방식을 다르게 지원한다.

 

URP에서는 PCF에 9 tap pcf을 사용한다고 하며

HDRP에서는 광원 종류나 품질 단계에 따라서 5x5 tent filter나 7x7 tent filter처럼

더 많은 샘플과 보다 부드러운 filtering을 사용하는 식이다.

 

이처럼 상용엔진에서는 shadow filtering을 하나의 고정 방식으로 처리하기보다,

광원 종류와 품질 단계에 따라서 서로 다른 샘플링을 적용하도록 구성하고 있다고 볼 수 있다.

유니티 렌더링 파이프라인에서는 Soft Shadow 토글 옵션을 볼 수 있는데,

해당 옵션이 PCF의 사용 유무에 대한 부분이며,

Soft Shadow 그림자 품질은

PCF의 sample 개수를 조절하는 부분이다.

 

위의 두 사진을 보면, 왼쪽은 aliasing이 쉽게 눈에 띄는 모습을 확인할 수 있으며

왼쪽 사진은 경계가 보다 smooth하게 나타나 있는 모습을 확인해 볼 수 있다. 

 

정리하자면, PCF는 최종적으로 여러 샘플들 결과를 평균내어

화면에 보이는 Aliasing을 시각적으로 완화하고

추가적으로 그림자 경계에서 반그림자의 효과를 간단하게 줄 수 있는 방법이라고 볼 수 있다.

 

다만, PCF를 사용할때 샘플 수가 많아질수록(High Quality) 성능 비용이 증가하므로

품질과 비용 사이의 적절한 타협이 필요하다.


한편, PCF는 균일한 샘플링을 사용하기 때문에

공간에 따라서 pseudo penumbra 영역이 균일하게 형성되어

차폐물과의 거리와 무관한 그림자를 형성한다는 시각적 단점이 존재한다.

위의 사진을 보면 바퀴나 나무의 뿌리처럼 차폐물에 가까운 그림자일수록 그림자가 선명해지고,

안장이나 손잡이, 이파리같이 차폐물과의 거리가 멀어질수록 그림자가 흐려지는 모습을 확인해 볼 수 있다. 

 

그림자는 거리에 따라서 본그림자와 반그림자의 형성되는 영역이 달라지는데,

일반적으로 차폐 오브젝트와의 거리가 멀수

록 그림자 경계가 점점 흐려지는 모습을 형성한다.

PCF는 이러한 형상을 반영하지 못하기 때문에

이를 개선하기 위한 다른 처리 방법들의 필요성이 재기되었고

 

이를 개선하기 위한 방법으로

 

PCSS(Percent Closer Soft Shadow),

CHS(Contact Hardening Shadow),

 

등의 방법이 존재한다.

이러한 방식들은 PCF처럼 단순히 경계를 균일하게 흐리는 것이 아니라,

차폐물과 수신면의 거리 변화에 따라

반그림자(Penumbra)의 폭이 달라지는 형상

보다 잘 반영하기 위한 시도라고 볼 수 있다.

 

다만, 이러한 방식들은 기본적인 PCF보다 처리 과정이 복잡하고

추가적인 샘플링이나, 기본 렌더링 파이프라인의 수정,

실험, 연구 등의 단계가 필요하기에

본 글에서는 해당 심화 주제까지의 접근으로는 당장 다루지 않는것으로 한다.


Poisson Disk Sampling

PCF 샘플링은 여러 샘플의 평균합을 통해서

다소 안정적인 비주얼을 보여줄 수 있지만,

필터 패턴이 픽셀의 인접한 텍셀 배열을 샘플링하기에
경우에 따라 인공적인 블러나 반복 무늬처럼 보일 수 있다.

 

또한 앨리어싱이 크게 형성되어 있는 경우,

PCF는 앨리어싱 경계에 따라서 규칙적으로 흐려지는 효과가 나타나기 때문에

아티팩트 형상을 완벽하게 바로 잡기 어려운 상황이 있을 수도 있을 것이다.

이를 보완하기 위해서 Poisson Disk라는 방법으로 확장해 볼 수 있다.

 

Poisson Disk Sampling은 샘플 위치를 일정 반경 내에서
보다 불규칙하게 분포시키는 방식으로,
규칙적인 패턴을 줄이고 보다 자연스러운 soft shadow를 표현하는 데 유리하다.

 

즉, PCF가 주변 인접 텍셀을 비교적 규칙적인 방식으로 샘플링한다면,

Poisson Disk Sampling은 이러한 샘플 위치 자체를 불규칙하게 배치함으로써

반복적인 필터 패턴을 줄이고 보다 자연스러운 결과를 얻고자 하는 방식이라고 이해할 수 있다.

여기서 중요한것은

푸아송 디스크 방식은 단순히 무작위로 랜덤하게만 샘플을 추출하는 방식이 아니라

각 샘플들이 서로 일정한 최소 거리를 유지한 통계적 성질을 기반으로배치된다는 점이다.

 

즉, 각 샘플 주변에는 다른 샘플이 침투하지 못하는

최소 반경의 영역이 존재한다고 볼 수 있는데,
이러한 성질 때문에 샘플이 한곳에 과도하게 몰리는 것을 막으면서도
규칙적인 격자 패턴에서 나타나는 반복 무늬를 줄일 수 있다.

 

여기서 Poisson이라는 것에 대해 그 자체로 푸아송 분포를 따른다 라고 보기에는 어렵고

단지 특정 확률적 분포를 따르며,

각 샘플에 대해서 최소 거리들을 유지하는 샘플의 방식이라고 보는게 적합하다.

이를 수식적으로 보면,

기본적으로 PCF의 평균 구조 자체가 달라지는 것은 아니고

샘플 오프셋 Δi의 배치가 달라지는 것이다.

 

PCF가 주변 인접 texel을 비교적 규칙적인 방식으로 샘플링한다면,
Poisson Disk Sampling은 이러한 샘플 위치 자체를 불규칙하게 배치함으로써
보다 자연스럽고 덜 인공적인 그림자 경계를 얻고자 하는 방식으로 이해할 수 있다.

 

다만 이 역시 본질적으로 더 많은 샘플링 비용을 요구하며,

분포나 회전에 따라 노이즈처럼 보일 수 있으므로

적절한 샘플 설계가 중요하다.

 

한편, MiHoYo가 2020 Unite Seoul에서 소개한 PS4용 원신 렌더링 파이프라인에서도

Poisson disc기반 soft shadow를 사용하였다고 소개하였다.

 

발표에 따르면 screen space shadow map 상에서 11개의 Poisson Disk 샘플을 이용해

부드러운 그림자 경계를 생성하였는데, 이러한 방식은 그대로 적용할 경우 비용이 크기 때문에

shadow 전체 픽셀에 일괄적으로 적용하기보다는

그림자 가장자리 영역에 대한 마스크를 생성한 뒤

그 부분에 추가 샘플링을 집중하는 방향으로 최적화가 이루어졌다고 한다.

 

즉, Poisson Disk Sampling에 대해서는 그림자 처리 자체가 매우 비싼 연산이기에

실용 적용 사례에서는 단순히 샘플 패턴을 불규칙하게 바꾸는 것에 그치지 않고,

실제 구현에서는 penumbra가 필요한 edge 영역을

중심으로 선택적으로 적용하여 최적화 처리를 하는 것 같다.

 

본 글에서는 해당 방식을 적용하기 위해서는 기존 렌더링 파이프라인을 일부 수정 및 추가해야 하기 때문에

Poisson Disk Sampling의 구현까지는 당장 다루지 않고,

흔히 많이 사용되는 균일한 PCF의 규칙적인 샘플 패턴을 완화하기 위한

확장 방향 중 하나라는 점 정도로만 인식하고 넘어가도록 한다.


내용 정리

지금까지 글에서는 그림자의 목적과 역할, 여러 구현 방법들,

그리고 Shadow Map을 기반으로 한 실시간 그림자 처리의 기본 아이디어와
그 과정에서 발생하는 대표적인 아티팩트들,

이를 완화하기 위한 여러 기법들에 대해 정리해 보았다.

 

정리하자면, Shadow Map은 광원 시점의 가시성 판정을 통해

그림자를 처리하는 매우 강력하고 일반적인 방법이지만,
동시에 depth texture 기반의 이산적 표현이라는 한계를 가지며
이로 인해 Shadow Acne, Peter Panning, Aliasing 등과 같은 문제들이 발생한다.

 

이러한 문제를 완화하기 위해

Bias, Cascade Split, Filtering 등과 같은 여러 기법들이 함께 사용되며,
PCF와 Poisson Disk Sampling을 통해

Aliasing 문제와 Pseudo Penumbra를 다루는 방식에 대해서도 알아보았다.

 

하지만 지금까지의 내용은 어디까지나 일반적인 real time shadow map의 문맥에서
그림자를 보다 사실적이고 안정적으로 처리하기 위한 배경지식에 가깝다.

 

실제로 이를 Non Photorealistic Rendering 환경에서

그림자를 어떻게 처리해야 할지에 대한 것은 별개의 문제이며

이를 위한 구현과 구현에 따른 문제 상황들에 대해서 앞으로 다루어 보아야 한다. 

따라서, 다음으로는 본격적으로
Shadow Map을 NPR 환경 안에서 어떻게 사용하고,
receiver와 caster가 동시에 관여할 때 어떤 문제가 발생하는지,
또 현재 시점에서 어떤 부분까지 구현하였고

어떤 부분에 대한 문제를 아직 해결하지 못했는지에 대해
구현 관점에서 정리해 보려고 한다.