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

DirectX-정점(vertex_삼각형 띄우기)

by 즐거운 리뷰하는 (게임)프로그래머_리프TV 2010. 5. 19.



먼저 정점을 띄우기 위해서는 정점을 보관할 정점 버퍼가 필요 합니다.

정점 버퍼에 FVF(사용자 정의 정점 형식)를 사용하여 생성할 수 있습니다.

최초 생성한 정점 버퍼는 쓰레기 값이 들어가 있기 때문에,

Lock함수를 사용하여 값을 넣을 수 있는 포인터를 가져 옵니다.

그 후 미리 지정해 놓은 정점 정보를 정점 버퍼에 저장합니다.

HRESULT InitVB()
{
	// 삼각형을 렌더링 하기 위해 3개의 정점 선언
	CUSTOMVERTEX verticse[] = 
	{
		{ 150.0f,	50.0f,	0.5f,	1.0f, 0xffff0000, },
		{ 250.0f,	250.0f,	0.5f,	1.0f, 0xff00ff00, },
		{  50.0f,	250.0f,	0.5f,	1.0f, 0xff00ffff, },
	};

	// 점정 버퍼를 생성
	// 3개의 사용자 정점을 보관할 메모리를 할당
	// FVF를 지정하여 보관할 데이터 형식을 지정
	if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3 * sizeof( CUSTOMVERTEX ),
		0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
	{
		return E_FAIL;
	}

	// 정점 버퍼를 값으로 채운다.
	// 정점 버퍼의 Lock() 함수를 호출하여 포인터를 얻어온다.
	VOID* pVertices;
	if( FAILED( g_pVB->Lock( 0, sizeof( verticse ),
		(void**)&pVertices, 0 ) ) )
		return E_FAIL;

	memcpy( pVertices, verticse, sizeof( verticse ) );
	g_pVB->Unlock();

	return S_OK;
}​


바로 그 일을 하는 함수가 InitVB입니다.

이제 정점 버퍼를 생성하고 값을 대입하였으니,

화면에 띄워 주는 일만 남았습니다.

화면에 띄워 주기 위해선 정점 버퍼를 출력 스트림에 할당하고,
셰이더 정보를 삽입한 후에,
화면에 출력시켜 주면됩니다.

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() ) )
	{
		// 실제 렌더링 명령들이 나열될 곳
		// 정점 버퍼의 삼각형을 그린다.
		// 1. 정점 정보가 담겨 있는 정점 버퍼를 출력 스트림으로 할당
		g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
		// 2. D3D에게 정점 셰이더 정보를 지정한다. 
		// 대부분의 경우에는 FVF만 지정한다.
		g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
		// 3. 기하 정보를 출력하기 위한 DrawPrimitive() 함수 호출
		g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
		
		// 렌더링 종료
		g_pd3dDevice->EndScene();
	}

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


이게 전반적인 흐름이고,
조금 자세히 한번 살펴 보도록 합시다.

CreateVertexBuffer 함수는 정점 버퍼를 생성하는 함수인데,
UINT Length,( 생성할 정점 버퍼의 바이트 단위 크기 )
DWORD Usage,( 정점 버퍼의 종류 혹은 처리 방식(SW, HW) 지정 )
DWORD FVF, ( 정점 정보 구조체에 따라 선언된 FVF 플래그 값 )
D3DPOOL Pool, ( 정점 버퍼가 저장될 메모리의 위치( 비디오카드, 시스템 메로리) 와 관련 방식 지정 )
IDirect3DVertexBuffer9** ppVertexBuffer, ( 반환될 정점 버퍼의 인터페이스 )
HANDLE* pSharedHandle
로 이루어져 있는데 HANDLE의 경우는 NULL을 넣어 주는것 같습니다.

다음으로 Lock 함수 같은 경우에는,
UINT OffsetToLock, ( Lock을 할 버퍼의 시작점, SizeToLock과 함께 양쪽 모두 0이면 버퍼 전체 )
UINT SizeToLock, ( Lock을 할 버퍼의 크기, OffsetToLock과 함께 양쪽 모두 0이면 버퍼 전체 )
void** ppbData, ( 읽고 쓸 수 있게 된 메모리 영역의 포인터 )
DWORD Flags ( Lock을 수행할 때 함께 사용하는 플래그 )
예제의 사용을 보면 버퍼의 시작점은 0으로 두고
사이즈는 sizeof를 사용해 할당 받고,
VOID* pVertices 라는 변수에 메모리 영역의 포인터를 삽입,
플래그는 주지 않은 상태인것을 확인 할 수 있습니다.

Lock함수의 주의점은 꼭 마지막에 UnLock를 해주어야 한다는 것을 잊지 맙시다.

다음으로 Render를 해줄때 사용하는
SetStreamSource, SetFVF, DrawPrimitive 함수를 살펴 봅시다.

책에선 SDK도움말을 확인 하라길래 그냥 인터넷에서 뒤져서 대충 정리해 보았습니다.

SetStreamSource 함수,
UINT StreamNumber, ( 스트림의 최대숫자까지 설정 할 수 있다는데, 보통 0으로 한다고 함 )
IDirect3DVertexBuffer9* pStreamData, ( 정점 버퍼가 저장된 데이타 )
UINT OffsetInBytes, (정점 버퍼에 시작될 스트림 오프셋 값, 보통 0을 지정한다.)
UINT Stride ( 한 정점의 크기로 앞서 설정된 구조체 크기를 넣어 준다.)

SetFVF 함수,
DWORD FVF ( 유연 정점 포맷, FVF의 설정 )

DrawPrimitive 함수,
D3DPRIMITIVETYPE PrimitiveType, ( 어떤 방식으로 그려줄 것인가에 대한 속성 값 )
UINT StartVertex, ( 로드하는 최초 정점 인덱스 )
UINT PrimitiveCount ( 랜더링 하는 도형의 갯수 )

대충 이정도 개념을 잡고 있으면 정점에 대해선 어느정도 이해한거라고 생각합니다.

// 전체 소스

// 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 디바이스
LPDIRECT3DVERTEXBUFFER9	g_pVB = NULL;	// 정점을 보관할 정점 버퍼

// 사용자 정점을 정의할 구조체
struct CUSTOMVERTEX
{
	FLOAT x, y, z, rhw;	// 정점의 변환된 좌표
	DWORD color;	// 정점의 색깔
};
// 자용자 정점 구조체에 관한 정보를 나타내는 FVF 값
// 구조체는 X, Y, Z, RHW 값과 Diffuse 색깔 값으로 이루어져 있음을 알 수 있다.
#define D3DFVF_CUSTOMVERTEX ( D3DFVF_XYZRHW | D3DFVF_DIFFUSE )

//////////////////////////////////////////////////////////////////////////
// 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;
}

//////////////////////////////////////////////////////////////////////////
// 정점 버퍼를 생성, 초기화
HRESULT InitVB()
{
	// 삼각형을 렌더링 하기 위해 3개의 정점 선언
	CUSTOMVERTEX verticse[] = 
	{
		{ 150.0f,	50.0f,	0.5f,	1.0f, 0xffff0000, },
		{ 250.0f,	250.0f,	0.5f,	1.0f, 0xff00ff00, },
		{  50.0f,	250.0f,	0.5f,	1.0f, 0xff00ffff, },
	};

	// 점정 버퍼를 생성
	// 3개의 사용자 정점을 보관할 메모리를 할당
	// FVF를 지정하여 보관할 데이터 형식을 지정
	if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3 * sizeof( CUSTOMVERTEX ),
		0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
	{
		return E_FAIL;
	}

	// 정점 버퍼를 값으로 채운다.
	// 정점 버퍼의 Lock() 함수를 호출하여 포인터를 얻어온다.
	VOID* pVertices;
	if( FAILED( g_pVB->Lock( 0, sizeof( verticse ),
		(void**)&pVertices, 0 ) ) )
		return E_FAIL;

	memcpy( pVertices, verticse, sizeof( verticse ) );
	g_pVB->Unlock();

	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() ) )
	{
		// 실제 렌더링 명령들이 나열될 곳
		// 정점 버퍼의 삼각형을 그린다.
		// 1. 정점 정보가 담겨 있는 정점 버퍼를 출력 스트림으로 할당
		g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
		// 2. D3D에게 정점 셰이더 정보를 지정한다. 
		// 대부분의 경우에는 FVF만 지정한다.
		g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
		// 3. 기하 정보를 출력하기 위한 DrawPrimitive() 함수 호출
		g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
		
		// 렌더링 종료
		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;
	}

	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 02 : Vertices"),
		WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL,
		wc.hInstance, NULL );

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

			// 메시지 루프
			MSG msg;
			ZeroMemory( &msg, sizeof( msg ) );

			while( msg.message != WM_QUIT )
			{
				// 메시지 큐에 메시지가 있으면 메시지 처리
				if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}
				else
					// 처리할 메시지가 없으면 Render() 함수 호출
					Render();
			}
		}
	}

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

}​


// 실행 화면

댓글0