16. Projected Shadow Mapping
그림자를 생성하고 적용하는 과정에서 별
다른 연산이 없으므로 호환성이 뛰어나다.
캐릭터의 모습을 적용할 수 있다.
매 프레임마다 그림자를 갱신해 줘야 하는
이슈가 있지만 멀리 있는 녀석의 그림자는
생략하는 등 트릭으로 극복가능.
17. Projected Shadow Mapping
문제점 1 : 셀프 그림자가 적용되지 않는다.
이런 것
들
이런 것
들
이런 것
들
18. Projected Shadow Mapping
문제점 1 : 셀프 그림자가 적용되지 않는다.
왜냐하면 그림자가 오브젝트 단위로 생성
되기 때문이다.
그렇기 때문에 오브젝트 자신의 차폐를 계
산하기 어렵다.
23. Stencil Shadow Volume
일단 씬을 렌더링하고 깊이정보를 저장한다.
오브젝트의 외곽에 있는 버텍스를 광원방향
으로 잡아 늘린다.
앞서 저장한 깊이정보와 그림자 볼륨을 비교
하여 그림자인지 아닌지를 스텐실 버퍼에 기
록한다.
최종 렌더링시에 스텐실 버퍼를 참고하여 그
림자를 그린다.
24. Stencil Shadow Volume
그림자 볼륨 중 앞에 면만
스텐실 버퍼에 1로 기록한다.
*계산 전에 스텐실 버퍼를
0으로 초기화 해두자!
43. Depth Buffer Shadow
해결방안은??
그림자 맵의 해상도를 높인다. -> 한계가 있다.
요즘 게임 특히 야외를 보여주는 게임은 점점
먼 곳 까지 보여주기 때문에 커버할 수 없다.
그래도 해상도만 괜찮으면 쓸만한 그림자니까
개량해서 사용해야 겠다!
44. Perspective Shadow Map( PSM )
그림자 맵 해상도 이슈가 발생하는 이유는
간단하다.
게임 상의 오브젝트들을 모두 그림자 맵에
담기 때문에 넓을 수록 한 텍셀이 표현해야
할 그림자 영역이 점점 넓어지다 보니 품질
이 떨어지게 된다.
45. Perspective Shadow Map( PSM )
위의 구현 방법은 맵이 100m x 100m면 전
체를 그림자 맵에 담아서 사용하게 된다.
텍스쳐 사이즈가 256 x 256이면 한 텍셀은
40cm x 40cm가 된다고 보면 된다.
46. Perspective Shadow Map( PSM )
시점이 가까운 곳이나 먼 곳이나 동일한 해
상도를 사용하게 된다. 먼 곳에 있는 녀석
들은 자세히 그릴 필요가 없지 않은가?
가까운 녀석에게 해상도를 집중하고 먼 녀
석은 대충 그려주자가 PSM의 목적이다.
47. Perspective Shadow Map( PSM )
시점에서 가까운 곳과 먼 곳은 카메라에 의
해 결정된다. ( View )
가까운 물체는 커보이고 멀리 있는 물체는
작아보인다. ( Projection )
View x Projection 변환된 녀석을 그림자 맵
으로 뽑아내자!
48. Perspective Shadow Map( PSM )
기존에 쉐도우 맵을 뽑을 때완 달리 가까운 녀석의
비중이 더 커지게 그림자 맵을 생성할 수 있다.
51. Perspective Shadow Map( PSM )
기존의 그림자 맵보다 효율적으로 만들 수
있어서 더 매끄러운 그림자가 나온다.
이런 부분만 보면 완벽할 것 같다!!
52. Perspective Shadow Map( PSM )
PSM은 광원을 기준으로 그림자 맵을 생성
한다. 따라서 광원에선 멀리 있는 그림자를
카메라 시점에서는 가까이 볼 수도 있다.
이 때는 마찬가지로 그림자의 품질이 떨어
진다.
53. Perspective Shadow Map( PSM )
PSM은 광원을 기준으로 그림자 맵을 생성
한다. 따라서 광원을 기준으로 보이는 녀석
들에 대해서만 그림자 맵을 만들게 된다.
오브젝트는 보이지 않지만 그림자만 보여
야 하는 상황에서는 올바르게 그림자가 생
성되지 않을 수 있다.
54. Light Space Perspective Shadow Map( LSPSM )
PSM의 문제를 해결하기 위해 개량된 방법을 제
시하였다. 그것이 바로 LSPSM이다.
위의 문제를 정리하면 광원 기준의 그림자 생성
과 보이지 않는 오브젝트가 있을 수 있다는 것이
다.
55. Light Space Perspective Shadow Map( LSPSM )
하지만 저런 특정 상황을 제외하고는 그림자 맵
을 카메라에 맞게 옮겨서 생성하는 아이디어는
괜찮은 것 같다.
그렇다면 위의 문제를 해결하면서 그림자 맵을
옮겨서 생성하자!
그림자 맵 생성을 위한 좌표계를 만들어 내고 그림자를 생성하자!
56. Light Space Perspective Shadow Map( LSPSM )
그림자를 생성할 가능성이 있는 오브젝트는 모두
포함시키게 시야 거리를 조정한다.
최대한의 해상도를 구하기 위해 최대한 가까운
곳에서 그림자 맵을 뽑아낸다.
좌표계는 다음과 같은 방식으로 생각해 볼 수 있
다.
57. Light Space Perspective Shadow Map( LSPSM )
빛의 방향의 반대되는 방향을 Y축
뷰 벡터와 Y축을 외적해서 구한 방향을 X축
X축과 Y축을 외적해서 구한 방향을 Z축
58. Light Space Perspective Shadow Map( LSPSM )
PSM은 뒷 부분 그림자의 품질이 낮다.
59. Light Space Perspective Shadow Map( LSPSM )
LSPSM은 뒷 부분 그림자까지 제대로 그려준다.
60. Cascaded LSPSM
LSPSM을 가지고 좋은 그림자를 생성해 냈으나
점점 더 야외 맵은 넓어지고 멀리 보이게 되고 있
다.
LSPSM을 이용해 처리를 해도 마찬가지로 먼 곳
까지는 텍스쳐 해상도의 한계에 부딪치게 된다.
61. Cascaded LSPSM
해상도에 한계가 있어서 계속 그림자 맵을 효율
적으로 생성하는 것에만 신경을 쓰다가 그림자
맵을 여러 개 두면 되지 않을까? 로 생각이 넘어
오게 된다.
지금까지 구현을 보아 가까운 녀석에게는 그림자
해상도를 많이 투자하는게 맞고 먼 녀석에게는
적게 투자하더라도 어느정도는 투자가 되어야 한
다.
62. Cascaded LSPSM
근거리용 그림자 맵( 가까운 물체의 해상도는 높
게 )
중거리용 그림자 맵( 중간 물체도 위보단 높게 )
장거리용 그림자 맵( 멀리있는 물체도 최소한 보
이게 )
이런식으로 나누면 되겠구나!
물론 게임마다 거리를 나누는 기준과 몇 단계로 나눌
것이냐는 따로 정해야 한다.
69. Soft Shadow
Post Processing이다 보니 격렬하게 뭉게
면 3D모델 렌더링에 영향을 끼칠 수 있다.
Soft Shadow역시 이것 저것 개량해서 쓰면
좋은 퀄리티를 낼 수 있겠다. ( 이건 다음
에.. )
70. Variance Shadow Map( VSM )
언리얼 엔진 3.0에서 사용한다고 한다.
그림자 맵에 광원에서 오브젝트까지의 거
리와 그 제곱값을 저장한다.
이 값을 이용해 그림자가 생길 확률을 계산
하고 그림자를 입혀준다.
71. Variance Shadow Map( VSM )
VSM을 위한 그림자 맵을 만들기 위해서는
부동 소수점을 저장하는 텍스쳐를 이용해
야 한다.
이 기법을 이용하면 소프트 쉐도우와 같은
구현이 된다고 한다.
72. Variance Shadow Map( VSM )
기존의 그림자 맵은 그림자다 아니다.
0 아니면 1 이런 느낌.
반면 VSM은 체비쇼프 부등식을 이용하여 깊
이값을 기반으로 빛이 비치는 정도를 확률로
구한다.
빛이 안 비칠 확률이 높을 수록 어둡게 만든다.
나름 자연스러운 그림자 완성!
74. 그 중에서 깊이 버퍼 그림자 구현 부분을
자세히 알아보자.
깊이 버퍼 그림자 구현의 기초를 이해하면
나머지 그림자 구현을 이해하는데 도움이
될 것이다.
75. 깊이 버퍼 그림자는 2패스로 진행된다.
그림자 맵 생성
▪ 광원의 위치에서 본 장면을 렌더링한다.
▪ 렌더링 할 색은 광원으로 부터의 거리로 한다.
그림자 맵과 실제 거리 비교 후 그림자 적용
▪ 그림자 맵이 가리키는 픽셀 값과 실제 거리를 비교한
다.
▪ 그림자 맵이 가리키는 픽셀 값이 더 작으면 그림자다.
76. 그림자 맵 생성
그림자 맵을 저장하기 위한 텍스쳐를 생성한다.
그림자 맵 생성시 사용할 깊이 버퍼를 만든다.
렌더 타겟과 깊이 버퍼를 만든 녀석들로 바꾼다.
그림자 맵에 맞게 뷰포트를 잡아준다.
장면을 렌더링 한다.
뷰포트 , 깊이 버퍼 , 렌더 타겟을 복구한다.
77. 그림자 맵 생성
전에 포스트 이펙트 발표 때를 기억하시면 됩니
다.
이따가 소스를 간단하게 훑어 보겠습니다.
78. 그림자 맵 생성
광원에서 본 장면을 렌더링해야 한다.
장면을 그리기 위해 넘어온 좌표들을 광원을 중심으
로 한 행렬로 변환해야 한다.
광원을 중심으로 한 행렬 구하기
월드 행렬 x 뷰 행렬 x 투영 행렬
광원위치를 기준으로한 뷰 행렬
광원을 기준으로 적당한 FOV등을 설정한 투영 행렬
79. 그림자 맵 생성
버텍스 쉐이더
버텍스들을 광원을 중심으로한 행렬로 변환한다.
픽셀 쉐이더에서 깊이값을 저장할 수 있게 변환된 위
치를
넘겨 준다.
픽셀 쉐이더
넘어온 변환된 위치를 이용해 깊이 값을 저장시킨다.
깊이 : z / w ( 0 ~ 1 값 )
81. 그림자 맵 생성
위의 소스를 보면 z / w 구문이 픽셀 쉐이더에
있음을 알 수 있다.
버텍스 쉐이더에서 계산을 하는게 더 빨라 보이
지만 값이 불안정해 질 수 있다.
픽셀 쉐이더에서는 버텍스 사이 사이에 있는 픽
셀들의 값을 채워줘야 한다.
82. 그림자 맵 생성
우리가 원하는 깊이의 저장은 이런 모습이다.
두 버텍스 p0 = ( z0 , w0 ) , p1 = ( z1 , w1 )가 있다고
하고 이 버텍스들의 중심 부분을 얻어온다고 생각해
보자.
z’ = ( ( z0 + z1 ) / 2 )
( ( w0 + w1 ) / 2 )
하지만 버텍스 쉐이더에서 먼저 계산을 하고 넘
긴다면? 오차가 날 것이다.
z’ = ( ( z0 ) + ( z1 ) ) * 1
( ( w0 ) ( w1 ) ) 2
83. 그림자 맵 적용
퍼스펙티브 보정에 의한 보간에서는 비선형 깊
이가 바르게 보간 되지 않는단다…
비선형 깊이가 무엇인가 하면 투영행렬까지 거
친 위치정보는 원근처리가 되어있기 때문에 깊
이값을 구하면 선형적인 값이 나오지 않는다.
그렇기 때문에 깔끔하게 픽셀쉐이더에서 그 순
간 깊이를 계산해서 저장하는 것이다.
84. 그림자 맵 적용
그렇단 말은 선형적인 값을 갖는 깊이를 버텍스
쉐이더에서 계산할 수 있으면 그냥 픽셀 쉐이더
에 넘겨줘도 된다는 말이다.
선형 깊이를 구하는 식은
z’ = ( z - zn ) * ( zf )
( zf - zn ) ( z )
85. 그림자 맵 적용
( x , y , z , 1 )을 변환하면?
( w * x , h * y , Q * z – Q * Zn , z )
Z와 W만 집중해서 봅시다!
( Q * z – Q * Zn ) / W
-> ( Zf * z ) – ( Zf * Zn ) * ( 1 )
( Zf – Zn ) ( Zf – Zn ) ( z )
-> ( Zf * ( z – Zn ) )
z * ( Zf – Zn )
86. 그림자 맵과 실제 거리 비교 후 그림자 적용
그림자 맵을 만들었으니 그림자 맵을 이용하자.
렌더링할 때 그림자 맵을 매핑할 수 있어야 한
다.
그림자 맵 : X : ( -1 , 1 ) , Y : ( -1 , 1 ) , Z : ( 0 ,
1 ) 텍스쳐의 UV : X : ( 0 , 1 ) , Y : ( 0 , 1 )
그림자 맵을 UV좌표계로 변환해 보자.
U = +0.5X + 0.5
V = - 0.5Y + 0.5
87. 그림자 맵과 실제 거리 비교 후 그림자 적용
위의 변환식을 행렬로 묶으면?( -1 , 1 ) -> ( 0 ,
1 )
( 0.5 0 0 0.5 )
( 0 -0.5 0 0.5 )
( 0 0 1 0 )
( 0 0 0 1 )
88. 그림자 맵과 실제 거리 비교 후 그림자 적용
투영했을 때 ( 0 , 1 )범위로 갔다는 것은 w = 1,
즉 W값으로 나누었다는 이야기가 된다. W로
나누기 전이라면 다음과 같이 적용해야 한다.
( 0.5 0 0 0.5 + ( x / w ) )
( 0 -0.5 0 0.5 + ( y / w ) )
( 0 0 1 0 )
( 0 0 0 1 )
89. 그림자 맵과 실제 거리 비교 후 그림자 적용
X = 1 , Y = 1 , W = SHADOW_MAP_SIZE
91. 그림자 맵과 실제 거리 비교 후 그림자 적용
조명 처리를 위한 부분은 제외하고 보면
Out.ShadowMapUV = mul(Pos, mWLPB);
그림자 맵을 입히기 위해서 변환한다.
Out.Depth = mul(Pos, mWLP);
그림자 맵의 값과 거리를 비교하기 위해 변환한다.
93. 그림자 맵과 실제 거리 비교 후 그림자 적용
거리비교 부분
그림자 맵의 값을 얻어온다.
현재 투영된 녀석과 거리를 맞추기 위해 w값을 곱한
다.
그 값을 z값과 비교한다.
Bias를 막기 위해 0.03이라는 마법의 숫자를 넣었다.
그림자 맵의 깊이보다 넘어온 녀석이 크면 그림자!
아니면 그냥 텍스쳐 디퓨즈 색.
최종적으로 조명계산까지 덧 붙인다.