반응형

//-----------------------------------------------------------------------------
// File: Matrices.cpp
//
// Desc: 우리는 이제 장치를 생성하고 2D 정점을 렌더링하는 법을 알았다.
//   이 튜토리얼은 다음으로 기하학적인 3D를 렌더링을 배운다.
//       기하학적 3D를 다루기 위해서는 4x4 메트릭스 변환을 이용해 기하학적으로
//       변환(이동), 회전, 크기조정 그리고 카메라를 조종할수 있다.
//       
//   3차원 모델계 좌표를 사용하는데, 우리가 월드 변환을 하여 이동, 회전,
//   크기 조정할수 있다. 이떄 월드 행렬이 사용된다. 다시 월드 좌표계의 기하
//   정보를 카메라 좌표계로 변환한다.
//   기하학적 월드좌표를 사용하기 위해서는 카메라의 위치나 눈의 위치 월드계의
//       어느곳을 보고 있는지가 필요하다.
//       또하나의 변환은 view matrix에 의한 시점의 위치와 회전을 한다.
//       마지막 변환으로 투영 변환이 있다. 프로젝션은 3D를 우리가 보이는 
//   2D 화면으로 변환한다.
//
//       OpenGL에서는 행렬 연산 함수를 직접 작성해야 하지만, D3D에서는 D3DX라는 
//   유틸리티 함수들이 여러 개 존재한다. 여기서는 D3DX 계열 함수를 사용할
//   것이다.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------

// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D = NULL; // D3DDevice를 생성할 D3D객체 변수
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // 렌더링에 사용될 D3D 디바이스
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // 정점을 보관할 정점 버퍼

// 정점을 정의할 구조체
struct CUSTOMVERTEX
{
 FLOAT x, y, z; // 정점의 변화된 좌표(rhw값이 있으면 변환이 완료된 정점)
 DWORD color;        // 정점의 색상
};

// 사용자 정점 구조체에 관한 정보를 나타내는 FVF값
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)

 


//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Direct3D 초기화
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
   if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    // 디바이스 생성을 위한 구조체
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof( d3dpp ) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

    // D3DDevice 장치 생성
    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;
}

 


//-----------------------------------------------------------------------------
// Name: InitGeometry()
// Desc: 기하 정보 초기화
//-----------------------------------------------------------------------------
HRESULT InitGeometry()
{
    // 삼각형을 렌더링 하기 위해 세개의 정점 선언
    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;
}

 


//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: 초기화된 객체들 소거
//-----------------------------------------------------------------------------
VOID Cleanup()
{
    if( g_pVB != NULL )
        g_pVB->Release();

    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )
        g_pD3D->Release();
}

 

//-----------------------------------------------------------------------------
// Name: SetupMatrices()
// Desc: 행렬은 세 개가 있고, 월드. 뷰, 프로젝션 행렬이다.
//-----------------------------------------------------------------------------
VOID SetupMatrices()
{
    // 월드 행렬, 우리가 물체를 Y축으로 돌리기 위해 사용
    D3DXMATRIXA16 matWorld;

    // 회전 행렬 설정은일반적으로 전체 한바퀴 회전은 2*PI radians 이다. 
    // float 연산의 정밀도를 위해 1000으로 나머지 연산을 한다.
    UINT iTime = timeGetTime() % 1000;
 // 1000미리 초마다 한 바퀴씩 회전하는 애니메이션 행렬을 만든다.
    FLOAT fAngle = iTime * ( 2.0f * D3DX_PI ) / 1000.0f; 
    D3DXMatrixRotationY( &matWorld, fAngle );
 // 생성한 회전 행렬을 월드 행렬로 디바이스에 설정
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

 // 뷰행렬을 정의하기 위해서는 세가지 값이 필요하다.
 // 눈의 위치, 눈이 바라보는 위치, 천정 방향을 나타내는 상방벡터
 // 위 세가지의 값으로 뷰 행렬을 생성 한다.
 D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.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 );

 // 프로젝션 행렬을 정의하기 위해서는 시야각(FOV=Field Of View)과
 // 종회비(aspect ratio), 클리핑 평면의 값이 필요하다.
    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f );
    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

 

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: 화면을 그린다
//-----------------------------------------------------------------------------
VOID Render()
{
    // 백버퍼를 검은색으로 지운다.
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0f, 0 );

    // 렌더링 시작
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        // 월드, 뷰, 프로젝션 메트릭스를 설정한다
        SetupMatrices();

        // 정점 정보를 렌더링 한다
        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 1 );

        // 렌더링 종료
        g_pd3dDevice->EndScene();
    }

 // 후면 버퍼를 보이는 화면으로 
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

 


//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: 윈도우 메시지 처리
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( 0 );
            return 0;

  case WM_KEYDOWN: // 키를 눌렀을때
   if( wParam == VK_ESCAPE ) { // esc를 눌렀을때
    ::DestroyWindow(hWnd);
    return 0;
   }
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

 


//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: 프로그램 시작 지점
//-----------------------------------------------------------------------------
INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
{
 // 윈도우 클래스 등록
    WNDCLASSEX wc =
    {
        sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc, 0L, 0L,
        GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
        L"D3D Tutorial", NULL
    };
    RegisterClassEx( &wc );

 // 윈도우 창 생성
    HWND hWnd = CreateWindow( L"D3D Tutorial", L"D3D Tutorial 03: Matrices",
                              WS_OVERLAPPEDWINDOW, 100, 100, 256, 256,
                              NULL, 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( L"D3D Tutorial", wc.hInstance );
    return 0;
}

 

 

 

 

- 월드 변환(World transform)
 3차원 그래픽에서 모든 정점은 지역 좌표계(로컬 좌표계, 모델 좌표계) 값을 갖는다.
이 값들은 최초에 3차원 데이터가 생성되는 시점에서 기준이 되는 원점을 중심으로 한 좌표계이다.
 모든 물체들이 각각의 고유한 원점을 기준으로 모델링 되어 있으므로 여러개의 물체를 3차원 공간에
 출력할 경우 이들 물체들이 원점을 공유하게 되는 현상이 발생한다.

 (원점에서 물체가 겹쳐서 나타남)

 로컬좌표계를 월드 좌표계로 변화하는 행렬을 간단하게 변환 행렬(Transform Matrix)이라 하며
줄여서 TM이라고 부른다. 100개의 물체를 3차원 공간에 그리려면 100개의 TM이 필요하다.
 과거에는 TM값을 적용해서 물체를 그리기 위해 루프를 돌며 정점과 TM을 일일이 곱해주는 연산을
했지만 D3D 7.0 이후부터는 그렇게 할 경우 TnL지원을 받지 못한다. 
 따라서 TM은 반드시 SetTransform(D3DTS_WORLD, &matWorld)처럼 해주어야 하드웨어 가속을 받을수
있다.


- 카메라 변화(Camera transform)
 3차원 월드 좌표계를 카메라를 기준으로 한 카메라 좌표계로 변환하는 것을 말한다.

 카메라 변환 행렬 계산은 D3DMatrixLookAtLH(&matView, &vEyePt, &LookatPt, &vUpVec) 를 사용 한다.
matView : 변환 행렬이 들어갈 행렬 구조체,
vEyePt : 카메라의 위치 월드 좌표
LookatPt : 카메라가 바라보는 위치 월드 좌표
vUpVec : 카메라의 상방로컬벡터

 카메라 변환 행렬 적용은 SetTransform(D3DTS_VIEW, &matView)처럼 적용해주어야 한다.


- 투영 변환(Projection transform)
 월드 좌표계와 카메라는 모두 3차원 좌표계다. 하지만 우리가 모니터를 통해서 보는 화면은 2차원
화면이다. 그렇기 때문에 3차원을 우리가 볼수있는 2차원으로 변환이 필요하다. 가장 쉬운 방법은
3개의 축중에서 한개의 축을 버리고 2개의 축으로 2차원 좌표계로 변환된다.
 투영에는 가장 많이 사용하는 원근 투영(perspective projection) 기법으로 직교투영 법이 있다.

 투영 변환 행렬 계산은 D3DXMatrixPerspectiveFovLH(&matProj, fov, Sw/Sh, Zn, Zf) 를 사용 한다.
matProj : 변환 행렬이 들어갈 행렬 구조체
fov : 시야 각 (Field Of View)
Sw/Sh : 종횡비(가로세로의 비율)
Zn : 가까운 클리핑 평면(near clipping plane)
Zf : 먼 클리핑 평면(far clipping plane)

 투영 변환 행렬 적용은 SetTransform(D3DTS_PROJECTION, &matProj)처럼 적용해주어야 한다.

- 렌더링 파이프 라인
 렌터링 파이프는 로컬 좌표계 정점 -> 월드 행렬 -> 카메라 행렬 -> 투영 행렬 -> 뷰포트 좌표계 정점 이런 순의 과정을 거치게 된다.


이전 소스에 비해 회전이 들어가고 예제 코드에서 정점 구조체에 rhw값이 없다는 것에 주의할것
#define에서도 D3DFVF_XYZ로 RHW가 빠져있다 이것을 추가하게 되면 화면이 나오지 않는다.

반응형

'DirectX > DirectX 9.0' 카테고리의 다른 글

[DirectX 9.0] Textures  (0) 2014.02.11
[DirectX 9.0] Lights  (0) 2014.02.11
[DirectX 9.0] Vertices  (0) 2014.02.11
CreateDevice  (0) 2011.03.02
DirectX 설치 및 설정 방법  (0) 2011.03.02

+ Recent posts