[DirectX] DirectX 뷰포트 설정하기!!

이제 기본 화면을 설정했으면, 뷰포트를 한번 설정해보자...

사실 게임을 만든다면 딱히 뷰포트가 필요없다.. 뷰포트라고 하면 랜더링이 찍힐 화면의 범위라고 생각하면된다. 기본적으로 랜더링을 하면 화면 전체에 찍히므로, 화면 전체를 보고 하는 게임에는 필요가 없다.

 하지만 개념적인 이해를 위해 한번 설정하는 법을 알아보자.

 이전에 초기화 하는 부분을 참고하여, 그 이후부터 소스를 하나씩 추가해 보도록 하자.

이전 포스팅 보기

 이번 예제에서는 간단히 띄워볼 주전자를 준비하면된다. (프로젝트 폴더내에
teapot.x파일을 미리 넣어두자. 물론 경로를 다르게 해도 되지만, 그렇게 되면 설정도 다시 해줘야 하므로 그냥 프로젝트 폴더 내에 두는 것이 편하다.)

이제 아래 사진을 보면서 차근차근 하나씩 해보자.


위의 변수들을 더 추가해준다.

LPD3DXMESH        g_pTeapotMesh = NULL;
D3DMATERIAL9      g_teapotMtrl;
D3DLIGHT9         g_pLight0;

DWORD g_dwBackBufferWidth  = 0;
DWORD g_dwBackBufferHeight = 0;

여기서 pTeapotMesh 가 아까 넣어주었던 주전자.x 파일용 변수이다. 다이렉트x는 기본적으로 메쉬 파일인 .x 파일 로드를 지원해 준다. 그래서 다른 모델링이있다면 그 것을 써도 된다. 그리고 아래의 메터리얼과 라이트는 현재, .x 모델에 아무런 텍스쳐가 없기때문에 그냥 띄우면 검게만 나올 뿐이다. 그렇기 때문에 재질 정보와 거기에 따른 빛을 넣어주는 것이다. 그리고 폭은 간단히 화면을 2분할 하기 위해 필요한 정보이기 때문에 크게 신경쓰지 않아도 된다.


그리고 init 함수를 찾아가서...

 

위와 같이 아래의 내용을 추가해준다.

 D3DXMATRIX matProj;
 D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian( 45.0f ),
  640.0f / 480.0f, 0.1f, 100.0f );
 g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

 g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
 g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
 g_pd3dDevice->SetRenderState( D3DRS_SPECULARENABLE, TRUE );

 //   // Setup a simple directional light and some ambient...
 g_pLight0.Type = D3DLIGHT_DIRECTIONAL;
 g_pLight0.Direction = D3DXVECTOR3( 1.0f, 0.0f, 1.0f );

 g_pLight0.Diffuse.r = 1.0f;
 g_pLight0.Diffuse.g = 1.0f;
 g_pLight0.Diffuse.b = 1.0f;
 g_pLight0.Diffuse.a = 1.0f;

 g_pLight0.Specular.r = 1.0f;
 g_pLight0.Specular.g = 1.0f;
 g_pLight0.Specular.b = 1.0f;
 g_pLight0.Specular.a = 1.0f;

 g_pd3dDevice->SetLight( 0, &g_pLight0 );
 g_pd3dDevice->LightEnable( 0, TRUE );

 g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_COLORVALUE( 0.2f, 0.2f, 0.2f, 1.0f ) );

 // Setup a material for the teapot
 ZeroMemory( &g_teapotMtrl, sizeof(D3DMATERIAL9) );

 g_teapotMtrl.Diffuse.r = 1.0f;
 g_teapotMtrl.Diffuse.g = 1.0f;
 g_teapotMtrl.Diffuse.b = 1.0f;
 g_teapotMtrl.Diffuse.a = 1.0f;

 // Load up the teapot mesh...
 D3DXLoadMeshFromX( L"teapot.x", D3DXMESH_SYSTEMMEM, g_pd3dDevice,
  NULL, NULL, NULL, NULL, &g_pTeapotMesh );

 //
 // Cache the width & height of the back-buffer...
 //

 LPDIRECT3DSURFACE9 pBackBuffer = NULL;
 D3DSURFACE_DESC d3dsd;
 g_pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
 pBackBuffer->GetDesc( &d3dsd );
 pBackBuffer->Release();
 g_dwBackBufferWidth  = d3dsd.Width;
 g_dwBackBufferHeight = d3dsd.Height;

위의 내용을 간단히 설명하면, 먼저 프로젝션 행렬 설정과, 그 뒤에는 .x 파일 로드와 재질, 빛에 대한 속성정리이다. 그리고 랜더 스테이트들을 설정해서 빛을 사용하고, ZWrite를 하고 하는 등의 속성을 설정했다. 마지막으로는 현재 만들어진 백버퍼를 얻어와 그 크기를 입력받고 다시 해제하는 부분이 들어있다.


 if( g_pTeapotMesh != NULL )
  g_pTeapotMesh->Release();

 위의 부분은 그렇게 로드된 주전자 매쉬를 해제하는 부분이다. 왜 초기화 선언 다음 작업으로 해제를 넣냐면, 가급적이면 초기화를 하는 순간 해제를 같이 넣어야 잊지 않기 때문이다. 물론, 현재 이정도 예제에서 주전자 해제 하나 안했다고 문제가 있진 않겠지만, 이런 프로그램 습관 하나하나가 쌓여야 나중에 큰 프로젝트에서 해제를 안해서 프로그램을 죽이는 불상사를 예방할 수 있는 것이다.


그리고 이제 랜더 부분을...

 

 D3DXMATRIX matView;
 D3DXMATRIX matWorld;
 D3DXMATRIX matRotation;
 D3DXMATRIX matTranslation;

 //
 // Render to the left viewport
 //

 D3DVIEWPORT9 leftViewPort;

 leftViewPort.X      = 0;
 leftViewPort.Y      = 0;
 leftViewPort.Width  = g_dwBackBufferWidth / 2;
 leftViewPort.Height = g_dwBackBufferHeight;
 leftViewPort.MinZ   = 0.0f;
 leftViewPort.MaxZ   = 1.0f;

 g_pd3dDevice->SetViewport( &leftViewPort );

 // Now we can clear just view-port's portion of the buffer to red...
 g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
  D3DCOLOR_COLORVALUE( 1.0f, 0.0f, 0.0f, 1.0f ), 1.0f, 0 );

 g_pd3dDevice->BeginScene();
 {
  // For the left view-port, leave the view at the origin...
  D3DXMatrixIdentity( &matView );
  g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

  // ... and use the world matrix to spin and translate the teapot 
  // out where we can see it...
  D3DXMatrixTranslation( &matTranslation, 0.0f, 0.0f, 5.0f );
  matWorld = matTranslation;
  g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

  g_pd3dDevice->SetMaterial( &g_teapotMtrl );
  g_pTeapotMesh->DrawSubset(0);
 }
 g_pd3dDevice->EndScene();

 //
 // Render to the right viewport
 //

 D3DVIEWPORT9 rightViewPort;

 rightViewPort.X      = g_dwBackBufferWidth / 2;
 rightViewPort.Y      = 0;
 rightViewPort.Width  = g_dwBackBufferWidth / 2;
 rightViewPort.Height = g_dwBackBufferHeight;
 rightViewPort.MinZ   = 0.0f;
 rightViewPort.MaxZ   = 1.0f;

 g_pd3dDevice->SetViewport( &rightViewPort );

 // Now we can clear just view-port's portion of the buffer to green...
 g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
  D3DCOLOR_COLORVALUE( 0.0f, 1.0f, 0.0f, 1.0f ), 1.0f, 0 );

 g_pd3dDevice->BeginScene();
 {
  // For the right view-port, translate and rotate the view around
  // the teapot so we can see it...
  D3DXMatrixTranslation( &matTranslation, 0.0f, 0.0f, 5.0f );
  matView = matTranslation;
  g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

  // ... and don't bother with the world matrix at all.
  D3DXMatrixIdentity( &matWorld );
  g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

  g_pd3dDevice->SetMaterial( &g_teapotMtrl );
  g_pTeapotMesh->DrawSubset(0);
 }
 g_pd3dDevice->EndScene();

 //
 // We're done! Now, we just call Present()
 //

 g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

이렇게 바꿔주면 된다.

소스를 살펴보면, 랜더링에 Begin과 end가 2번있다. 즉, 각각의 뷰포트 마다 한번씩 그려서 2번을 그린것이다.

 그렇다.. 우리가 하는 2개 뷰포트 분할 작업은 각각 따로 랜더링된 화면을 다른 위치에 그리는 것이다.. 머 간단히 미니맵 같은데 응용해 쓰거나, 아니면 머 다른 어떤 곳에 새로 랜더링된 어떤것을 그려야 하면 사용될 수는 있겠지만, 딱히 생각나는 곳은 없다. 요즘은 그냥 Surface에 랜더링을 하고 바로 텍스쳐화 시켜서 화면에 뿌려줄수도 있기 때문이다.

 내용은 보면, 월드 행렬 설정과, 메트리얼 설정 등 간단한 연산만 하고 바로 그렸다. 원래 예제는 마우스 인풋도 해서 주전자를 돌려볼 수도 있지만, 현재 예제는 그 것이 중점이 아니기 때문에 뺴버렸다. 어쩃든, 위의 프로젝트를 랜더링하면 아래 화면이 나온다...


바로 왼쪽과 오른쪽에 서로 다른 화면이 랜더링 된 모습이다.....

뷰포트에 대해서 감이 잡히는가? 머 딱히 어려운 부분은 없으니 이해만 하고 넘어가도록 하고, 이제 진정한 버텍스에 대해 알아보도록 하자..

[DirectX] DirectX 뷰포트 설정하기!!

 

Posted by 바람처럼..
|