2010/10/23
Devrookie 엠에스박( 박민수 )
 그림자란?
 3D에서의 그림자
 그림자 구현의 역사
 깊이 버퍼 그림자 구현
 시연
 Q & A
 참고자료
 그림자 : 빛이 지나가는 경로에 불투명한
물체가 존재하여 빛이 통과하지 못해 생기
는 어두운 부분.
 물체의 가장자리를 지나가는 빛은 회절 현
상에 의해 물체를 넘어갈 수도 있어서 가장
자리 부분은 비교적 밝은 그림자가 생긴다.
반 그림자 : 흐릿하다.
본 그림자 : 진하다.
 3D에서는 그림자를 크게 2가지로 나눌 수 있다.
그림자
음영
 음영은 보통 Lighting 계산으로 구한다.
 특별한 처리를 하지 않아도 기본적인 파이프라인
에서 음영은 제공해 준다.
 그림자는 애초부터 따로 구현해야 하는 부분이였
다.
 Circle Shadow
 Projected Shadow Mapping
 Stencil Shadow Volume
 Depth Buffer Shadow
 Perspective Shadow Map( PSM )
 Light Space Perspective Shadow Map( LSPSM )
 Cascaded LSPSM
 Soft Shadow
 Variance Shadow Map( VSM )
 3D 그래픽스 매니악스를 훑어보자!
 Circle Shadow
 캐릭터의 위치를 계산하여 발 밑 부분에 원
형의 그림자 텍스쳐를 적용시켜준다.
 다른 캐릭터들도 원형의 그림자를 갖게 되
어 리얼리티가 떨어진다.
캐릭터의 위치정도는
파악할 수 있게 해준다.
 Circle Shadow
 심플하게 생각해보자.
 머리 위에서 캐릭터를 향해 빛이 비친다.
 이 때 쉽게 그림자를 생성하는 법은?
 캐릭터의 y좌표를 땅의 높이로 두고 검은색으
로 렌더링한다!
 심플하게 생각해보자.
빛의 방향
 심플하게 생각해보자.
 빛이 다른 방향에서 비춘다면?
 심플하게 생각해보자.
빛의 방향
 Projected Shadow Mapping
 앞의 아이디어를 기본으로 나온 기법이다.
 광원에서 본 오브젝트의 실루엣을 그림자
텍스쳐에 저장한다.
 광원방향으로 투영하듯 텍스쳐를 매핑한
다.
 Projected Shadow Mapping
캐릭터 형태가
나온다!
 Projected Shadow Mapping
 그림자를 생성하고 적용하는 과정에서 별
다른 연산이 없으므로 호환성이 뛰어나다.
 캐릭터의 모습을 적용할 수 있다.
 매 프레임마다 그림자를 갱신해 줘야 하는
이슈가 있지만 멀리 있는 녀석의 그림자는
생략하는 등 트릭으로 극복가능.
 Projected Shadow Mapping
 문제점 1 : 셀프 그림자가 적용되지 않는다.
이런 것
들
이런 것
들
이런 것
들
 Projected Shadow Mapping
 문제점 1 : 셀프 그림자가 적용되지 않는다.
 왜냐하면 그림자가 오브젝트 단위로 생성
되기 때문이다.
 그렇기 때문에 오브젝트 자신의 차폐를 계
산하기 어렵다.
 Projected Shadow Mapping
 문제점 2 : 투영을 잘 시켜야 한다.
 Projected Shadow Mapping
 문제점 2 : 투영을 잘 시켜야 한다.
 계산을 잘 못하면 벽 넘어에 그림자가 생기
는등 버그가 발생 할 수 있다.
 Stencil Shadow Volume
 셀프 쉐도우를 위해서는 광원을 기준으로
차폐 여부를 가지고 그림자를 적용해야 한
다.
 Stencil Shadow Volume
셀프 쉐도우
볼륨
 Stencil Shadow Volume
 일단 씬을 렌더링하고 깊이정보를 저장한다.
 오브젝트의 외곽에 있는 버텍스를 광원방향
으로 잡아 늘린다.
 앞서 저장한 깊이정보와 그림자 볼륨을 비교
하여 그림자인지 아닌지를 스텐실 버퍼에 기
록한다.
 최종 렌더링시에 스텐실 버퍼를 참고하여 그
림자를 그린다.
 Stencil Shadow Volume
그림자 볼륨 중 앞에 면만
스텐실 버퍼에 1로 기록한다.
*계산 전에 스텐실 버퍼를
0으로 초기화 해두자!
 Stencil Shadow Volume
볼륨의 뒷면은 -1로 그려준다.
 Stencil Shadow Volume
1인 부분을 그림자로 보면 된다.
 Stencil Shadow Volume
 광원이 여러 개가 있거나 오브젝트가 서로
겹치는 등 경우에 따라서 스텐실 버퍼의 값
이 1보다 클 수도 있다.
 0이 아니면 그림자로 생각해도 무방하다!
 Stencil Shadow Volume
 깊이정보를 이용해서 차폐를 계산했기 때
문에 셀프쉐도우가 가능해 졌다.
 기타 다른 그림자의 문제도 해결된 듯 보인
다!
 Stencil Shadow Volume
 구현을 보면 알겠지만 그림자를 처리할 때
폴리곤 단위로 처리를 한다.
 아까는 오브젝트가 그림자 단위였는데 폴
리곤 단위니 전보다 세밀해 졌다!
 Stencil Shadow Volume
잎사귀 텍스쳐도 어차피
네모판일 뿐…
 Stencil Shadow Volume
그림자가 사각형이다.
 Stencil Shadow Volume
 볼륨을 생성하는 부분도 생각을 해봐야 한
다.
 잘못 늘리면 그 만큼 그림자가 잘못 생성된
다.
 Stencil Shadow Volume
 그리고 원래 캐릭터의 모양을 유지하면서
정점을 늘려야 하기 때문에 늘릴 정점이 따
로 필요하다.
 보이는 것보다 더 많은 정점이 쓰이게 된다.
 Stencil Shadow Volume
 Stencil Shadow Volume
 하지만 이전 Direct10 스터디 때도 나왔듯
이 정점을 생성해주는 쉐이더가 추가 되었
기 때문에 위의 문제점은 해결이 될 듯 하
다.
 Depth Buffer Shadow
 위의 Stencil Shadow Volume도 문제가 있
다.
 그런데 GPU는 발전해가고 픽셀 계산능력
이 올라가면서 Depth Buffer Shadow 기법
이 사용되기 시작했다.
 Depth Buffer Shadow
 광원을 기준으로 오브젝트들과의 거리( 깊
이 )를 저장한다.( 그림자 맵 )
 실제 렌더링 시에 다시 광원과 오브젝트의
거리를 비교하고 위에서 저장해둔 차폐 정
보를 비교하여 그림자를 판별하고 적용한
다.
 Depth Buffer Shadow 광원의 시점에서 깊이가 저장되면
노란색 부분은 기록되고 주황색 부분은
묻히게 된다.
위에서 무시된 부분이
그림자가 됨을 알 수 있다.
 Depth Buffer Shadow
위에서 저장된 값은 결국 광원이
도달하는 부분이다.
실제 렌더링할 때 다시 한번 광원과
오브젝트의 거리를 재서 위에 저장한
값과 비교해 보고 그림자인지 아닌지
판단한다.
D > S : 그림자
D == S : 그림자 아님
 Depth Buffer Shadow
 차폐 정보를 이용해 처리한 것이니까 셀프
쉐도우가 가능하다.
 아까는 폴리곤 단위였지만 지금은 픽셀 단
위이므로 위의 문제도 해결된다.
 Depth Buffer Shadow
 모든 구현에는 문제점이 존재한다.
 픽셀 단위의 구현으로 인해 그림자 맵의 해
상도가 적으면 그만큼 그림자의 세밀함이
떨어지게 된다.
 Depth Buffer Shadow
픽셀 돋네..
 Depth Buffer Shadow
 해결방안은??
 그림자 맵의 해상도를 높인다. -> 한계가 있다.
 요즘 게임 특히 야외를 보여주는 게임은 점점
먼 곳 까지 보여주기 때문에 커버할 수 없다.
 그래도 해상도만 괜찮으면 쓸만한 그림자니까
개량해서 사용해야 겠다!
 Perspective Shadow Map( PSM )
 그림자 맵 해상도 이슈가 발생하는 이유는
간단하다.
 게임 상의 오브젝트들을 모두 그림자 맵에
담기 때문에 넓을 수록 한 텍셀이 표현해야
할 그림자 영역이 점점 넓어지다 보니 품질
이 떨어지게 된다.
 Perspective Shadow Map( PSM )
 위의 구현 방법은 맵이 100m x 100m면 전
체를 그림자 맵에 담아서 사용하게 된다.
 텍스쳐 사이즈가 256 x 256이면 한 텍셀은
40cm x 40cm가 된다고 보면 된다.
 Perspective Shadow Map( PSM )
 시점이 가까운 곳이나 먼 곳이나 동일한 해
상도를 사용하게 된다. 먼 곳에 있는 녀석
들은 자세히 그릴 필요가 없지 않은가?
 가까운 녀석에게 해상도를 집중하고 먼 녀
석은 대충 그려주자가 PSM의 목적이다.
 Perspective Shadow Map( PSM )
 시점에서 가까운 곳과 먼 곳은 카메라에 의
해 결정된다. ( View )
 가까운 물체는 커보이고 멀리 있는 물체는
작아보인다. ( Projection )
 View x Projection 변환된 녀석을 그림자 맵
으로 뽑아내자!
 Perspective Shadow Map( PSM )
기존에 쉐도우 맵을 뽑을 때완 달리 가까운 녀석의
비중이 더 커지게 그림자 맵을 생성할 수 있다.
 Perspective Shadow Map( PSM )
 Perspective Shadow Map( PSM )
 Perspective Shadow Map( PSM )
 기존의 그림자 맵보다 효율적으로 만들 수
있어서 더 매끄러운 그림자가 나온다.
 이런 부분만 보면 완벽할 것 같다!!
 Perspective Shadow Map( PSM )
 PSM은 광원을 기준으로 그림자 맵을 생성
한다. 따라서 광원에선 멀리 있는 그림자를
카메라 시점에서는 가까이 볼 수도 있다.
 이 때는 마찬가지로 그림자의 품질이 떨어
진다.
 Perspective Shadow Map( PSM )
 PSM은 광원을 기준으로 그림자 맵을 생성
한다. 따라서 광원을 기준으로 보이는 녀석
들에 대해서만 그림자 맵을 만들게 된다.
 오브젝트는 보이지 않지만 그림자만 보여
야 하는 상황에서는 올바르게 그림자가 생
성되지 않을 수 있다.
 Light Space Perspective Shadow Map( LSPSM )
 PSM의 문제를 해결하기 위해 개량된 방법을 제
시하였다. 그것이 바로 LSPSM이다.
 위의 문제를 정리하면 광원 기준의 그림자 생성
과 보이지 않는 오브젝트가 있을 수 있다는 것이
다.
 Light Space Perspective Shadow Map( LSPSM )
 하지만 저런 특정 상황을 제외하고는 그림자 맵
을 카메라에 맞게 옮겨서 생성하는 아이디어는
괜찮은 것 같다.
 그렇다면 위의 문제를 해결하면서 그림자 맵을
옮겨서 생성하자!
 그림자 맵 생성을 위한 좌표계를 만들어 내고 그림자를 생성하자!
 Light Space Perspective Shadow Map( LSPSM )
 그림자를 생성할 가능성이 있는 오브젝트는 모두
포함시키게 시야 거리를 조정한다.
 최대한의 해상도를 구하기 위해 최대한 가까운
곳에서 그림자 맵을 뽑아낸다.
 좌표계는 다음과 같은 방식으로 생각해 볼 수 있
다.
 Light Space Perspective Shadow Map( LSPSM )
 빛의 방향의 반대되는 방향을 Y축
 뷰 벡터와 Y축을 외적해서 구한 방향을 X축
 X축과 Y축을 외적해서 구한 방향을 Z축
 Light Space Perspective Shadow Map( LSPSM )
PSM은 뒷 부분 그림자의 품질이 낮다.
 Light Space Perspective Shadow Map( LSPSM )
LSPSM은 뒷 부분 그림자까지 제대로 그려준다.
 Cascaded LSPSM
 LSPSM을 가지고 좋은 그림자를 생성해 냈으나
점점 더 야외 맵은 넓어지고 멀리 보이게 되고 있
다.
 LSPSM을 이용해 처리를 해도 마찬가지로 먼 곳
까지는 텍스쳐 해상도의 한계에 부딪치게 된다.
 Cascaded LSPSM
 해상도에 한계가 있어서 계속 그림자 맵을 효율
적으로 생성하는 것에만 신경을 쓰다가 그림자
맵을 여러 개 두면 되지 않을까? 로 생각이 넘어
오게 된다.
 지금까지 구현을 보아 가까운 녀석에게는 그림자
해상도를 많이 투자하는게 맞고 먼 녀석에게는
적게 투자하더라도 어느정도는 투자가 되어야 한
다.
 Cascaded LSPSM
 근거리용 그림자 맵( 가까운 물체의 해상도는 높
게 )
 중거리용 그림자 맵( 중간 물체도 위보단 높게 )
 장거리용 그림자 맵( 멀리있는 물체도 최소한 보
이게 )
 이런식으로 나누면 되겠구나!
 물론 게임마다 거리를 나누는 기준과 몇 단계로 나눌
것이냐는 따로 정해야 한다.
 Cascaded LSPSM
 Cascaded LSPSM
 Soft Shadow
 지금까지 그림자 맵을 찍는 부분에 대해서
만 생각해 왔다.
 문제는 그림자 품질이 떨어지는 현상이다.
 Post Processing으로 해결할 수 있지 않을
까?
 Soft Shadow
 계단현상이 생긴 부분을 뭉게버리자!
 뭉게지면 부드러워 지고 실제 그림자의 뿌
연 느낌도 줄 수 있을 것 같다!
 Soft Shadow
뭉게기 전 뭉게기 후
그림을 확대해서 둘 다 뭉게진 듯 하지만 자세히 보면 보인다.
 Soft Shadow
뭉게기 전 뭉게기 후
 Soft Shadow
 Post Processing이다 보니 격렬하게 뭉게
면 3D모델 렌더링에 영향을 끼칠 수 있다.
 Soft Shadow역시 이것 저것 개량해서 쓰면
좋은 퀄리티를 낼 수 있겠다. ( 이건 다음
에.. )
 Variance Shadow Map( VSM )
 언리얼 엔진 3.0에서 사용한다고 한다.
 그림자 맵에 광원에서 오브젝트까지의 거
리와 그 제곱값을 저장한다.
 이 값을 이용해 그림자가 생길 확률을 계산
하고 그림자를 입혀준다.
 Variance Shadow Map( VSM )
 VSM을 위한 그림자 맵을 만들기 위해서는
부동 소수점을 저장하는 텍스쳐를 이용해
야 한다.
 이 기법을 이용하면 소프트 쉐도우와 같은
구현이 된다고 한다.
 Variance Shadow Map( VSM )
 기존의 그림자 맵은 그림자다 아니다.
 0 아니면 1 이런 느낌.
 반면 VSM은 체비쇼프 부등식을 이용하여 깊
이값을 기반으로 빛이 비치는 정도를 확률로
구한다.
 빛이 안 비칠 확률이 높을 수록 어둡게 만든다.
 나름 자연스러운 그림자 완성!
 Variance Shadow Map( VSM )
 그 중에서 깊이 버퍼 그림자 구현 부분을
자세히 알아보자.
 깊이 버퍼 그림자 구현의 기초를 이해하면
나머지 그림자 구현을 이해하는데 도움이
될 것이다.
 깊이 버퍼 그림자는 2패스로 진행된다.
 그림자 맵 생성
▪ 광원의 위치에서 본 장면을 렌더링한다.
▪ 렌더링 할 색은 광원으로 부터의 거리로 한다.
 그림자 맵과 실제 거리 비교 후 그림자 적용
▪ 그림자 맵이 가리키는 픽셀 값과 실제 거리를 비교한
다.
▪ 그림자 맵이 가리키는 픽셀 값이 더 작으면 그림자다.
 그림자 맵 생성
 그림자 맵을 저장하기 위한 텍스쳐를 생성한다.
 그림자 맵 생성시 사용할 깊이 버퍼를 만든다.
 렌더 타겟과 깊이 버퍼를 만든 녀석들로 바꾼다.
 그림자 맵에 맞게 뷰포트를 잡아준다.
 장면을 렌더링 한다.
 뷰포트 , 깊이 버퍼 , 렌더 타겟을 복구한다.
 그림자 맵 생성
 전에 포스트 이펙트 발표 때를 기억하시면 됩니
다.
 이따가 소스를 간단하게 훑어 보겠습니다.
 그림자 맵 생성
 광원에서 본 장면을 렌더링해야 한다.
 장면을 그리기 위해 넘어온 좌표들을 광원을 중심으
로 한 행렬로 변환해야 한다.
 광원을 중심으로 한 행렬 구하기
 월드 행렬 x 뷰 행렬 x 투영 행렬
 광원위치를 기준으로한 뷰 행렬
 광원을 기준으로 적당한 FOV등을 설정한 투영 행렬
 그림자 맵 생성
 버텍스 쉐이더
 버텍스들을 광원을 중심으로한 행렬로 변환한다.
 픽셀 쉐이더에서 깊이값을 저장할 수 있게 변환된 위
치를
넘겨 준다.
 픽셀 쉐이더
 넘어온 변환된 위치를 이용해 깊이 값을 저장시킨다.
 깊이 : z / w ( 0 ~ 1 값 )
 그림자 맵 생성
 그림자 맵 생성
 위의 소스를 보면 z / w 구문이 픽셀 쉐이더에
있음을 알 수 있다.
 버텍스 쉐이더에서 계산을 하는게 더 빨라 보이
지만 값이 불안정해 질 수 있다.
 픽셀 쉐이더에서는 버텍스 사이 사이에 있는 픽
셀들의 값을 채워줘야 한다.
 그림자 맵 생성
 우리가 원하는 깊이의 저장은 이런 모습이다.
 두 버텍스 p0 = ( z0 , w0 ) , p1 = ( z1 , w1 )가 있다고
하고 이 버텍스들의 중심 부분을 얻어온다고 생각해
보자.
 z’ = ( ( z0 + z1 ) / 2 )
( ( w0 + w1 ) / 2 )
 하지만 버텍스 쉐이더에서 먼저 계산을 하고 넘
긴다면? 오차가 날 것이다.
 z’ = ( ( z0 ) + ( z1 ) ) * 1
( ( w0 ) ( w1 ) ) 2
 그림자 맵 적용
 퍼스펙티브 보정에 의한 보간에서는 비선형 깊
이가 바르게 보간 되지 않는단다…
 비선형 깊이가 무엇인가 하면 투영행렬까지 거
친 위치정보는 원근처리가 되어있기 때문에 깊
이값을 구하면 선형적인 값이 나오지 않는다.
 그렇기 때문에 깔끔하게 픽셀쉐이더에서 그 순
간 깊이를 계산해서 저장하는 것이다.
 그림자 맵 적용
 그렇단 말은 선형적인 값을 갖는 깊이를 버텍스
쉐이더에서 계산할 수 있으면 그냥 픽셀 쉐이더
에 넘겨줘도 된다는 말이다.
 선형 깊이를 구하는 식은
 z’ = ( z - zn ) * ( zf )
( zf - zn ) ( z )
 그림자 맵 적용
( 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 )
 그림자 맵과 실제 거리 비교 후 그림자 적용
 그림자 맵을 만들었으니 그림자 맵을 이용하자.
 렌더링할 때 그림자 맵을 매핑할 수 있어야 한
다.
 그림자 맵 : 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
 그림자 맵과 실제 거리 비교 후 그림자 적용
 위의 변환식을 행렬로 묶으면?( -1 , 1 ) -> ( 0 ,
1 )
 ( 0.5 0 0 0.5 )
( 0 -0.5 0 0.5 )
( 0 0 1 0 )
( 0 0 0 1 )
 그림자 맵과 실제 거리 비교 후 그림자 적용
 투영했을 때 ( 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 )
 그림자 맵과 실제 거리 비교 후 그림자 적용
X = 1 , Y = 1 , W = SHADOW_MAP_SIZE
 그림자 맵과 실제 거리 비교 후 그림자 적용
 그림자 맵과 실제 거리 비교 후 그림자 적용
 조명 처리를 위한 부분은 제외하고 보면
 Out.ShadowMapUV = mul(Pos, mWLPB);
 그림자 맵을 입히기 위해서 변환한다.
 Out.Depth = mul(Pos, mWLP);
 그림자 맵의 값과 거리를 비교하기 위해 변환한다.
 그림자 맵과 실제 거리 비교 후 그림자 적용
 그림자 맵과 실제 거리 비교 후 그림자 적용
 거리비교 부분
 그림자 맵의 값을 얻어온다.
 현재 투영된 녀석과 거리를 맞추기 위해 w값을 곱한
다.
 그 값을 z값과 비교한다.
 Bias를 막기 위해 0.03이라는 마법의 숫자를 넣었다.
 그림자 맵의 깊이보다 넘어온 녀석이 크면 그림자!
 아니면 그냥 텍스쳐 디퓨즈 색.
 최종적으로 조명계산까지 덧 붙인다.
 http://ko.wikipedia.org/wiki/%EA%B7%B8%EB%A6%BC%EC%9E%90 - 그림자
 http://allosha.tistory.com/ - 알로샤님 블로그
 http://en.wikipedia.org/wiki/Shadow_volume - Stencil Shadow Volume Wiki
 http://x66vx.egloos.com/3808794 - PSM ~ PPSM까지 정리해 주셨음.
 http://www-sop.inria.fr/reves/Marc.Stamminger/psm/ PSM 정리
The End

Depth buffershadow

  • 1.
  • 2.
     그림자란?  3D에서의그림자  그림자 구현의 역사  깊이 버퍼 그림자 구현  시연  Q & A  참고자료
  • 3.
     그림자 :빛이 지나가는 경로에 불투명한 물체가 존재하여 빛이 통과하지 못해 생기 는 어두운 부분.  물체의 가장자리를 지나가는 빛은 회절 현 상에 의해 물체를 넘어갈 수도 있어서 가장 자리 부분은 비교적 밝은 그림자가 생긴다.
  • 4.
    반 그림자 :흐릿하다. 본 그림자 : 진하다.
  • 5.
     3D에서는 그림자를크게 2가지로 나눌 수 있다. 그림자 음영
  • 6.
     음영은 보통Lighting 계산으로 구한다.  특별한 처리를 하지 않아도 기본적인 파이프라인 에서 음영은 제공해 준다.  그림자는 애초부터 따로 구현해야 하는 부분이였 다.
  • 7.
     Circle Shadow Projected Shadow Mapping  Stencil Shadow Volume  Depth Buffer Shadow  Perspective Shadow Map( PSM )  Light Space Perspective Shadow Map( LSPSM )  Cascaded LSPSM  Soft Shadow  Variance Shadow Map( VSM )  3D 그래픽스 매니악스를 훑어보자!
  • 8.
     Circle Shadow 캐릭터의 위치를 계산하여 발 밑 부분에 원 형의 그림자 텍스쳐를 적용시켜준다.  다른 캐릭터들도 원형의 그림자를 갖게 되 어 리얼리티가 떨어진다.
  • 9.
    캐릭터의 위치정도는 파악할 수있게 해준다.  Circle Shadow
  • 10.
     심플하게 생각해보자. 머리 위에서 캐릭터를 향해 빛이 비친다.  이 때 쉽게 그림자를 생성하는 법은?  캐릭터의 y좌표를 땅의 높이로 두고 검은색으 로 렌더링한다!
  • 11.
  • 12.
     심플하게 생각해보자. 빛이 다른 방향에서 비춘다면?
  • 13.
  • 14.
     Projected ShadowMapping  앞의 아이디어를 기본으로 나온 기법이다.  광원에서 본 오브젝트의 실루엣을 그림자 텍스쳐에 저장한다.  광원방향으로 투영하듯 텍스쳐를 매핑한 다.
  • 15.
     Projected ShadowMapping 캐릭터 형태가 나온다!
  • 16.
     Projected ShadowMapping  그림자를 생성하고 적용하는 과정에서 별 다른 연산이 없으므로 호환성이 뛰어나다.  캐릭터의 모습을 적용할 수 있다.  매 프레임마다 그림자를 갱신해 줘야 하는 이슈가 있지만 멀리 있는 녀석의 그림자는 생략하는 등 트릭으로 극복가능.
  • 17.
     Projected ShadowMapping  문제점 1 : 셀프 그림자가 적용되지 않는다. 이런 것 들 이런 것 들 이런 것 들
  • 18.
     Projected ShadowMapping  문제점 1 : 셀프 그림자가 적용되지 않는다.  왜냐하면 그림자가 오브젝트 단위로 생성 되기 때문이다.  그렇기 때문에 오브젝트 자신의 차폐를 계 산하기 어렵다.
  • 19.
     Projected ShadowMapping  문제점 2 : 투영을 잘 시켜야 한다.
  • 20.
     Projected ShadowMapping  문제점 2 : 투영을 잘 시켜야 한다.  계산을 잘 못하면 벽 넘어에 그림자가 생기 는등 버그가 발생 할 수 있다.
  • 21.
     Stencil ShadowVolume  셀프 쉐도우를 위해서는 광원을 기준으로 차폐 여부를 가지고 그림자를 적용해야 한 다.
  • 22.
     Stencil ShadowVolume 셀프 쉐도우 볼륨
  • 23.
     Stencil ShadowVolume  일단 씬을 렌더링하고 깊이정보를 저장한다.  오브젝트의 외곽에 있는 버텍스를 광원방향 으로 잡아 늘린다.  앞서 저장한 깊이정보와 그림자 볼륨을 비교 하여 그림자인지 아닌지를 스텐실 버퍼에 기 록한다.  최종 렌더링시에 스텐실 버퍼를 참고하여 그 림자를 그린다.
  • 24.
     Stencil ShadowVolume 그림자 볼륨 중 앞에 면만 스텐실 버퍼에 1로 기록한다. *계산 전에 스텐실 버퍼를 0으로 초기화 해두자!
  • 25.
     Stencil ShadowVolume 볼륨의 뒷면은 -1로 그려준다.
  • 26.
     Stencil ShadowVolume 1인 부분을 그림자로 보면 된다.
  • 27.
     Stencil ShadowVolume  광원이 여러 개가 있거나 오브젝트가 서로 겹치는 등 경우에 따라서 스텐실 버퍼의 값 이 1보다 클 수도 있다.  0이 아니면 그림자로 생각해도 무방하다!
  • 28.
     Stencil ShadowVolume  깊이정보를 이용해서 차폐를 계산했기 때 문에 셀프쉐도우가 가능해 졌다.  기타 다른 그림자의 문제도 해결된 듯 보인 다!
  • 29.
     Stencil ShadowVolume  구현을 보면 알겠지만 그림자를 처리할 때 폴리곤 단위로 처리를 한다.  아까는 오브젝트가 그림자 단위였는데 폴 리곤 단위니 전보다 세밀해 졌다!
  • 30.
     Stencil ShadowVolume 잎사귀 텍스쳐도 어차피 네모판일 뿐…
  • 31.
     Stencil ShadowVolume 그림자가 사각형이다.
  • 32.
     Stencil ShadowVolume  볼륨을 생성하는 부분도 생각을 해봐야 한 다.  잘못 늘리면 그 만큼 그림자가 잘못 생성된 다.
  • 33.
     Stencil ShadowVolume  그리고 원래 캐릭터의 모양을 유지하면서 정점을 늘려야 하기 때문에 늘릴 정점이 따 로 필요하다.  보이는 것보다 더 많은 정점이 쓰이게 된다.
  • 34.
  • 35.
     Stencil ShadowVolume  하지만 이전 Direct10 스터디 때도 나왔듯 이 정점을 생성해주는 쉐이더가 추가 되었 기 때문에 위의 문제점은 해결이 될 듯 하 다.
  • 36.
     Depth BufferShadow  위의 Stencil Shadow Volume도 문제가 있 다.  그런데 GPU는 발전해가고 픽셀 계산능력 이 올라가면서 Depth Buffer Shadow 기법 이 사용되기 시작했다.
  • 37.
     Depth BufferShadow  광원을 기준으로 오브젝트들과의 거리( 깊 이 )를 저장한다.( 그림자 맵 )  실제 렌더링 시에 다시 광원과 오브젝트의 거리를 비교하고 위에서 저장해둔 차폐 정 보를 비교하여 그림자를 판별하고 적용한 다.
  • 38.
     Depth BufferShadow 광원의 시점에서 깊이가 저장되면 노란색 부분은 기록되고 주황색 부분은 묻히게 된다. 위에서 무시된 부분이 그림자가 됨을 알 수 있다.
  • 39.
     Depth BufferShadow 위에서 저장된 값은 결국 광원이 도달하는 부분이다. 실제 렌더링할 때 다시 한번 광원과 오브젝트의 거리를 재서 위에 저장한 값과 비교해 보고 그림자인지 아닌지 판단한다. D > S : 그림자 D == S : 그림자 아님
  • 40.
     Depth BufferShadow  차폐 정보를 이용해 처리한 것이니까 셀프 쉐도우가 가능하다.  아까는 폴리곤 단위였지만 지금은 픽셀 단 위이므로 위의 문제도 해결된다.
  • 41.
     Depth BufferShadow  모든 구현에는 문제점이 존재한다.  픽셀 단위의 구현으로 인해 그림자 맵의 해 상도가 적으면 그만큼 그림자의 세밀함이 떨어지게 된다.
  • 42.
     Depth BufferShadow 픽셀 돋네..
  • 43.
     Depth BufferShadow  해결방안은??  그림자 맵의 해상도를 높인다. -> 한계가 있다.  요즘 게임 특히 야외를 보여주는 게임은 점점 먼 곳 까지 보여주기 때문에 커버할 수 없다.  그래도 해상도만 괜찮으면 쓸만한 그림자니까 개량해서 사용해야 겠다!
  • 44.
     Perspective ShadowMap( PSM )  그림자 맵 해상도 이슈가 발생하는 이유는 간단하다.  게임 상의 오브젝트들을 모두 그림자 맵에 담기 때문에 넓을 수록 한 텍셀이 표현해야 할 그림자 영역이 점점 넓어지다 보니 품질 이 떨어지게 된다.
  • 45.
     Perspective ShadowMap( PSM )  위의 구현 방법은 맵이 100m x 100m면 전 체를 그림자 맵에 담아서 사용하게 된다.  텍스쳐 사이즈가 256 x 256이면 한 텍셀은 40cm x 40cm가 된다고 보면 된다.
  • 46.
     Perspective ShadowMap( PSM )  시점이 가까운 곳이나 먼 곳이나 동일한 해 상도를 사용하게 된다. 먼 곳에 있는 녀석 들은 자세히 그릴 필요가 없지 않은가?  가까운 녀석에게 해상도를 집중하고 먼 녀 석은 대충 그려주자가 PSM의 목적이다.
  • 47.
     Perspective ShadowMap( PSM )  시점에서 가까운 곳과 먼 곳은 카메라에 의 해 결정된다. ( View )  가까운 물체는 커보이고 멀리 있는 물체는 작아보인다. ( Projection )  View x Projection 변환된 녀석을 그림자 맵 으로 뽑아내자!
  • 48.
     Perspective ShadowMap( PSM ) 기존에 쉐도우 맵을 뽑을 때완 달리 가까운 녀석의 비중이 더 커지게 그림자 맵을 생성할 수 있다.
  • 49.
  • 50.
  • 51.
     Perspective ShadowMap( PSM )  기존의 그림자 맵보다 효율적으로 만들 수 있어서 더 매끄러운 그림자가 나온다.  이런 부분만 보면 완벽할 것 같다!!
  • 52.
     Perspective ShadowMap( PSM )  PSM은 광원을 기준으로 그림자 맵을 생성 한다. 따라서 광원에선 멀리 있는 그림자를 카메라 시점에서는 가까이 볼 수도 있다.  이 때는 마찬가지로 그림자의 품질이 떨어 진다.
  • 53.
     Perspective ShadowMap( PSM )  PSM은 광원을 기준으로 그림자 맵을 생성 한다. 따라서 광원을 기준으로 보이는 녀석 들에 대해서만 그림자 맵을 만들게 된다.  오브젝트는 보이지 않지만 그림자만 보여 야 하는 상황에서는 올바르게 그림자가 생 성되지 않을 수 있다.
  • 54.
     Light SpacePerspective Shadow Map( LSPSM )  PSM의 문제를 해결하기 위해 개량된 방법을 제 시하였다. 그것이 바로 LSPSM이다.  위의 문제를 정리하면 광원 기준의 그림자 생성 과 보이지 않는 오브젝트가 있을 수 있다는 것이 다.
  • 55.
     Light SpacePerspective Shadow Map( LSPSM )  하지만 저런 특정 상황을 제외하고는 그림자 맵 을 카메라에 맞게 옮겨서 생성하는 아이디어는 괜찮은 것 같다.  그렇다면 위의 문제를 해결하면서 그림자 맵을 옮겨서 생성하자!  그림자 맵 생성을 위한 좌표계를 만들어 내고 그림자를 생성하자!
  • 56.
     Light SpacePerspective Shadow Map( LSPSM )  그림자를 생성할 가능성이 있는 오브젝트는 모두 포함시키게 시야 거리를 조정한다.  최대한의 해상도를 구하기 위해 최대한 가까운 곳에서 그림자 맵을 뽑아낸다.  좌표계는 다음과 같은 방식으로 생각해 볼 수 있 다.
  • 57.
     Light SpacePerspective Shadow Map( LSPSM )  빛의 방향의 반대되는 방향을 Y축  뷰 벡터와 Y축을 외적해서 구한 방향을 X축  X축과 Y축을 외적해서 구한 방향을 Z축
  • 58.
     Light SpacePerspective Shadow Map( LSPSM ) PSM은 뒷 부분 그림자의 품질이 낮다.
  • 59.
     Light SpacePerspective Shadow Map( LSPSM ) LSPSM은 뒷 부분 그림자까지 제대로 그려준다.
  • 60.
     Cascaded LSPSM LSPSM을 가지고 좋은 그림자를 생성해 냈으나 점점 더 야외 맵은 넓어지고 멀리 보이게 되고 있 다.  LSPSM을 이용해 처리를 해도 마찬가지로 먼 곳 까지는 텍스쳐 해상도의 한계에 부딪치게 된다.
  • 61.
     Cascaded LSPSM 해상도에 한계가 있어서 계속 그림자 맵을 효율 적으로 생성하는 것에만 신경을 쓰다가 그림자 맵을 여러 개 두면 되지 않을까? 로 생각이 넘어 오게 된다.  지금까지 구현을 보아 가까운 녀석에게는 그림자 해상도를 많이 투자하는게 맞고 먼 녀석에게는 적게 투자하더라도 어느정도는 투자가 되어야 한 다.
  • 62.
     Cascaded LSPSM 근거리용 그림자 맵( 가까운 물체의 해상도는 높 게 )  중거리용 그림자 맵( 중간 물체도 위보단 높게 )  장거리용 그림자 맵( 멀리있는 물체도 최소한 보 이게 )  이런식으로 나누면 되겠구나!  물론 게임마다 거리를 나누는 기준과 몇 단계로 나눌 것이냐는 따로 정해야 한다.
  • 63.
  • 64.
  • 65.
     Soft Shadow 지금까지 그림자 맵을 찍는 부분에 대해서 만 생각해 왔다.  문제는 그림자 품질이 떨어지는 현상이다.  Post Processing으로 해결할 수 있지 않을 까?
  • 66.
     Soft Shadow 계단현상이 생긴 부분을 뭉게버리자!  뭉게지면 부드러워 지고 실제 그림자의 뿌 연 느낌도 줄 수 있을 것 같다!
  • 67.
     Soft Shadow 뭉게기전 뭉게기 후 그림을 확대해서 둘 다 뭉게진 듯 하지만 자세히 보면 보인다.
  • 68.
     Soft Shadow 뭉게기전 뭉게기 후
  • 69.
     Soft Shadow Post Processing이다 보니 격렬하게 뭉게 면 3D모델 렌더링에 영향을 끼칠 수 있다.  Soft Shadow역시 이것 저것 개량해서 쓰면 좋은 퀄리티를 낼 수 있겠다. ( 이건 다음 에.. )
  • 70.
     Variance ShadowMap( VSM )  언리얼 엔진 3.0에서 사용한다고 한다.  그림자 맵에 광원에서 오브젝트까지의 거 리와 그 제곱값을 저장한다.  이 값을 이용해 그림자가 생길 확률을 계산 하고 그림자를 입혀준다.
  • 71.
     Variance ShadowMap( VSM )  VSM을 위한 그림자 맵을 만들기 위해서는 부동 소수점을 저장하는 텍스쳐를 이용해 야 한다.  이 기법을 이용하면 소프트 쉐도우와 같은 구현이 된다고 한다.
  • 72.
     Variance ShadowMap( VSM )  기존의 그림자 맵은 그림자다 아니다.  0 아니면 1 이런 느낌.  반면 VSM은 체비쇼프 부등식을 이용하여 깊 이값을 기반으로 빛이 비치는 정도를 확률로 구한다.  빛이 안 비칠 확률이 높을 수록 어둡게 만든다.  나름 자연스러운 그림자 완성!
  • 73.
  • 74.
     그 중에서깊이 버퍼 그림자 구현 부분을 자세히 알아보자.  깊이 버퍼 그림자 구현의 기초를 이해하면 나머지 그림자 구현을 이해하는데 도움이 될 것이다.
  • 75.
     깊이 버퍼그림자는 2패스로 진행된다.  그림자 맵 생성 ▪ 광원의 위치에서 본 장면을 렌더링한다. ▪ 렌더링 할 색은 광원으로 부터의 거리로 한다.  그림자 맵과 실제 거리 비교 후 그림자 적용 ▪ 그림자 맵이 가리키는 픽셀 값과 실제 거리를 비교한 다. ▪ 그림자 맵이 가리키는 픽셀 값이 더 작으면 그림자다.
  • 76.
     그림자 맵생성  그림자 맵을 저장하기 위한 텍스쳐를 생성한다.  그림자 맵 생성시 사용할 깊이 버퍼를 만든다.  렌더 타겟과 깊이 버퍼를 만든 녀석들로 바꾼다.  그림자 맵에 맞게 뷰포트를 잡아준다.  장면을 렌더링 한다.  뷰포트 , 깊이 버퍼 , 렌더 타겟을 복구한다.
  • 77.
     그림자 맵생성  전에 포스트 이펙트 발표 때를 기억하시면 됩니 다.  이따가 소스를 간단하게 훑어 보겠습니다.
  • 78.
     그림자 맵생성  광원에서 본 장면을 렌더링해야 한다.  장면을 그리기 위해 넘어온 좌표들을 광원을 중심으 로 한 행렬로 변환해야 한다.  광원을 중심으로 한 행렬 구하기  월드 행렬 x 뷰 행렬 x 투영 행렬  광원위치를 기준으로한 뷰 행렬  광원을 기준으로 적당한 FOV등을 설정한 투영 행렬
  • 79.
     그림자 맵생성  버텍스 쉐이더  버텍스들을 광원을 중심으로한 행렬로 변환한다.  픽셀 쉐이더에서 깊이값을 저장할 수 있게 변환된 위 치를 넘겨 준다.  픽셀 쉐이더  넘어온 변환된 위치를 이용해 깊이 값을 저장시킨다.  깊이 : z / w ( 0 ~ 1 값 )
  • 80.
  • 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
  • 90.
     그림자 맵과실제 거리 비교 후 그림자 적용
  • 91.
     그림자 맵과실제 거리 비교 후 그림자 적용  조명 처리를 위한 부분은 제외하고 보면  Out.ShadowMapUV = mul(Pos, mWLPB);  그림자 맵을 입히기 위해서 변환한다.  Out.Depth = mul(Pos, mWLP);  그림자 맵의 값과 거리를 비교하기 위해 변환한다.
  • 92.
     그림자 맵과실제 거리 비교 후 그림자 적용
  • 93.
     그림자 맵과실제 거리 비교 후 그림자 적용  거리비교 부분  그림자 맵의 값을 얻어온다.  현재 투영된 녀석과 거리를 맞추기 위해 w값을 곱한 다.  그 값을 z값과 비교한다.  Bias를 막기 위해 0.03이라는 마법의 숫자를 넣었다.  그림자 맵의 깊이보다 넘어온 녀석이 크면 그림자!  아니면 그냥 텍스쳐 디퓨즈 색.  최종적으로 조명계산까지 덧 붙인다.
  • 96.
     http://ko.wikipedia.org/wiki/%EA%B7%B8%EB%A6%BC%EC%9E%90 -그림자  http://allosha.tistory.com/ - 알로샤님 블로그  http://en.wikipedia.org/wiki/Shadow_volume - Stencil Shadow Volume Wiki  http://x66vx.egloos.com/3808794 - PSM ~ PPSM까지 정리해 주셨음.  http://www-sop.inria.fr/reves/Marc.Stamminger/psm/ PSM 정리
  • 97.