Game Develop

[DirectX11] 깊이맵을 이용한 그림자매핑 본문

ComputerGraphics/DirectX

[DirectX11] 깊이맵을 이용한 그림자매핑

MaxLevel 2022. 10. 3. 21:07

여러 그림자 구현방법중, 깊이맵을 이용한 그림자구현 기법이다.

옛날에는 Circle Shadow, Projected Shadow Mapping 등을 사용했다고 하지만 요즘 하드웨어로 굳이 퀼리티가 떨어지는 기법을 사용할 필요가 없다.

제목처럼 깊이맵을 활용한 그림자매핑은 현재 상용엔진에서도 많이 적용하는 기법이다.

물론! 내가 지금 적용한건 제일 기초적인 그림자매핑이고, 실제로 상용엔진에서는 캐스케이드 그림자매핑이나 퍼센티지 클로저필터링 등 추가적인 최적화를 거친 기법을 사용한다.

 

깊이맵을 이용한 그림자매핑에 대한 설명은 사실 이미 잘나와있는것들이 굉장히 많아서... 나는 아래링크를 보고 구현해봤다. 다만 아래링크는 dx9이긴하지만 크게 상관은 없다.

https://www.slideshare.net/SukwooLee4/20181222-126893802

 

2018.12.22 깊이 버퍼 그림자 매핑

Depth Buffer를 이용한 깊이버퍼 그림자 매핑에 대해 설명한다.

www.slideshare.net

 

기본적으로 WVP변환에 대한 이해도가 잡혀있다면 무슨 원리인지 이해하는데 큰 어려움은 없을것이다.

셰이더코드까지 전부 나와있기에 구현자체는 문제가 없을 듯 하다.(그림자퀼리티가 좋든 나쁘든..) 

 

먼저 광원기준으로의 깊이맵을 얻어와야하기 때문에 DSV를 바인딩해주고 Draw를 해준다. 

깊이맵만 얻으면 되기때문에 렌더타겟에는 null을 셋팅해도 되지만, 그냥 보는용도로 사용하기위해서 렌더타겟 하나 셋팅하고 깊이값 따로 계산해서 그려준다음 ImGui로 띄웠다.

 

걍 원본으로도 띄워도 되긴 하는데, 깊이맵텍스쳐가 텍스쳐의 R채널에 저장되다 보니 거의 빨갛게 나오기도 하고 조금이라도 nearZ에서 멀어지면 거의 구분이 안가기 때문에 되도록 보는용도의 렌더타겟을 따로 만들어주는 편이다.

 

어쨌든 깊이맵 따온 이후에 실제로 그림자 그려주는 셰이더에서는 VS에서 정점월드값에다가 상수버퍼로 넘긴 광원의 뷰행렬, 투영행렬을 곱해줌으로써, 광원기준으로 봤을 때의 정점으로 변환시킨 후, 따로 저장해놓는다.

그러면 이 값들이 의미하는 바는, 광원기준으로의 ClipSpace좌표값들이다.

그리고 PS에서 저장한값을 꺼내서 xy값은 광원깊이맵을 샘플링하기위해 깊이맵의 UV좌표로 변환한다.

 

위 이미지에서 input.mClipPosition은, 메인카메라가 바라보는 각 정점을 광원기준으로의 ClipSpace공간좌표로 변환한값들이다. (VS에서 광원의 ViewMatrix와 ProjectionMatrix를 곱했으니까)

깊이값은 원근나누기 이후의 z값이니까, currentDepth에 z값을 w를(원근나누기)를 대입한것이고, xy좌표는 NDC좌표계인 깊이맵을 샘플링해야하니까 변환한것이다. 

 

그다음은 부동소수점에러방지를 위한 bias값을 깊이맵의 깊이값에 더해주고, currentDepth값이 더 크면 검은색으로 칠해주면 된다. currentDepth값이 샘플링한 깊이값보다 더 크다는 것은 뭔가로 막혀있다는 뜻이기 때문이다.

 

여기까지가 딱 제일 기본적인 깊이맵을 활용한 그림자매핑이고, 실제로 적용해봤더니 나는 상당히 퀄리티가 별로이게 나온다. 부드럽지가 않고 굉장히 각져서 나온다. 임시방편으로 그림자맵해상도를 올려보던가, 나중에 캐스케이드를 적용하던가 해야할 것 같다.

사실 이제는 언리얼위주로 할거같긴하지만...