이걸 보기전에 꼭꼭꼭꼭!!
반드시반드시반드시 이 게시판에 있는
Direct X - 6강 좌표계 세가지를 이해해야한다.
월드, 뷰, 프로젝션 좌표계에 대한 선행학습이 필요합니다.
간단하게보자
월드좌표계
로컬 좌표계는 원점을 공유한다.
이걸 월드 좌표계로 변환하여 출력해야 원점에 겹치지 않고 메시들이 출력될 수 있다.
로컬좌표계 -> 월드좌표계는 행렬변환이다,,
이걸 변환행렬이라고 하는데, 변환행렬은 모든 물체마다 고유하게 하나 씩 존재한다
3차원 공간에 100개의 물체를 그리려면 100개의 변환행렬이 필요한 것.
뷰 좌표계 (카메라 변환)
월드좌표계를 카메라를 기준점으로 한 좌표계로 변환하는 과정이 바로 뷰 좌표계이다.
두개의 함수만 기억해두자
카메라 변환행렬 계산
D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec)
matView : 변환 행렬이 들어갈 행렬 구조체
vEyePt : 카메라 위치의 월드 좌표
vLookatPt : 카메라가 바라보는 위치의 월드 좌표
vUpVec : 카메라의 상방 벡터
카메라 변환 행렬 적용
SetTransform(D3DTS_VIEW, &matView);
투영변환 (프로젝션 변환)
월드좌표계와 뷰좌표계는 모두 3차원 좌표계다, 그러나 실제 우리가 모니터를 통해서 보게되는 화면은 2차원이다.
그렇타면, 3차원 좌표계를 2차원 좌표계로 바꿔주는 변환이 필요하다는걸 알 수 있다.
3차원 좌표를 2차원으로 변환하는 가장 쉬운 방법은 한 축을 없애는 것이다. Z축을 없애버리면 자연히 X, Y값만 남기 때문에 2차원 좌표계로 변환되는건데 이걸 직교투영이라한다.
하지만 실제 많이 사용되는 변환은 원근투영이라는 기법으로, 카메라의 거리에 따라 투영되는 비율이 달라지는 기법이다.
일단 함수를 잘 기억하자
투영변환 행렬 계산
D3DXMatrixPerspectiveForLH(&matProj, fov, Sw/Sh, Zn, Zf);
matProj : 변환 행렬이 들어갈 행렬 구조체
FOV : 시야각
Sw/Sh : 종횡비
Zn : 근접 클리핑 평면 (near clipping plane)
Zf : 원거리 클리핑 평면 (far clipping plane)
투영변환 행렬 적용
SetTransform(D3DTS_PROJECTION, &matProj);
/// Direct3D9를 사용하기 위한 헤더
#include <d3dx9.h>
#include <Windows.h>
#include <mmsystem.h> /// TimeGetTime() 함수 사용하기 위한 헤더
/**-----------------------------------------------------------------------------
* 전역변수
*------------------------------------------------------------------------------
*/
LPDIRECT3D9 g_pD3D = NULL; /// D3D 디바이스를 생성할 D3D객체변수
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; /// 렌더링에 사용될 D3D디바이스
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; /// 정점을 보관할 정점 버퍼
/**-----------------------------------------------------------------------------
* 이 프로그램의 시작점
*------------------------------------------------------------------------------
*/
/// 사용자 정점을 정의할 구조체
struct CUSTOMVERTEX
{
FLOAT x, y, z; /// 정점의 변환된 좌표
DWORD color; /// 정점의 색
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|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. 디폴트 비디오카드를 사용(대부분은 비디오카드가 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;
}
/// 컬링 기능을 끈다. 삼각형의 앞면, 뒷면을 모두 렌더링한다.
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
/// 정점에 색깔값이 있으므로 광원기능을 끈다.
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
return S_OK;
}
HRESULT InitGeometry()
{
/// 삼각형을 렌더링 하기 위해 3개의 정점 선언
CUSTOMVERTEX g_Vertices[] = {
{ -1.0f, -1.0f, 0.0f, 0xffff0000, },
{ 1.0f, -1.0f, 0.0f, 0xff0000ff, },
{ 0.0f, 1.0f, 0.0f, 0xffffffff, },
};
if(FAILED(g_pd3dDevice->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL)))
{
return E_FAIL;
}
VOID* pVertices;
if(FAILED(g_pVB->Lock(0, sizeof(g_Vertices), (void**)&pVertices, 0)))
return E_FAIL;
memcpy(pVertices, g_Vertices, sizeof(g_Vertices));
g_pVB->Unlock();
return S_OK;
}
/**-----------------------------------------------------------------------------
* 초기화된 객체들을 소거한다.
*------------------------------------------------------------------------------
*/
VOID Cleanup()
{
if( g_pd3dDevice != NULL)
g_pd3dDevice->Release();
if( g_pD3D != NULL)
g_pD3D->Release();
if( g_pVB != NULL)
g_pVB->Release();
}
VOID SetupMatrices()
{
D3DXMATRIXA16 matWorld;
UINT iTime = timeGetTime() % 1000;
float 연산의 정밀도를 위해 1000으로 나머지연산
FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
1000 밀리초마다 한바퀴씩(2 * pi) 회전 애니메이션 행렬을 만든다
D3DXMatrixRotationY(&matWorld, fAngle);
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;
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
matProj : 값이 설정 될 행렬
D3DX_PI/4 : FOV(D3DX_PI/4 = 45도)
1.0f : 종횡비
1,0f : 근접 클리핑 평면(near clipping plane)
100.0f : 원거리 클리핑 평면(far clipping plane)
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
생성한 프로젝션 행렬을 디바이스에 설정
}
/**-----------------------------------------------------------------------------
* 화면을 그린다.
*------------------------------------------------------------------------------
*/
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() ) )
{
/// 월드, 뷰, 프로젝션 행렬을 설정한다
SetupMatrices();
속도 최적화를 생각한다면 BeginScene()함수와 EndScene() 함수 사이에서 화면에 폴리곤을 그리게 되므로, 이 부분은 간단하면 간단할 수록 좋다. 때문에, SetupMatrices() 함수를 BeginScene()함수 호출 전에 먼저 호출하는 것이 더 효율적이다. 이 예제에서는 함수자체가 짧아서 걍 넣은겁니다
/// 정점 버퍼의 내용을 그린다
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
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;
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(InitGeometry())){
/// 윈도우 출력
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 X - *정점 (0) | 2014.06.19 |
Direct X - *디바이스 생성 (0) | 2014.06.19 |
Direct X - 최초 세팅 작업 (0) | 2014.06.19 |