정점, 버틱스, Vertex ㅇㅋ?
/// Direct3D9를 사용하기 위한 헤더
#include <d3d9.h>
/**-----------------------------------------------------------------------------
* 전역변수
*------------------------------------------------------------------------------
*/
LPDIRECT3D9 g_pD3D = NULL; /// D3D 디바이스를 생성할 D3D객체변수
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; /// 렌더링에 사용될 D3D디바이스
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; /// 정점을 보관할 정점 버퍼
/**-----------------------------------------------------------------------------
* 이 프로그램의 시작점
*------------------------------------------------------------------------------
*/
/// 사용자 정점을 정의할 구조체
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; /// 정점의 변환된 좌표(rhw 값 : 변환이 완료된 정점)
DWORD color; /// 정점의 색
};
사용자 정점 구조체에 관한 정보를 나타내는 FVF값
구조체는 X, Y, Z, RHW와 Diffuse 색깔 값으로 이루어져 있음을 알 수 있다.
구조체 선언 뒤에는 세미콜론을 꼭 넣어줍시다
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
구조체와 함께 플래그 선언도 해주어야 한다.
구조체 선언시 항목에 들어간 그대로 선언해 주어야 한다, 구조체 선언부 내용과 플래그 선언부의 내용이 다르면 그 정점은 절대로 작동하지 않는다.
지금 XYZRHW와 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. 디폴트 비디오카드를 사용(대부분은 비디오카드가 1개 이다.)
/// 2. HAL디바이스를 생성한다.(HW가속장치를 사용하겠다는 의미)
/// 3. 정점처리는 모든 카드에서 지원하는 SW처리로 생성한다.(HW로 생성할경우 더욱 높은 성능을 낸다.)
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
/// 디바이스 상태정보를 처리할경우 여기에서 한다.
return S_OK;
}
HRESULT InitVB()
{
/// 삼각형을 렌더링 하기 위해 세개의 정점 선언
CUSTOMVERTEX vertices[] = {
{150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw(좌표변환이 완료된 정점), color
{250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};
실제 정점 값 할당.
3차원 공간의 x, y, z값과 rhw값, 그리고 각 정점마다 다른 색을 할당하고 있다.
색의 경우에는 0xAARRGGBB의 순서로 값을 준다.
A : 알파값으로 0x00부터 0xff의 값을 갖는다.
R, G, B : Red, Green, Blue의 값으로 0x00 ~ 0xff의 값을 가질 수 있다.
/// 정점버퍼를 생성한다
/// 세개의 사용자 정점을 보관할 메모리를 할당한다
/// FVF(사용자 정의 정점 : Flexible Vertex Format)를 지정하여 보관할 데이터 형식을 지정한다
if(FAILED(g_pd3dDevice->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL)))
{
return E_FAIL;
}
g_pVB 정점버퍼에 생성한 정점 버퍼를 비디오카드에 생성하여 보관한다.
정점 버퍼 : 정점을 모아두는 일종의 메모리, 단순한 배열이나 new, malloc()과는 다르게 정점 처리만을 위해 만들어진 특수한 메모리이기 때문에 훨씬 효율적이다.
정점버퍼는 크게 비디오 메모리와 시스템 메모리 두가지의 메모리를 사용한다.
비디오메모리는 메모리 용량이 작지만 강력한 성능을 보여주고
시스템메모리는 이와 반대로 메모리용량은 크지만 성능이 좋지않다
관리되는 정점버퍼의 양에 따라 적절히 사용해주면 되겠다.
CreateVertexBuffer 함수 인자
Length : 생성할 정점 버퍼의 바이트 단위 크기
Usage : 정점버퍼의 종류, 처리방식 (SW, HW) 지정
FVF : 정점 정보 구조체에 따라 선언된 FVF 플래그 값 (위에 상수 #define)
Pool : 정점 버퍼가 저장될 메모리의 위치(비디오카드, 시스템메모리)와 관리방식지정
ppVertexBuffer : 반환될 정점 버퍼의 인터페이스
/// 정점버퍼를 값으로 채운다
/// 정점버퍼의 Lock() 함수를 호출하여 포인터를 얻어온다
VOID* pVertices;
if(FAILED(g_pVB->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
return E_FAIL;
memcpy(pVertices, vertices, sizeof(vertices));
g_pVB->Unlock();
정점버퍼를 잠근 상태에서 정점 버퍼를 작성하고, 그 후 잠금상태를 풀어준다.
생성한 정점 버퍼가 쓰레기 값으로 가득 차있기 때문에, 값을 넣어줘야한다.
넣을 때는 Lock()을 사용하여 메모리 포인터를 얻어내야 한다.
Lock 함수 전달인자
OffsetToLock : Lock를 할 버퍼의 시작점, SizeToLock과 함께 양쪽 모두 0이면 버퍼 전체
SizeToLock : Lock을 할 버퍼의 크기, OffsetToLock과 함께 양쪽 모두 0이면 버퍼 전체
ppbData : 읽고 쓸 수 있게 된 메모리 영역의 포인터
Flags : Lock을 수행할 때 함께 사용하는 플래그
CreateVertexBuffer로 생성한 정점버퍼는 정확히 말해 정점버퍼의 인터페이스다.
이 인터페이스의 Lock()이라는 함수를 사용하여 실제 정점을 읽고 쓸 수 있는 메모리 포인터를 얻어내야 한다, 일단 Lock()으로 얻어진 ppbData 포인터는 일반 메모리처럼 마음대로 사용할 수 있다.
memcpy를 호출하여 정점 버퍼에 정점 정보를 쓴다
Lock()을 수행한 정점 버퍼는 반드시! Unlock()을 해줘야한다.
return S_OK;
}
InitVB()
정점버퍼를 생성하고 정점값을 채워넣는다.
정점버퍼 : 기본적으로 정점 정보를 갖고 있는 메모리 블록
정점 버퍼를 생성한 다음에는 반드시 Lock()와 Unlock()로 포인터를 얻어내서 정점정보를 정점 버퍼에 써넣어야한다.
또한 D3D는 인덱스 버퍼도 사용 가능하다는 것을 명심명심하자
정점 버퍼나 인덱스버퍼는 기본 시스템 메모리 외 디바이스 메모리에도 생성될 수 있다, 대부분의 비디오카드에서는 이렇게 할 경우 엄청난 속도향상을 얻어낼 수 있다!
사용자 정의 정점 형식 : FVF (Flexible Vertex Format)
정점 셰이더를 사용하기 위해서는 FVF를 써야만 한다.
FVF는 정해져 있는 정점의 포맷 중에서 자신이 제작하는 프로그램에 알맞은 것들을 모아서 새로운 정점 포맷을 정하는 방법인데, D3D에 정해져있는 포맷은 아래와 같다
FVF플래그 값의 용도
정점의 좌표 : 정점의 3차원 좌표
RHW : 동차 좌표계의 w값, 이게 있으면 변환이 완료된 정점
결합 가중치 : 스키닝에 사용, 나중에나올거야
법선 벡터 : 정점의 법선 벡터를 나타낸다, 광원처리시 사용
확산광 : RGBA(r, g, b, a) 매크로 값, 정점의 확산광 색을 나타낸다.
반사광 : RGBA(r, g, b, a) 매크로 값, 정점의 반사광 색을 나타낸다.
텍스쳐 좌표 : 텍스쳐 좌표값을 나타낸다, D3D는 8개까지 텍스쳐를 동시에 겹쳐 사용할 수 있다, 나중에보자
/**-----------------------------------------------------------------------------
* 초기화된 객체들을 소거한다.
*------------------------------------------------------------------------------
*/
VOID Cleanup()
{
if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if( g_pD3D != NULL)
g_pD3D->Release();
if( g_pVB != NULL)
g_pVB->Release();
}
/**-----------------------------------------------------------------------------
* 화면을 그린다.
*------------------------------------------------------------------------------
*/
VOID Render()
{
if( NULL == g_pd3dDevice )
return;
/// 후면버퍼를 파란색(0,0,255)으로 지운다.
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);
C#으로 했던 그것과 상당히 유사하다
DrawPrimitive 메서드의 TriangleList는 혹시 까먹었을지 모르니까 그림을 하나 첨부합니다
사실 DrawIndexedPrimitive()함수를 많이씁니다, 빨라서요.
/// 렌더링 종료
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,
"D3D Tutorial", NULL };
RegisterClassEx( &wc );
/// 윈도우 생성
HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 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();
}
}
}
/// 등록된 클래스 소거
UnregisterClass( "D3D Tutorial", wc.hInstance );
return 0;
}
돌려보자
'Graphics > DirectX' 카테고리의 다른 글
Direct 3D - *광원 (0) | 2014.06.19 |
---|---|
Direct X - *행렬 (0) | 2014.06.19 |
Direct X - *디바이스 생성 (0) | 2014.06.19 |
Direct X - 최초 세팅 작업 (0) | 2014.06.19 |
Direct X - 기초용어와 이론 (0) | 2014.06.19 |