프로그래밍 2016. 1. 26. 06:40
728x90

vsm with cascade shadow map 2

 

 

앞장에서 설명했다시피,

라이트뷰의 넓이와 라이트 포지션의 거리에 따라, 그림자 품질이 결정된다.

근거리에서는, 넓이와 라이트 위치가 작아야 우리가 원하는 그림자의 품질이 표현된다.

 

그런데 게임상에서는 다양한 크기와 다양한 높이값의 오브젝트가 있기 때문에,

원하는 그림자의 품질을 맞추기 위해서는 여러가지 고려사항이 생긴다.

 

여기서,

나공간의 그림자 표현에 대해서 설명해 보겠다.

그림자 표현의 목표는,

어색하지 않게 되도록 자연스러운 그림자 퀄리티를 유지.

캐릭터, 배경 오브젝트, 나무, 아이템, 지형이 모두 동적으로 그림자를 드리우고,

동적으로 그림자가 드리워질수 있는 것이다. 

이러한 목표에 맞추어,

여러가지로 뷰를 나누고, 오브젝트의 크기와 위치에 대해서 실시간으로 라이트뷰를 계산하는 등의 많은 테스트를 진행하였다.

 

현재, 어느 정도 정리가 되었는데,

 

1. 근중거리, 중원거리를 나누어 2개의 라이트뷰를 사용. (근중거리는 웬만한 크기의 오브젝트와 위치를 커버할수 있도록 좀 크게 셋팅)

값은 고정된 값을 사용.

이 라이트뷰에는 캐릭터, 배경 오브젝트, 나무, 아이템, 지형 모두가 렌더링

근중거리와, 중원거리에 draw 되는 요소들은 중복으로 draw 된다.

 

2. 초단거리에 라이트뷰 1개를 셋팅. 이 뷰는 오직 매우 가까운 거리의

캐릭터와 아이템, 배경 오브젝트, 지형만을 처리( 나무 등의 큰 오브젝트는 제외) 

실시간으로, 초단거리에 들어오는 요소들의 위치와 높이값을 참고하여, 라이트뷰의 위치, 크기를 설정. ( 요소들의 월드위치상의 최소점과 최대점을 구해 놓으면, 이 값들을 이용하여 중점을 구할수 있고, 라이트뷰의 위치, 크기를 중점에 대한 거리값 등을 이용하여, 대략적으로 계산할수 있다 )

여기에 그려지는 요소들은 근중거리, 중원거리의 뷰에 굳이 draw 될 필요가 없다.

 

3. 지형은 static 그림자와 같이 사용.

미리 구워둔 static 그림자와 동적 그림자를 같이 사용하는데,

이 두값을 모두 구한후, 그중 작은 값을 그림자 값으로 사용하면 된다.

이렇게 하면, 두개의 그림자가 섞여도 티가 나지 않는다.

 

총 3개의 라이트뷰가 사용되었다.

픽셀 쉐이더 코드를 살펴보자.

 

const float4 Calc_VSM( float4 _WP,
        float4 _depth1, float4 _depth2,
        bool _bFilter,
        float fShadowTexSize,
        float fShadowMomentsVel, float fShadowBias, float fShadowAddBright
         )
{
     float4 vOut = (float4)1.f;
     float c1 = 1, c2 = 1, c_actor = 1;

 
     float fCamDist = distance( _WP, _vWorldCamPos );

     //카메라로부터 너무 먼 것은 무시
     if( fCamDist > 4000 )
          return 1.f;

 

     //먼것은 스무드 처리

     float fLight = (fCamDist - _vShadow.y) / _vShadow.y;
     fLight = max( fLight, 0 );

 

     //제일 가까운 액터들만을 처리
     //_miNearActorShadowIdx
     if( fCamDist < _vShadow.x )
     {
          //_fShadowTexSize, fShadowMomentsVel, fShadowBias, fShadowAddBright
          float4 actor_d = mul( _WP, _matLightVP_3 );
          c_actor = Depth_Shadow_VSM( _WP, actor_d, Samp_Depth[3], _bFilter, 
                   _fShadowTexSize, fShadowMomentsVel, fShadowBias, fShadowAddBright ); 
     }


     //근중거리
     c1 = Depth_Shadow_VSM( _WP, _depth1, Samp_Depth[0], _bFilter, 
           _fShadowTexSize, fShadowMomentsVel, fShadowBias, fShadowAddBright ); 

 

     //중원거리
     c2 = Depth_Shadow_VSM( _WP, _depth2, Samp_Depth[1], _bFilter, 
           _fShadowTexSize, fShadowMomentsVel, fShadowBias, fShadowAddBright ); 
        
 
     //가장 작은 값을 사용한다
     vOut.a = min( min(c1, c2), c_actor ) + fLight;
     vOut.a = min( vOut.a, 1.f );

     return vOut;
}

 

 

vsm with cascade shadow map 스샷

그림자 텍스쳐 사이즈는 1024 와 2048 로 테스트 된 스샷들이 섞여있다.

 

 

 

매우 가까운 거리의 초단거리로 만들어진 라이트뷰이기 때문에,

라이트뷰의 크기가 작고, 따라서

그림자 퀄리티가 매우 훌륭하다!

 

 

 

 

 

 

 

화면 맨왼쪽의 캐릭터 그림자와 중간위치의 캐릭터 그림자의 외형을 비교해 보자.

화면 맨왼쪽은 근중거리뷰로,

화면 중간은 최단거리뷰로 처리되었다.

 

 

 

 

바위 위에 지형의 그림자가 드리워져 있다.

 

 

 

왼쪽의 바위 위에 지형의 그림자가 드리워져 있다, 그림자가 드리우지 않은 오른쪽 바위와 구분된다.

 

 

 

 

 

현재 나공간에 구현된 방식에도 문제점이 있다.

매우 높은 절벽 지형의 경우에는, 근중거리와 중원거리의 라이트뷰의 크기를 벗어나서

그림자로 덮여야될 오브젝트들이 제대로 표현되지 않는다는 것이다.

 

이것은 좀더 큰 영역을 커버할수 있는 라이트뷰를 하나 더 생성해서,

높은 절벽조차도 충분히 커버해 버리면, 해결될 문제점이기는 하다.

 

다음에는

vsm 의 light bleeding 현상( 그림자가 겹칠때 빛이 새는 듯한 현상) 과

카메라라 움직일때마다 발생되는 그림자 흔들림 현상 ( Shimmering shadow edges ) 에 대해서

설명해 보겠다. 

 

 

 

728x90

'프로그래밍' 카테고리의 다른 글

obb 충돌 구현 2  (0) 2016.02.18
obb 충돌 구현 1  (2) 2016.02.18
vsm with cascade shadow map 1  (0) 2016.01.25
vsm 지형 그림자  (0) 2016.01.14
vsm 그림자  (1) 2015.10.13
posted by BK dddDang
: