처음 기본세팅부터 이번 인덱스 버퍼까지
남김없이 이해해야한다.
인덱스버퍼란?
인덱스버퍼는 정점을 저장하기 위한 정점버퍼와 마찬가지로 정점의 인덱스를 보관하기 위한 전용 버퍼를 말한다.
인덱스 버퍼를 사용하는 이유는 여러가지지만, 그 중 하나는 정점을 여러번 나열하는 것 보다 메모리 소모량이 적다는 것, 또 하나는 자주사용되는 정점을 캐시에 저장해서 더 높은 효율을 낼 수 있다는 것이다.
#include <d3d9.h>
#include <d3dx9.h>
LPDIRECT3D9 g_pD3D = NULL; /// D3D 디바이스를 생성할 D3D 객체 변수
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; /// 렌더링에 사용될 D3D 디바이스
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; /// 정점을 보관할 정점 버퍼
LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; /// 인덱스를 보관할 인덱스 버퍼
/// 사용자 정점을 정의할 구조체
struct CUSTOMVERTEX
{
FLOAT x, y, z; /// 정점의 변환된 좌표
DWORD color; /// 정점의 색깔
};
자 여기서 하나만 생각해보고 가자
사각형은 삼각형 두개로 이루어져 있다.
예를 들어 삼각형1 : v0, v1, v2, 삼각형2 : v0, v2, v3 라고 생각하자.
ㅇㅋ?
그러면, 정점을 (v0부터 v7까지(6면체라고 가정했을 때의 정점 개수 8개)) 정점버퍼에 저장하구, 인덱스 정보를 나열하면 다음과 같다.
정점버퍼 : v0 ~ v7
인덱스버퍼 : (0,1,2),(0,2,3) /// (v0, v1,v2),(v0,v2,v3)ㅇㅋ?
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
struct MYINDEX
{
WORD _0, _1, _2; /// 일반적으로 인덱스는 16비트의 크기를 갖는다
}; /// 32비트의 크기도 가능하지만 구형 그래픽카드에서는 지원되지 않는다
32비트도 가능하지만, 구형 그래픽카드에서는 16비트 인덱스만 지원한다.
16비트이기때문에, DrawPrimitive()에서 한번에 그릴 수 있는 폴리곤의 숫자는 2^16 = 64k개가 된다.
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; /// 현재 바탕화면 모드에 맞춰서 후면버퍼를 생성
d3dpp.AutoDepthStencilFormat = D3DFMT_D16; /// 복잡한 오브젝트를 그릴것이라 Z버퍼가 필요하다
d3dpp.EnableAutoDepthStencil = TRUE;
/// 디바이스를 다음과 같은 설정으로 생성한다.
/// 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;
}
/// Z버퍼를 켠다
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0xffffffff);
return S_OK;
}
HRESULT InitVB()
{
/// 상자를 렌더링하기 위해 8개의 정점 선언
CUSTOMVERTEX vertices[] =
{
{-1, 1, 1, 0xffff0000},
{1, 1, 1, 0xff00ff00},
{1, 1, -1, 0xff0000ff},
{-1, 1, -1, 0xffffff00},
{-1,-1,1, 0xff00ffff},
{1,-1,1, 0xffff00ff},
{1,-1,-1, 0xff000000},
{-1,-1,-1,0xffffffff},
};
/// 정점버퍼생성
/// 8개의 사용자 정점을 보관할 메모리를 할당한다
/// FVF를 지정하여 보관할 데이터의 형식을 지정한다
if(FAILED(g_pd3dDevice->CreateVertexBuffer(8*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL))){
return E_FAIL;
}
/// 정점버퍼를 값으로 채운다
/// 정점버퍼의 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();
return S_OK;
}
HRESULT InitIB()
{
/// 상자를 렌더링하기 위해 12개의 면 선언
MYINDEX indices[] =
{
{0,1,2},{0,2,3},{4,6,5},{4,7,6}, /// 위아래면
{0,3,7},{0,7,4},{1,5,6},{1,6,2}, /// 왼오른면
{3,2,6},{3,6,7},{0,4,5},{0,5,1} /// 앞뒷면
};
6면체를 생성하기위한 삼각형 열두개!
좌표평면을 생각해보면 1사분면부터 4사분면까지 회전방식이 반대방향이다.
2 1 1 2
3 4 좌표평면 4 3 정점형식
/// 인덱스버퍼 생성
/// D3DFMT_INDEX16은 인덱스의 단위가 16비트라는 것이다
/// 우리는 MYINDEX 구조체에서 WORD형으로 선언했으므로 D3DFMT_INDEX16을 사용한다
if(FAILED(g_pd3dDevice->CreateIndexBuffer(12*sizeof(MYINDEX),0,D3DFMT_INDEX16, D3DPOOL_DEFAULT,
&g_pIB, NULL)))
{
return E_FAIL;
}
/// 인덱스 버퍼를 값으로 채운다
/// 인덱스 버퍼의 Lock() 함수를 호출하여 포인터를 얻어온다
VOID* pIndices;
if(FAILED(g_pIB->Lock(0, sizeof(indices), (void**)&pIndices, 0)))
return E_FAIL;
memcpy(pIndices, indices, sizeof(indices));
g_pIB->Unlock();
return S_OK;
}
VOID SetupMatrices()
{
/// 월드 행렬
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity(&matWorld);
D3DXMatrixRotationY(&matWorld, timeGetTime()/1000.0f); /// Y축 중심 회전행렬 생성
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld); /// 생성한 회전행렬을 월드행렬로 디바이스에 설정
/// 뷰행렬을 정의하기 위해서는 세가지 값이 필요하다
D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f); /// 1. 눈의 위치 (0, 3.0, -5)
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f); /// 2. 눈이 바라보는 위치 (0,0,0)
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f); /// 3. 천정 방향의 상방벡터 (0,1,0)
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
/// 1,2,3의 값으로 뷰 행렬 생성
g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
/// 생성한 뷰 행렬을 디바이스에 설정
/// 프로젝션 행렬을 정의하기 위해서는 시야각(FOV=Field Of View)과
/// 종횡비(aspect ratio), 클리핑 평면의 값이 필요하다.
D3DXMATRIXA16 matProj;
/// matProj : 값이 설정 될 행렬
/// D3DX_PI/4 : FOV(D3DX_PI/4 = 45도)
/// 1.0f : 종횡비
/// 1,0f : 근접 클리핑 평면(near clipping plane)
/// 100.0f : 원거리 클리핑 평면(far clipping plane)
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); /// 생성한 프로젝션 행렬을 디바이스에 설정
}
VOID Cleanup()
{
if( g_pIB != NULL)
g_pIB->Release();
if( g_pVB != NULL)
g_pVB->Release();
if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if( g_pD3D != NULL)
g_pD3D->Release();
}
VOID Render()
{
/// 후면버퍼와 Z버퍼를 지운다
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
/// 월드, 뷰, 프로젝션 행렬을 설정한다
SetupMatrices();
/// 렌더링 시작
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
/// 정점 버퍼의 삼각형을 그린다
/// 1. 정점정보가 담겨있는 정점 버퍼를 출력 스트림으로 할당한다
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
/// 2. D3D에게 정점 셰이더 정보를 지정한다. 대부분의 경우에는 FVF만 지정한다
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
/// 3. 인덱스 버퍼를 지정한다
g_pd3dDevice->SetIndices(g_pIB);
/// 4. DrawIndexedPrimitive()를 호출한다
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,0,8,0,12);
정점+인덱스버퍼로 폴리곤을 출력한다.
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",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );
/// Direct3D 초기화
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
/// 정점버퍼 초기화
if(SUCCEEDED(InitVB())){
if(SUCCEEDED(InitIB())){
/// 윈도우 출력
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 3D - 프레임워크 (0) | 2014.06.19 |
Direct 3D - *메시 (0) | 2014.06.19 |
Direct 3D - *텍스쳐 (0) | 2014.06.19 |
Direct 3D - *광원 (0) | 2014.06.19 |