계층구조
예를들면 이런거지
이런 구조일때, 오른쪽발 메시가 그려지기 위해서는 다음과 같은 과정을 거쳐야만 한다.
Vw = V오른발 * TM오른발 * TM오른다리 * TM오른허벅지 * TM엉덩이 * TM허리
이걸 프로그램으로 구현한다면 아래와 같은 의사코드(슈도코드^^)가 될 것이다.
D3DXMATRIX TM
TM = TM오른발 * TM오른다리 * TM오른허벅지 * TM엉덩이 * TM허리
m_pd3dDevice->SetTransform(D3DTS_WORLD, &TM);
m_od3dDevice->DrawPrimitive(m_pVB[X3DS_FOOT_R]);
계층구조 실제구현
복잡한 관절체 애니메이션의 첫걸음은 의외로 간단한 상자돌리기에서 시작한다.
일반적으로는 지구와 달과의 관계나 태양계 전체를 시뮬레이션 하는 것으로 계층구조 구현을 공부하는 경우가 많지만, 일단 2개의 상자가지고 해보자.
1. 부모메시는 Y축 회전을 한다.
2. 자식메시는 Z축 회전을 한다.
3. 자식메시는 부모로부터 일정거리(3,3,3) 떨어져있다.
개념도를보자
부모메시 : 기본변환 - TMparent
Y축 회전 - Rparent
자식메시 : 기본변환 - TMchild
Z축 회전 - Rchild
부모행렬 - Mparent
부모 최종행렬 : Mparent = Rparent * TMparent
자식 최종행렬 : Mchild = Rchild * TMchild * Mparent
= Rchild * TMchild * (Rparent * TMparent)
#include <d3d9.h>
#include <d3dx9.h>
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
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;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
struct MYINDEX
{
WORD _0, _1, _2; // 일반적으로 인덱스는 16비트의 크기를 갖는다
};
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;
/// 디바이스를 다음과 같은 설정으로 생성한다.
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()
{
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
};
if(FAILED(g_pd3dDevice->CreateVertexBuffer(8*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL)))
{
return E_FAIL;
}
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 } //뒷면
};
// 인덱스 버퍼 생성
// 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 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_pVB != NULL)
g_pVB->Release();
if(g_pIB != NULL)
g_pIB->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);
}
HRESULT InitGeometry()
{
if(FAILED(InitVB())) return E_FAIL;
if(FAILED(InitIB())) return E_FAIL;
return S_OK;
}
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();
}
}
}
/// 등록된 클래스 소거
UnregisterClass( "D3D Tutorial", wc.hInstance );
return 0;
}
결과는 이 두 상자가 빙빙빙 돈다
'Graphics > DirectX' 카테고리의 다른 글
Direct X - 스키닝 (0) | 2014.07.30 |
---|---|
Direct X - 애니메이션 (0) | 2014.07.29 |
Direct X - 법선맵핑 (0) | 2014.07.25 |
Direct 3D - 빌보드 (0) | 2014.07.24 |
Direct 3D - 라이트 맵핑 (0) | 2014.07.23 |