계층구조(Hierachy)란?
- 프로그래밍/3D그래픽스 & 쉐이더
- 2011. 1. 5. 13:54
사람과 동물은 관절체(articulated body)로 이루어져있다.
이러한 관절체의 중요한 특징은 계층구조를 갖는다는것!!!(hierachy)
거래한 트리구조를 가진다!.
종류:
BSP(Binary surface particle tee, 2진트리)
쿼드트리
옥트리 등이 있다.
계층구조는 간단하게 생각하면 부모 - 자식 관계를 구현하는것.
부모-자식관계를 3D로 구현한다는 것은 결국 3D적인 표현 수단을 사용하는것 == 결국은 행렬을 사용하는것이다.
애니메이션의 계층구조에 대하여.
노드의 월드행렬 = 노드의 지역행렬(Local Matrix) * 부모의 월드행렬( Parent World Matrix)
************************************************************
* 계층 구조 생성 예제
* 파일 : Hierarchy.cpp
*
* 설명 : 계층 구조를 만드는 것은 '자식행렬 * 부모행렬' 이다.
* 실제 이를 프로그램으로 구현해 보자.
********************************************************************/
#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;
//인덱스를 보관할 인덱스 버퍼
D3DXMATRIXA16 g_matTMParent; //부모의 TM(변환행렬)
D3DXMATRIXA16 g_matRParent; //부모의 회전 행렬
D3DXMATRIXA16 g_matTMChild; //자식의 TM(변환행렬)
D3DXMATRIXA16 g_matRChild; //자식의 회전 행렬
//사용자 정점을 정의할 구조체
struct CUSTOMVERTEX
{
FLOAT x, y, z; //정점의 변환된 좌표
DWORD color; //정점의 색깔
};
//사용자 정점 구조체에 관한 정보를 나타내는 FVF 값
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
struct MYINDEX
{
WORD _0, _1, _2; //일반적으로 인덱스는 16비트의 크기를 갖는다.
};
/********************************************************************
* Direct3D 초기화
********************************************************************/
HRESULT InitD3D(HWND hWnd)
{
//디바이스를 생성하기 위한 D3D 객체 생성
if(NULL == (g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
{
return E_FAIL;
}
//디바이스를 생성할 구조체
//복잡한 오브젝트를 그릴 것이므로 Z 버퍼가 필요
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
//디바이스 생성
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice)))
{
return E_FAIL;
}
//컬링 기능을 끈다.
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
//Z 버퍼 기능을 켠다.
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
//정점에 색깔값이 있으므로 광원 기능을 끈다.
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
return S_OK;
}
/********************************************************************
* 정점 버퍼를 생성하고 정점값을 채워넣는다.
********************************************************************/
HRESULT InitVB()
{
//상자(cube)를 렌더링하기 위해 8개의 정점 선언
CUSTOMVERTEX vertices[] =
{
{ -1, 1, 1, 0xffff0000 }, //v0
{ 1, 1, 1, 0xff00ff00 }, //v1
{ 1, 1, -1, 0xff0000ff }, //v2
{ -1, 1, -1, 0xffffff00 }, //v3
{ -1, -1, 1, 0xff00ffff }, //v4
{ 1, -1, 1, 0xffff00ff }, //v5
{ 1, -1, -1, 0xff000000 }, //v6
{ -1, -1, -1, 0xffffffff } //v7
};
//정점 버퍼 생성
//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()
{
//상자(cube)를 렌더링하기 위해 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 } //뒷면
};
//인덱스 버퍼 생성
//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;
}
/********************************************************************
* 기하 정보 초기화
********************************************************************/
HRESULT InitGeometry()
{
if(FAILED(InitVB()))
{
return E_FAIL;
}
if(FAILED(InitIB()))
{
return E_FAIL;
}
return S_OK;
}
/********************************************************************
* 카메라 행렬 설정
********************************************************************/
void SetupCamera()
{
//월드 행렬 설정
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity(&matWorld);
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);
//뷰 행렬 설정
D3DXVECTOR3 vEyePt(0.0f, 10.0f, -10.0f);
D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
//프로젝션 행렬 설정
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}
/********************************************************************
* 애니메이션 행렬 생성
********************************************************************/
VOID Animate()
{
//부모 메시는 원점에 있으므로 TM은 단위 행렬
D3DXMatrixIdentity(&g_matTMParent);
//부모 메시의 Y축 회전 행렬
D3DXMatrixRotationY(&g_matRParent, GetTickCount()/500.0f);
//자식 메시의 Z축 회전 행렬
D3DXMatrixRotationZ(&g_matRChild, GetTickCount()/500.0f);
//자식 메시는 원점으로부터 (3, 3, 3) 거리에 있음
D3DXMatrixTranslation(&g_matTMChild, 3, 3, 3);
}
/********************************************************************
* 초기화 객체들 소거
********************************************************************/
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 DrawMesh(D3DXMATRIXA16* pMat)
{
g_pd3dDevice->SetTransform(D3DTS_WORLD, pMat);
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->SetIndices(g_pIB);
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
}
/********************************************************************
* 화면 그리기
********************************************************************/
VOID Render()
{
D3DXMATRIXA16 matWorld;
//후면 버퍼와 Z버퍼 초기화
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
//애니메이션 행렬 설정
Animate();
//렌더링 시작
if(SUCCEEDED(g_pd3dDevice->BeginScene()))
{
matWorld = g_matRParent * g_matTMParent;
DrawMesh(&matWorld); //부모 상자 그리기
matWorld = g_matRChild * g_matTMChild *( g_matRParent * g_matTMParent );
//matWorld = g_matRChild * g_matTMChild * ( matWorld ); //위 행렬과 같은 결과
DrawMesh(&matWorld); //자식 상자 그리기
//렌더링 종료
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, "BasicFrame", NULL };
RegisterClassEx(&wc);
//윈도우 생성
HWND hWnd = CreateWindow("BasicFrame", "Hierarchy", WS_OVERLAPPEDWINDOW,
100, 100, 500, 500, GetDesktopWindow(), NULL, wc.hInstance, NULL);
//Direct3D 초기화
if(SUCCEEDED(InitD3D(hWnd)))
{
if(SUCCEEDED(InitGeometry()))
{
//카메라 행렬 설정
SetupCamera();
//윈도우 출력
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("D3D Tutorial", wc.hInstance);
return 0;
}
이 글을 공유하기