본문 바로가기
프로그래밍/DirectX

DirectX-디바이스 생성

by 리뷰하는 (게임)프로그래머_리프TV 2010. 5. 17.



자 이제 DX를 해봅시다!
일단 닥치고 코드를 칩니다.

// DX9를 사용하기 위한 헤더
#include <d3d9.h>

#pragma comment(lib, "dxerr9.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "winmm.lib")

//////////////////////////////////////////////////////////////////////////
// 전역 변수

LPDIRECT3D9			g_pD3D = NULL;	// D3D 디바이스를 생성할 D3D 객체 변수
LPDIRECT3DDEVICE9	g_pd3dDevice = NULL;	// 렌더링에 사용될 D3D 디바이스

//////////////////////////////////////////////////////////////////////////
// Direct3D 초기화

HRESULT InitD3D( HWND hWnd )
{
	// 디바이스를 생성하기 위한 D3D 객체 생성
	if( NULL == (g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
		return E_FAIL;

	D3DPRESENT_PARAMETERS d3dpp;	// 디바이스 생성을 위한 구조체
	ZeroMemory( &d3dpp, sizeof( d3dpp ) );	// 반드시 ZeroMemory() 함수로 구조체를 지워야 함
	d3dpp.Windowed = TRUE;	// 창모드로 생성
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;	// 가장 효율 적인 SWAP 효과
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;	// 현재 바탕화면 모드에 맞춰 후면 버퍼를 생성

	// 디바이스를 다음과 같은 설정으로 생성
	// 1. 디폴트 비디오카드를 사용.
	// 2. HAL 디바이스를 생성.
	// 3. 정점 처리는 모든 카드에서 지원하는 SW처리로 생성

	if( FAILED(g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice ) ) )
	{
		return E_FAIL;
	}

	return S_OK;
}

//////////////////////////////////////////////////////////////////////////
// 초기화된 객체들을 소거

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

	if( g_pD3D != NULL )
		g_pD3D->Release();
}

//////////////////////////////////////////////////////////////////////////
// 화면 그리기

VOID Render()
{
	if( NULL == g_pd3dDevice )
		return;

	// 후면 버퍼를 파란색으로 
	g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
		D3DCOLOR_XRGB( 0, 0, 255 ), 1.0f, 0 );

	// 렌더링 시작
	if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
	{
		// 실제 렌더링 명령들이 나열될 곳

		// 렌더링 종료
		g_pd3dDevice->EndScene();
	}

	// 후면 버퍼를 보이는 화면으로
	g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

//////////////////////////////////////////////////////////////////////////
// 윈도우 프로시져

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

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

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

//////////////////////////////////////////////////////////////////////////
// 이 프로그램의 시작점

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
	// 윈도우 클래스 등록
	WNDCLASSEX wc = { sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc,
		0L, 0L, GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
		TEXT("D3D Tutorial"), NULL };

	RegisterClassEx( &wc );

	// 윈도우 생성
	HWND hWnd = CreateWindow( TEXT("D3D Tutorial"), TEXT("D3D Tutotial 01 : CreateDevice"),
		WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL,
		wc.hInstance, NULL );

	// Direct3D 초기화
	if( SUCCEEDED(InitD3D(hWnd)) )
	{
		// 윈도우 출력
		ShowWindow( hWnd, SW_SHOWDEFAULT );
		UpdateWindow( hWnd );

		// 메시지 루프
		MSG msg;
		while( GetMessage( &msg, NULL, 0, 0 ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
	}

	// 등록된 클래스 소거
	UnregisterClass( TEXT("D3D Tutorial"), wc.hInstance );
	return 0;

}​


실행하면 파란 화면이 나옵니다!
끝!!!!!!!!!!!!!

이라고 말하고 싶지만, 한번 천천히 살펴 봅시다;

API에 WindowEx를 만드는 소스를 참고하시면 이해가 편합니다.

WindowEx.txt
다운로드



일단 순서를 살펴봅시다. 140줄이나 되지만 고작 파란 화면만 뜨더니 끝나는,

기본적인 API의 메시지큐 구조를 파악했다는 전제로 저희가 알아야할 부분은
InitD3D 함수와,
Rander, Cleanup 함수 입니다.
변수로는 전역에 선언된
LPDIRECT3D9
LPDIRECT3DDEVICE9
에 대해서 어느정도 파악을 해보도록 합시다.

프로그램을 실행하면
최초 INT WINAPI WinMain 함수에 들어가게 됩니다.
기존 API처럼 WndClass를 등록하고 화면에 띄우는작업은 거의 비슷하지만,
메시지 루프에 들어가기전 InitD3D라는 함수 들르는것이 보입니다.

모든 내용을 다 알필요는 없다고 생각됩니다. 적당히 그런가? 정도로 파악하고 넘어갑시다.
언젠간 확실히 알게 될....꺼라 생각합니다.
코드 설명은 주석에 어느정도 달려 있으니 그냥 그런가 하고 넘어간다고 치고.
InitD3D에서 하는 일은,
D3D객체를 만들고,
디바이스에 대한 설정을 한 이후에,
그 설정값을 가지고, 디바이스를 생성!
하는 것 같습니다.
아마 모든 작업이 무사히 완료 됐다면 이제 메시지 루프에 들어가게 되겠지요.

메시지 루프에 들어가보니 WM_PAINT에서
Render 함수를 호출하네요,
바로 밑에는 ValidateRect를 해주는것을 보아하니,
지속적으로 WM_PAINT를 돌려 주는것 같습니다.
Render함수에서 이제 무언가 그려줘야 겠지만
이번 예제는 아무것도 그려주지 않습니다.
Render함수에서 하는 일은,
일단 g_pd3dDevice 의 변수가 아직 유효한가에 대해서 검사하는것 같습니다.
혹시라도 디바이스가 지워지지(NULL값이)않았는가 하는 예외처리를 해주고 있네요.
후면 버퍼를 퍼렇게 물들이고,
BeginScene과 EndScene 사이에 무언가를 렌더링 한 후에,
Present를 사용해서 후면 버퍼에 있는 디바이스를 보여주도록 이루어져 있습니다.
프로그램이 실행되는 동안에는 무한의 Rander함수 호출과,
이벤트에 따른 동작들이 이루어 지는것 같습니다.
이제 프로그램을 종료 하게 되면,
WM_DESTORY로 메시지가 들어가게 될테고

PostQuitMessage를 하기전에 Cleanup 함수를 호출합니다.


Cleanup 함수에 가보니, 전역으로 잡아 주었던 D3D객체와 디바이스를 NULL값으로 주고 있네요.
그 후 릴리즈를 사용해서 완벽하게 지워줍니다.

어차피 당장 무언가 거대한걸 바라는것도 아니니,
일단 이정도로만 개념을 잡아두면 당분간은 무리가 없을 것 같습니다.