프로그래밍 2013. 12. 23. 18:45
728x90

depth buffer shadow - 깊이 버퍼 그림자 (4)

 

<카메라 셋팅>

깊이 버퍼 그림자에서 카메라 셋팅은 매우 중요합니다.
원하는 그림자 모양을 뽑아내기 위해서는
카메라의 위치, 타겟을 주의깊게 셋팅해 줄 필요가 있습니다.

코드를 살펴보겠습니다.
코드에 주석이 포함되어 있습니다.


 

zMatrix -> D3DXMATRIX
Vector3 -> D3DXVECTOR3

zMatrixOrthoLH -> D3DXMatrixOrthoLH

zMatrixLookAtLH -> D3DXMatrixLookAtLH

로 치환하시면 됩니다.


zMatrix g_matLightView;
zMatrix g_matLightProj;

 

Vector3 cam_dir = QManagerActor::_mpCamera->GetDir(); //카메라 방향
Vector3 cam_pos = QManagerActor::_mpCamera->GetCamEyePos(); //카메라 위치

 

Vector3 light_at;
Vector3 light_pos;
Vector3 vLightDir;

 

//라이트의 방향을 바꾸어 주고 싶으면 이값을 바꾸어 주면 됩니다.
vLightDir.x = vLightDir.y = vLightDir.z = 1.f;

 

light_at = cam_pos;
light_pos = light_at;

light_pos.x += 500.f * vLightDir.x;
light_pos.z += 500.f * vLightDir.z;

light_pos.y += 500.f;

 

//perspective 에서는 카메라 거리에 따라 원근에 따라 오브젝트의 끝이 좁아지는듯한 현상이 있다

(당연하지만)
//이것은 카메라 거리가 다른 perspective 뷰에서는 꽤 큰 그림자 외형의 차이가 만들어진다.
//ortho 투영은 카메라 거리에 따른 그림자 외형 변화가 없다.
//그림자를 찍어내기 위한 거리가 다른, 여러개의 카메라를 사용할 때 유리하다.
//카메라 거리에 따라 그림자가 바뀔때 티가 잘 나지 않는다.
zMatrixOrthoLH( &g_matLightProj, 500, 500, 1.f, 10000.f );
zMatrixLookAtLH( &g_matLightView, &light_pos, &light_at, &Vector3(0.f, 1.f, 0.f) );

 

카메라 방향이 바뀌어도, 항상 일정한 빛의 위치를 잡기 위해서
(태양빛처럼 항상 고정된 위치에 라이트 위치를 셋팅해야,
카메라 방향이 바뀌어도 그림자가 바뀌지 않겠지요.)
먼저 라이트 카메라 타겟의 위치를 구한 후
거기에 얼마간의 거리값을 더해 주어 라이트 카메라 위치를 구해주었습니다.

 

이렇게 하면, 카메라의 방향이 계속 바뀌어도
라이트는 항상 일정한 곳에서 비춰지게 되어,
생성되는 그림자의 방향이 늘 일정해지게 됩니다.

 

만들어진 g_matLightView, g_matLightProj 는
그림자를 그릴 쉐이더와
그림자가 드리워질 오브젝트를 렌더링할 쉐이더에
전달해 주어야 합니다.

 

pEffect->GetEffect()->SetMatrix( "_matLightView", &g_matLightView );
pEffect->GetEffect()->SetMatrix( "_matLightProj", &g_matLightProj );
이런 식이 되겠지요.

 

그림자 생성 과정을 다시 요약해 보겠습니다.


1. 라이트 카메라 생성(위 코드 부분입니다. 라이트 뷰와 라이트 프로젝션 매트릭스를 생성해 두어야 합니다. )
2. 렌더타겟을 준비
3. 렌더타겟에 그림자를 드리울 오브젝트->render(), ( 쉐이더에 g_matLightView, g_matLightProj 를 전달 )
4. 렌더타겟을 종료
5. 그림자가 드리워질 오브젝트의 렌더링 ( 쉐이더에 g_matLightView, g_matLightProj 를 전달 )

 

3번 과정에서 g_matLightView, g_matLightProj 를 전달받아
라이트뷰에 그림자를 그리는 쉐이더 코드는
깊이 버퍼 그림자 (3) 에 설명되어 있습니다.


fps 뷰에서 하나의 라이트뷰만을 사용하려 하면,
카메라의 거리값을 꽤 많이 주어야 시야에 들어오는 모든 오브젝트들의 그림자를 만들 수 있습니다.
그런데, 이렇게 하면, 그림자의 퀄리티가 많이 떨어지게 됩니다.
마치 도트를 찍은 듯하게....

 

저의 경우는
카메라 거리에 따라 3개의 라이트뷰로 쪼갰습니다.

(근거리, 중거리, 원거리)

 

근거리
   light_at = cam_pos + ( cam_dir * 0.f );
   light_pos = light_at;

   light_pos.x += 500.f * vLightDir.x;
   light_pos.z += 500.f * vLightDir.z;
   light_pos.y += 500.f * vLightDir.y;

  zMatrixOrthoLH( &g_matLightProj, 500, 500, 1.f, 10000.f );

  zMatrixLookAtLH( &g_matLightView, &light_pos, &light_at, &Vector3(0.f, 1.f, 0.f) );

 

중거리
   light_at = cam_pos + ( cam_dir * 700.f );
   light_pos = light_at;

   light_pos.x += 700.f * vLightDir.x;
   light_pos.z += 700.f * vLightDir.z;
   light_pos.y += 700.f * vLightDir.y;

  zMatrixOrthoLH( &g_matLightProj, 1200.f, 1200.f, 1.f, 10000.f );

  zMatrixLookAtLH( &g_matLightView, &light_pos, &light_at, &Vector3(0.f, 1.f, 0.f) );

 

원거리
   light_at = cam_pos + ( cam_dir * 1400.f );
   light_pos = light_at;

   light_pos.x += 1000.f * vLightDir.x;
   light_pos.z += 1000.f * vLightDir.z;
   light_pos.y += 1000.f * vLightDir.y;

  zMatrixOrthoLH( &g_matLightProj, 1500.f, 1500.f, 1.f, 10000.f );

  zMatrixLookAtLH( &g_matLightView, &light_pos, &light_at, &Vector3(0.f, 1.f, 0.f) );


라이트뷰를 나누게 되면,
그림자를 만들기 위한
모든 오브젝트를 각각의 라이트뷰마다 전체적으로 다 렌더링 시켜줄 필요는 없어집니다.

 

시야에 가까운 오브젝트는 근거리 라이트뷰에,
조금 더 먼 오브젝트는 중거리 라이트뷰에,
아주 먼 것들은 원거리 라이트뷰에 렌더링시켜 주면 됩니다.

 

주의할 점은 경계가 딱 맞추어 쪼개어 지지 않은 이상은,

거리에 따라 오브젝트를 나누어 렌더링 할때

조금은 느슨하게 처리해 주는 것이 좋습니다.

중복시켜 렌더링해줄수도 있다는 것입니다. (^^;;;)

이 부분은 테스트를 하면서 조절해 주시면 될 듯 합니다.


각각의 거리값은 실제 렌더링을 해보면서 맞춘값입니다.
위 코드에서는 각각의 라이트뷰 경계가 완벽하게 쪼개어지진 않았습니다만,
(라이트뷰의 경계가 정확하지 않고, 영역이 맞물려 있습니다)
거리값들과 orthoView의 크기를 조절하면 최대한 근접하게 셋팅할 수 있습니다.
광대한 지형을 표현해 줄때는, 라이트뷰의 갯수를 더 많이 늘릴 수도 있겠지요.

 

라이뷰의 경계가 맞물려 지는 부분에서는
같은 오브젝트의 그림자가 각각 들어 있을 수 있기 때문에
그림자가 겹쳐져 그려질 수 있습니다.
겹쳐지지 않게 처리하기 위해서는
근거리에서든, 원거리에서든 먼저 그려지는 픽셀에만
그림자의 뎁스값을 적용해 주면 겹쳐지지 않습니다.

 

겹쳐 그리지 않게 처리하면,
카메라의 거리가 바뀔때
그림자가 거리의 경계( 예를 들어 근거리와 중거리 사이의 경우 )에 물리더라도
도드라지게 어색한 부분은 생기지 않습니다.
( ortho 뷰로 그렸기 때문에, 그림자 외형에 차이가 없기 때문입니다. )
약간의 그림자 퀄리티 차이가 있습니다만,
(근거리가 중거리보다는 좀더 그림자 퀄리티가 좋겠지요)
이 부분은 라이트뷰의 갯수를 더 쪼개면,
해결될 수 있는 부분입니다.
 
저의 테스트로는, 라이트뷰를 3개로 나누었을 때
근거리, 중거리 그림자 경계가 꽤 부드럽게 전환되는 듯이
보였기 때문에 만족할만한 부분이었습니다.

 

3개의 라이트뷰에서 각각 그려진 그림자맵은
그림자가 드리워질 오브젝트를 렌더링하는 쉐이더 코드에서 처리해 주어야 합니다.

이 부분은 다시 설명 드리도록 하겠습니다.

 

 

 

 

728x90
posted by BK dddDang
: