E D R S I H C RSS
ID
Password
Join
낮에는 너무 바빠 근심이 없고, 밤에는 너무 졸려 걱정할 겨를이 없는 사람은 행복한 사람. ―L.A.



Contents

1 시작전에
2 설치
3 개요
4 1 단계 : 윈도우 생성
5 2 단계 : D3D 초기화
6 3 단계 : 시스템 메세지 다루기
7 4 단계 : 장면 랜더링 및 화면에 표시하기
8 5 단계 : 셧다운
9 덧붙이기 : 풀스크린 여부 선택
10 마치며

1 시작전에 #

이 글을 읽으시는 분은 아마도 DX를 처음 시작하시는 분들이라 생각합니다. 비록 MSDN에 있는 날림 튜토리얼 번역이지만, 다소 빨리 읽어버리고 파악하실 수만 있다면 그나마 제 역할 다한거라 생각하려합니다. ^_^; 항상 명심하실 것은, API에 대한 내용은 도움말이 짱입니다. (즉, 영어가 딸리시더라도 MSDN을 읽어보시는 것이 좋습니다.) DX에 대한 시작이 순조롭기를 바랍니다.

2 설치 #

DX8을 설치하는 방법은 두가지가 있습니다.

  1. [http]http://msdn.microsoft.com/directx/ 에서 잘 찾아보시면, 8.1 SDK 다운로드를 얻으실 수 있습니다. 이것을 받아서 설치합니다. (아래의 2번처럼 런타임을 설치할 필요는 없습니다.)
  2. [http]http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ 에서 플렛폼 SDK 업데이트를 통하여 업그레이드합니다. 그다음, 꼭 [http]http://www.microsoft.com/windows/directx/downloads/drx81.asp를 들러 DirectX 8.1 런타임을 설치해주셔야 합니다. (이 방법은 Prelease 버젼이라고 되어있어서 VC include/lib 경로를 이중으로 지정해야하는 불편함은 있습니다.)

3 개요 #

D3D를 사용하기 위해서는 먼저 어플리케이션 윈도우를 생성해야한다. 그러면 D3D 객체들을 생성하고 초기화할 수 있다. You use the COM interfaces that these objects implement to manipulate them and to create other objects required to render a scene. 이 튜토리얼의 CreateDevice 샘플 프로젝트는 D3D 디바이스를 생성하고 푸른 화면을 랜더링하기 위한 작업들을 나타내고 있다.

이 튜토리얼은 D3D를 초기화 하고, 장면을 랜더링 한다음, 셧다운하는 단계를 설명한다.

이 튜토리얼의 예제는 (SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut01_CreateDevice에서 얻을 수 있다.

4 1 단계 : 윈도우 생성 #

어떤 윈도우 어플리케이션이라도 가장 먼저 실행해야 하는 것은 사용자에게 나타나는 윈도우를 생성하는 일이다. 다음 코드는 윈도우 초기화를 실행한다.

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    // 원도우 클래스를 등록한다. (O.o 이렇게도...)
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClassEx( &wc );

    // 어플리케이션 윈도우를 생성한다.
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01: CreateDevice",
                              WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );

앞서 제시한 코드 예제는 표준 윈도우 프로그래밍에 대한 예제이다. 예제는 "D3D Tutorial"이라는 윈도우 클래스를 정의하고 등록하는 것으로 부터 시작하고 있다. 클래스가 등록되면, 예제 코드는 등록된 클래스를 사용하여 300 * 300 크기의 기본적인 최상위 윈도우를 생성한다. 이 윈도우는 메뉴도 없고, 어떠한 자식 윈도우(콘트롤까지도)도 없다. 예제는 WS_OVERLAPPEDWINDOW 윈도우 스타일을 사용한다 - 이것은 최소화, 최대화, 닫기 버튼이 달려있는 극히 평범한 윈도우를 생성하게 된다. (만약 풀스크린 모드에서 동작할 것이라면 추천되는 윈도우 스타일은 WS_EX_TOPMOST이다. 이것은 윈도우가 활성화되어있지 않을지라도 모든 최상위가 아닌 윈도우들 앞에 위치하도록 해준다) 한번 윈도우가 생성되면 윈도우를 업데이트하고 표시하도록 하기위해 Win32 함수들을 호출한다.

윈도우가 준비되었으므로, 필수 D3D 객체들을 설정하도록 하자.

5 2 단계 : D3D 초기화 #

CreateDevice 예제 프로젝트는 D3D 초기화를 윈도우가 생성된 직후에 InitD3D() 함수에서 실행한다. 일단 어플리케이션 윈도우가 생성된 후에는, 장면을 랜더링하는 데 사용될 D3D 객체들을 초기화할 단계라고 생각하면 된다. 이 단계에서는 D3D 객체를 생성하고, 표시에 해당하는 인자들을 설정하고 마지막으로 D3D 디바이스들을 생성한다.

D3D 객체를 생성한 후에는 D3D 디바이스를 생성하기 위해서 IDirect3D8::CreateDevice() 메소드를 사용할 수 있다. 또한 디바이스, 화면 모드, 타입등등을 검출하는데에도 사용할 수 있다. 아래 코드 조각은 D3D 객체를 생성하는 것을 보여준다.

if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
    return E_FAIL;

Direct3DCreate8()함수의 유일한 인자값은 언제나 D3D_SDK_VERSION로 설정하면 된다. 이것은 D3D에게 정확한 헤더 화일이 사용되었는지를 알려준다. SDK 버젼과 소스상의 버젼이 맞지 않으면 Direct3DCreate8은 실패하게 된다.

다음은 IDirect3D8::GetAdapterDisplayMode()를 사용하여 현재 화면 모드를 얻어내는 코드이다.

D3DDISPLAYMODE d3ddm;
if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
    return E_FAIL;

D3DDISPLAYMODE 구조체의 Format 맴버변수는 Direct3D 디바이스를 생성할 때 사용될 수 있다. 윈도우 모드로 실행해야할 경우에는, 비디오 카드의 현재 디스플레이 모드와 맞는 백 버퍼를 생성하기 위해서 이 맴버변수(Format)가 사용된다.

D3DPRESENT_PARAMETERS 구조체의 각 항목을 채움으로써, 여러분이 작성할 3D 어플리케이션이 어떻게 작동할 것인지를 정의할 수 있다. 여기서는 Windowed 맴버는 true로 설정해서 윈도우 모드임을 지정하고, SwapEffect 맴버는 D3DSWAPEFFECT_DISCARD로 설정하고 BackBufferFormat 맴버는 d3ddm.Format으로 설정하여 앞서 구한 비디오카드의 현재 화면 모드 포멧으로 설정하게 된다.

D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed   = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;

마지막 단계는 D3D 디바이스를 생성하기 위해 IDirect3D8::CreateDevice() 메소드를 실행하는 것이다. 다음 코드를 참조해라.

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                  &d3dpp, &g_pd3dDevice ) ) )

위 코드 예제는 D3DADAPTER_DEFAULT 옵션값에 따라 기본 비디오 카드에 따른 디바이스를 생성한다. 대부분의 경우, 시스템은 여러개의 비디오카드를 설치하지 않았다면 1개의 비디오 카드만이 인식된다. 그리고 소프트웨어 디바이스보다는 하드웨어 디바이스를 선호한다고 간주하고 DeviceType 맴버에 D3DDEVTYPE_HAL을 정의했으며, 시스템이 소프트웨어 정점 처리방식을 사용한다고 설정하기 위해서 D3DCREATE_SOFTWARE_VERTEXPROCESSING 옵션값을 설정했다. (만일 하드웨어 정점 처리방식이라면 D3DCREATE_HARDWARE_VERTEXPROCESSING으로 설정하면 된다. 비디오카드가 하드웨어 정점 처리방식을 지원한다면 상당한 성능향상을 볼 수 있다.)

이제 D3D가 설치되었다. 다음 단계는 시스템 메세지를 처리하기 위한 체계를 다뤄보도록 한다.

6 3 단계 : 시스템 메세지 다루기 #

어플리케이션 윈도우와 D3D를 초기화한 후라면, 이제 장면을 랜더링 할 준비가 되어있는 것이다. 대부분의 경우, 윈도우 어플리케이션은 메세지 루프내에서 윈도우 메세지들을 처리하며, 메세지 큐에 메세지가 없을 경우 장면들을 랜더링한다. 어쨌거나 (일반적인 게임개발에는 어긋나지만 단순하게 하기 위해서) 이 셈플에서는 WM_PAINT에 맞추어 랜더링하는 방법을 택하도록 한다.

// The message loop.
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) )
{
    TranslateMessage( &msg );
    DispatchMessage( &msg );
}

평범한 메세지 루프다. 자, 다음은 메세지 핸들링 함수이다.

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;

        case WM_PAINT:
            Render();
            ValidateRect( hWnd, NULL );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

WM_PAINT 메세지 처리부분에 Render() 함수가 있는 것을 눈여겨 보도록 하라. 모든 랜더링이 끝난 후 화면을 갱신하기 위해 ValidateRect()함수를 사용했다.

자, 이제 어플리케이션은 시스템 메세지를 처리할 수 있다. 다음 단계는 화면을 랜더링 하는 부분이다.

7 4 단계 : 장면 랜더링 및 화면에 표시하기 #

장면에 랜더링하고 표시하기 위해서 샘플 코드는 백버퍼를 푸른 색으로 채우고, 백버퍼를 화면 버퍼에 전송한 후, 화면버퍼를 화면에 나타낸다(present).

화면을 지우기 위해서, IDirect3DDevice8::Clear() 메소드를 사용해라.

// 푸른색으로 백버퍼를 지운다.
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

Clear() 함수의 처음 두개의 인자는 각각 지울 사각영역(RECT)의 배열의 길이와 포인터를 의미한다. 여기서는 그냥 전체를 지우는 것이므로 구체적인 영역을 지정할 필요가 없어 0과 NULL로 설정했다. RECT의 배열은 랜더링되는 타겟 평면상의 지워질 영역들을 나타낸다.

대부분의 경우에는 화면전체를 나타내는 한개의 사각영역만을 사용하게 된다. 이것은 위 예제와 같이 첫번째 인자는 0, 두번째 인자는 NULL로 놓는 것으로 가능하다. 3번째 인자는 이 메소드의 동작을 지정한다. 랜더링 타겟 표면을 지정하거나, 그에 딸린 깊이 버퍼, 스텐실 버퍼, 혹은 앞서 설명한 옵션들의 조합을 지정할 수 있다. 이 튜토리얼에서는 깊이 버퍼를 사용하지 않으므로 D3DCLEAR_TARGET 옵션만을 지정하고 있다. 마지막 3개의 인자들은 랜더링 타겟 평면과 깊이 버퍼, 스텐실 버퍼를 지우는데 사용되는 값을 설정한다. 이 예제에서는 푸른 색으로 지우기 위해 D3DCOLOR_XRGB(0,0,255)으로 지정하고 있다. 마지막 두 인자는 무시된다. (스텐실과 깊이 버퍼에 대한 옵션을 지정하지 않았기 때문이다)

뷰 포트를 클리어한 후에는 랜더링하는 부분만이 남았다. 다음 코드를 참고해보자.

// 장면 랜더링을 시작한다.
g_pd3dDevice->BeginScene();

// 장면 객체들의 랜더링이 여기서 일어난다. 즉, 여기서 열라 그린다. ^^;

// 장면 랜더링을 마무리한다.
g_pd3dDevice->EndScene();

IDirect3DDevice8::BeginSceneIDirect3DDevice8::EndScene메소드는 시스템에게 랜더링이 시작될 것이라는 것과 완료했다는 신호를 보내는 역할을 한다. 랜더링관련 처리는 반드시 이 두 메소드 사이에서만 해야한다. 심지어 중간에 랜더링이 실패했다 하더라도, EndScene메소드를 호출하여 마무리를 하고 다시 BeginScene을 호출하여야만 한다는 것을 명심하자.

장면을 랜더링한 후에, IDirect3DDevice8::Present메소드를 사용하여 이것을 화면에 나타낸다.

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

처음 2개의 인자는 원본 사각영역값(RECT)와 타겟 사각영역값(RECT)이다. 예제 코드에서는 둘다 NULL로 설정함으로써 백 버퍼 전체를 주 버퍼 전체로 적용하도록 했다. 세번째 인자는 이 화면표시에 목표가 되는 윈도우 핸들을 지정한다. 이것이 NULL로 지정되었으므로 D3DPRESENT_PARAMETERS 구조체의 hWndDeviceWindow 맴버 변수값이 대신 사용된다. 4번째 인자는 리젼값을 나타내는 인자이며 대부분의 경우 NULL로 지정된다.

8 5 단계 : 셧다운 #

실행중 몇몇 경우에 어플리케이션은 반드시 셧다운 시켜야 한다. 셧다운하는 작업은 어플리케이션 윈도우를 해제 하는 것 뿐만 아니라 어플리케이션에서 사용하는 DX 객체(그리고 그것들을 가리키는 포인터들)들을 해제하는 것까지도 포함하는 것이다. CleanUp()함수가 이 역할을 담당하며, WM_DESTROY 메세지에서 이 함수가 실행된다.

VOID Cleanup()
{
    if( g_pd3dDevice != NULL)
        g_pd3dDevice->Release();
    if( g_pD3D != NULL)
        g_pD3D->Release();
}

각 객체마다 IUnknown::Release() 메소드를 실행해서 객체의 메모리를 해제하는 것을 볼 수 있다. DX는 COM 규칙에 따라 개발되어있으므로 이런 식으로 메모리를 해제해주어야 한다. (이 메소드는 내부적으로 레퍼런스 카운트를 감소시키고, 만일 카운트가 0이면 비로소 메모리를 날린다.)

셧다운에 덧붙이자면, 화면 해상도를 변경하거나 색상 깊이를 변경할 경우같은 때에는 일반적인 실행중이라도 사용중인 D3D 객체를 해제하고 재생성해야할 수도 있다. 이럴 필요가 있을때 마다 이 CleanUp() 함수를 실행하고 다시 생성하는 것이 좋다.

9 덧붙이기 : 풀스크린 여부 선택 #

  1. 다음 코드를 전역변수 선언부에 추가한다.
    bool bFullScreen = false;
    


  2. 다음을 WinMain() 함수 맨 첫줄에 추가한다.
    bFullScreen = (MessageBox(NULL, "풀스크린으로 실행하실 겁니까?", "풀스크린 여부", MB_YESNO) == IDYES);
    


  3. 다음과 같은 코드를 InitVars() 함수내에서 찾는다.
    ...
       // Get the current desktop display mode
       D3DDISPLAYMODE d3ddm;
       DXTEST( g_lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm) );
        
       D3DPRESENT_PARAMETERS d3dpp;
       ZeroMemory( &d3dpp, sizeof(d3dpp) );
    
       d3dpp.Windowed = TRUE;
       d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
       d3dpp.BackBufferFormat = d3ddm.Format;
    
       DXTEST( g_lpD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
                                     D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_lpD3DDevice) );
    ...
    
    이것을 다음과 같이 바꾼다.
    ...
       // Get the current desktop display mode
       D3DDISPLAYMODE d3ddm;
       DXTEST( g_lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm) );
        
       D3DPRESENT_PARAMETERS d3dpp;
       ZeroMemory( &d3dpp, sizeof(d3dpp) );
    
       // 풀스크린여부에 따라 화면 초기화
       if (bFullScreen)
       {
          d3dpp.Windowed               = FALSE;
          d3dpp.BackBufferCount        = 1;
          d3dpp.SwapEffect             = D3DSWAPEFFECT_COPY;
          d3dpp.hDeviceWindow          = hWnd;
          d3dpp.BackBufferWidth        = WIN_WIDTH;
          d3dpp.BackBufferHeight       = WIN_HEIGHT;
          d3dpp.BackBufferFormat       = d3ddm.Format;
       }
       else
       {
          d3dpp.Windowed = TRUE;
          d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
          d3dpp.BackBufferFormat = d3ddm.Format;
       }
          DXTEST( g_lpD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
                D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_lpD3DDevice) );
    ...
    
    Windowed 항목만 false로 설정하면 되는 것이 아니라 백버퍼도 같이 화면 폭과 너비로 지정해주어야함을 알 수 있다.

10 마치며 #

상당히 간단하고 다소 부실한 튜토리얼입니다. (번역하면서 불만이 많이 생기는 중... -_-;) 하지만 처음 DX를 접하시는 분들에게는 다소 DX에 대한 갈증이 해결되리라 믿습니다. ^^; 간간히 토를 붙여 불려나갈테니 참고해주세요.

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2010-10-28 12:42:52
Processing time 0.5600 sec