차례
1. 소개 [Bottom] [Top]
Direct3D 는 게임에서 주로 많이 사용되고 있으며 MFC 는 응용 프로그램 개발에 많이 사용되고 있다. 이렇게 사용 목적이 전혀 다른 두 가지를 게임 개발에 이용하는 것은 부적합하다. 하지만 게임 자체를 개발하는 것에는 부적합할지 모르나 개발에 필요한 보조 프로그램을 개발하는데는 가장 적합하지 않을까?
아직도 많은 사람들은 MFC 는 덩치가 크고 느리다고 한다. 그러나 그것은 펜티엄 100MHz 를 사용하던 과거의 이야기일뿐, 지금과 같이 거의 모든 CPU 가 GHz 를 넘어가는 경우엔 전혀 다르다. 메모리나 저장매체 또한 GByte 단위를 넘어가고 있는 현시점에서 전혀 영향을 줄 수 없는 것들이다. 그리고 WINAPI 보다 편리한 인터페이스와 프레임워크를 제공하고 있으므로 프로그래밍 속도 또한 빠르다.
MFC 를 이용한 Direct3D 프로그래밍은 게임 프로그램 보다는 보조 프로그램을 개발하는 것이 목적이다. 그리고 Direct3D 프로그래밍을 처음 시작할 때도 유용하다. 우선 Direct3D 를 구동하기 위한 프레임워크 개발이 필요 없으며 여러가지 데이터를 입력하거나 테스트할 경우에는 손쉽게 인터페이스를 만들고 수정할 수 있다. MFC 를 이용한 Direct3D 프로그래밍를 시작하게 된 이유도 편리한 인터페이스 프로그래밍 때문이다.
참고로 Microsoft Visual Studio 2005 를 기준으로 정리한다.
2. 참고도서 [Bottom] [Top]
- IT EXPERT : 3D 게임 프로그래밍
- 김용준 저, 한빛 미디어
-
ISBN: 8979142536
- Direct3D 의 Framework 에 대하여 간략히 소개하고 있음 ( p.142 ~ 158 )
3. 참고링크 [Bottom] [Top]
-
http://www.gamedev.net/reference/articles/article1778.asp
- Integrating Direct3D 8.1 With MFC Using Visual Studio 6.0
-
http://www.moon-labs.com/resources/IntegratingDirect3D9.0WithMFC.pdf
- Integrating Direct3D 9.0 with MFC Using Visual Studio .Net (7.0)
-
http://www.codeproject.com/opengl/windowopengl2.asp
- How to draw OpenGL to a window you created in a dialog box with the resource editor
-
http://www.gamedev.net/reference/articles/article1358.asp
- Creating 3D Tools with MFC
4. MFC 프로젝트 만들기 [Bottom] [Top]
MFC 프레임워크는 다양한 형태를 제공한다. 기본적으로는 다중 문서 (MDI) 와 문서/뷰 (Doc/View) 구조가 선택되어 있지만, Direct3D 프로그래밍에서는 단일 문서 (SDI) 구조를 사용할 것이다. 그리고 문서/뷰 (Doc/View) 구조는 불필요하기 때문에 단일 뷰 구조만 사용한다. 문서/뷰 구조를 사용하고 싶다면 참고링크 에 소개된 글들을 참고한다.
4.1. 새 프로젝트 만들기 [Bottom] [Top]
-
Visual Studio 의 새 프로젝트 마법사를 열고, 아래 그림과 같이 MFC 응용 프로그램 을 선택한 후 프로젝트 이름을 입력하고 확인을 클릭한다.
-
예) 프로젝트 이름: MFC4Direct3D
-
-
MFC 응용 프로그램 마법사 가 열리면 다음과 같이 선택한다.
-
단일 문서 를 선택한다.
-
문서/뷰 아키텍처 지원 은 체크하지 않는다(단일 뷰 구조).
-
참고> 유니코드를 사용하지 않는 경우, 유니코드 라이브러리 사용 을 체크하지 않는다.
-
-
MFC 응용 프로그램 마법사 의 나머지 과정은 기본값을 사용한다.
- 새 프로젝트 만들기 끝나면 다음과 같이 구조가 될 것이다.
4.2. 라이브러리 링크 시키기 [Bottom] [Top]
새로운 프로젝트가 만들어졌다면, Direct3D 를 사용할 수 있도록 Direct3D 라이브러리를 추가한다. 라이브러리 추가는 프로젝트 속성에서 추가하거나 컴파일 지시어를 사용한다.
-
D3D9.lib
- D3DX9.lib
- Winmm.lib
4.2.1. 방법1: 프로젝트 속성에서 라이브러리 추가 [Bottom] [Top]
4.2.2. 방법2: 컴파일 지시어를 사용하여 라이브러리 추가 [Bottom] [Top]
MFC 응용 프로그램 프로젝트는 미리 컴파일된 헤더 (Precompiled Header) 를 사용하므로 StdAfx.h 헤더 파일에 다음과 같이 추가하면 된다.
-
줄 번호 보이기/숨기기
#pragma comment( lib, "D3D9" ) #pragma comment( lib, "D3DX9" ) #pragma comment( lib, "Winmm" )
5. 프로그램 구조 이해하기 [Bottom] [Top]
기본적인 프로젝트 설정이 끝났다면 구현에 앞서 다음의 그림을 보면서 앞으로 만들게 프로그램에 대하여 대략적인 구조를 파악해 보자.
여러개의 추가적인 클래스와 더 많은 멤버 함수들이 있지만 여기서는 프로그램 초기화, 렌더링, 마무리 작업의 주축을 이루는 부분에 대해서만 정리하였다.
아래에서 설명하겠지만 CChildView 클래스는 C3DBase 클래스를 상속 받은 클래스로 UpdateFrame() 과 Render() 함수는 C3DBase 클래스에 정의된 가상 함수 (Virtual Function) 를 재정의 (Overriding) 한 것이다.
그럼, 프로그램 초기화, 렌더링, 마무리 작업에 대하여 시퀸스 다이어그램을 보면서 처리 과정을 이해해 보자.
5.1. 초기화 과정 [Bottom] [Top]
일반적인 MFC 응용 프로그램 초기화 과정에 3D 장치 초기화/설정 및 3D 객체 생성 과정을 추가한 것으로 CChildView::InitStartup() 함수에서 필요한 초기화 과정을 추가하면 된다.
예를 들어 카메라 및 조명을 초기화 하거나, 격자 (Grid) 객체를 생성한다.
5.2. 렌더링 과정 [Bottom] [Top]
- 기본 렌더링 과정
- 루프를 통한 렌더링 과정 수행
- 화면 갱신에 의한 렌더링 과정
- 창의 전환이나 크기 조절, 이동 시 발생하는 화면 갱신에 대하여 렌더링 과정 수행
5.3. 마무리 과정 [Bottom] [Top]
프로그램 종료 시 3D 장치 해제와 3D 객체 제거 과정을 수행한다. CChildView::FinalCleanup() 함수에서 필요한 마무리 과정을 추가하면 된다.
예를 들어 CChildView::InitStartup() 함수에서 생성된 객체를 제거한다.
6. Direct3D 에 맞게 MFC 프로그래밍하기 [Bottom] [Top]
먼저, 앞서 그림에서 CMFC4Direct3DApp, CMainFrame, CChildView 그리고 C3DBase 로 4개의 주요 클래스를 볼 수 있다. 이 중에서 CMFC4Direct3DApp 클래스는 프로젝트에 따라 다른 이름으로 생성될 수도 있으며, 각 클래스는 개조하는 과정에서 자세히 설명할 것이다.
6.1. C3DBase 클래스 만들기 [Bottom] [Top]
C3DBase 클래스는 CChildView 클래스가 상속하게 될 Base 클래스로 주요 기능은 다음과 같다.
- Direct3D 인터페이스와 장치를 얻어서 Direct3D 를 사용할 수 있도록 초기화 한다.
- Direct3D 인터페이스와 장치를 해제하여 Direct3D 를 종료한다.
- 3D 렌더링 과정을 수행한다.
6.1.1. 멤버 함수 [Bottom] [Top]
-
줄 번호 보이기/숨기기
HRESULT CreateDirect3D( HWND hWnd, UINT nPresentationInterval ); void ReleaseDirect3D(); void ResetDirect3D( int nWidth, int nHeight ); void RenderDirect3D(); virtual void UpdateFrame() = 0; virtual void Render() = 0; void Pause( BOOL bPause=TRUE );
- CreateDirect3D() 함수
-
Direct3D 장치를 생성한다.
-
- ReleaseDirect3D() 함수
-
Direct3D 장치를 해제한다.
-
- ResetDirect3D() 함수
-
화면 (View) 의 크기가 바뀔때 Direct3D 장치를 재설정한다.
-
- RenderDirect3D() 함수
-
렌더링 과정을 수행한다. 내부에서 UpdateFrame() 과 Render() 함수를 호출한다.
-
-
UpdateFrame() 함수 (가상 함수)
-
상속 받은 클래스에서 재정의 해야하며 프레임 갱신 처리를 한다.
-
-
Render() 함수 (가상 함수)
-
상속 받은 클래스에서 재정의 해야하며 렌더링 처리를 한다.
-
- Pause() 함수
- 다른 창으로 포커스가 이동 시 CPU 점유율을 떨어뜨리기 위해서 사용한다.
6.2. CChildView 클래스 개조하기 [Bottom] [Top]
CChildView 클래스는 C3DBase 클래스를 상속받은 클래스로 실제 3D 렌더링을 수행한다. 또한 Direct3D 의 초기화 및 해제가 실제로 처리되는 곳이다. 즉, 3D 렌더링의 핵심 클래스가 된다.
-
PreCreateWindow() 함수 수정
-
아래의 코드 중 AfxRegisterWndClass() 함수의 3번째 매개변수 값을 NULL 로 설정한다.
줄 번호 보이기/숨기기1 BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 2 { 3 if (!CWnd::PreCreateWindow(cs)) 4 return FALSE; 5 6 cs.dwExStyle |= WS_EX_CLIENTEDGE; 7 cs.style &= ~WS_BORDER; 8 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 9 ::LoadCursor(NULL, IDC_ARROW), NULL, NULL); 10 11 return TRUE; 12 }
-
참고> AfxRegisterWndClass() 함수의 3번째 매개변수는 Background Brush 를 설정하는 곳으로 NULL 로 설정하게 되면 프레임마다 화면이 지워지지 않는다. 즉, 화면이 깜박거리는 현상 (Flicker 현상) 을 방지한다.
-
-
OnPaint() 함수 수정
- 위에서 설명했듯이 창 변화에 따른 화면 갱신 과정으로 렌더링 과정을 수행한다.
- WM_PAINT 메세지 핸들러.
줄 번호 보이기/숨기기1 void CChildView::OnPaint() 2 { 3 CPaintDC dc(this); // 삭제하면 안됨. 4 5 if( IS_NOT_NULL( g_pD3DDevice ) ) 6 { 7 // 렌더링 수행 8 RenderDirect3D(); 9 } 10 }
-
참고> CPaintDC dc(this); 코드는 삭제하지 말것. 삭제하게 되면 화면 갱신 시 창의 프레임 갱신이 느려지게 된다.
-
InitStartup() 함수 추가
-
초기화 과정에 필요한 작업을 여기에 추가한다.
줄 번호 보이기/숨기기1 HRESULT CChildView::InitStartup() 2 { 3 //---------------------------------------------------------------------- 4 // TODO: 초기화 처리를 수행한다. 5 // * 배경색 설정 6 // * 카메라 설정 7 // * 조명 설정 8 // * 렌더링 객체 생성 등 9 // 10 11 // 배경색 설정 12 SetBackColor( 0x40, 0x40, 0x40 ); 13 14 // 카메라 설정 15 m_pCamera = new CCamera(); 16 InitCamera(); 17 18 // 조명 설정 19 InitLight(); 20 LightEnable( FALSE ); // 조명 비활성화 21 22 // 격자 생성 23 m_grid.Create( 20, 4, 20 ); 24 25 return S_OK; 26 }
-
-
FinalCleanup() 추가
-
초기화 과정이나 실행 과정에서 생성된 3D 객체를 여기서 제거한다.
줄 번호 보이기/숨기기1 void CChildView::FinalCleanup() 2 { 3 //---------------------------------------------------------------------- 4 // TODO: 마무리 처리를 수행한다. 5 // * 초기화 또는 수행 중 생성된 3D 객체 해제 등 6 7 // 격자 제거 8 m_grid.Release(); 9 10 // 카메라 제거 11 SAFE_DELETE( m_pCamera ); 12 13 // Direct 3D 해제 14 ReleaseDirect3D(); 15 }
-
-
UpdateFrame() 함수 재정의 (C3DBase 가상 함수)
-
실제 프레임 갱신 작업을 여기에 추가한다.
줄 번호 보이기/숨기기1 void CChildView::UpdateFrame() 2 { 3 if( IS_NOT_NULL( m_pCamera ) ) 4 { 5 // 카메라 설정 6 m_pCamera->SetCamera(); 7 } 8 9 //-------------------------------------------------------------------------- 10 // TODO: 프레임 갱신 처리를 수행한다. 11 // 12 }
-
- Render() 함수 재정의 (C3DBase 가상 함수)
-
실제 렌더링 작업을 여기에 추가한다.
줄 번호 보이기/숨기기1 void CChildView::Render() 2 { 3 // 격자 렌더링 4 m_grid.Render(); 5 6 //-------------------------------------------------------------------------- 7 // TODO: 3D 오브젝트를 렌더링한다. 8 // 9 }
-
- WM_SIZE 메세지 핸들러 추가
- 창의 크기가 바뀔 때 Direct3D 장치를 재설정한다.
- 이 과정이 생략되면 창의 크기가 바뀔 시 3D 화면이 찌그러지거나 도트가 뭉게지는 현상이 발생한다.
줄 번호 보이기/숨기기1 void CChildView::OnSize(UINT nType, int cx, int cy) 2 { 3 __super::OnSize(nType, cx, cy); 4 5 if( IS_NOT_NULL( g_pD3DDevice ) ) 6 { 7 // Direct3D 재설정 8 ResetDirect3D( cx, cy ); 9 10 // 조명 설정 11 InitLight(); 12 LightEnable( FALSE ); // 조명 비활성화 13 } 14 }
6.3. CMainFrame 클래스 개조하기 [Bottom] [Top]
CMainFrame 클래스는 MFC 프레임워크에서 창의 기본 골격을 이루는 클래스로 창을 열거나 닫기, 메뉴, 툴바, 상태바 등을 관리하며 주로 사용자와의 인터페이스 처리를 담당하게 된다. 따라서 3D 렌더링에는 별로 중요하지 않다.
- InitDirect3D() 함수 추가
- 프로그램 시작 시 3D 초기화 과정을 수행한다.
줄 번호 보이기/숨기기1 HRESULT CMainFrame::InitDirect3D() 2 { 3 // View 영역 크기 설정 4 SetSize( 800, 600 ); 5 6 // Direct 3D 초기화 7 m_wndView.CreateDirect3D( m_wndView.m_hWnd, D3DPRESENT_INTERVAL_IMMEDIATE ); 8 9 // 사용자 초기화 수행 10 m_wndView.InitStartup(); 11 12 return S_OK; 13 }
-
SetSize() 함수 추가
- 프로그램 시작 시 3D 화면의 크기를 설정하는 곳으로 3D 화면에 맞게 창의 프레임 크기를 재계산하고 창 크기를 변경한다.
- 매개변수는 실제 3D 화면의 크기를 설정한다.
줄 번호 보이기/숨기기1 void CMainFrame::SetSize( int nWidth, int nHeight ) 2 { 3 RECT client, frame; 4 5 GetWindowRect( &frame ); 6 m_wndView.GetClientRect( &client ); 7 8 // View 영역 크기에 맞게 창크기 변경 9 frame.right = nWidth + frame.right - client.right; 10 frame.bottom = nHeight + frame.bottom - client.bottom; 11 12 MoveWindow( &frame ); 13 }
-
DestroyWindow() 함수 재정의 (CWnd 가상 함수)
- 프로그램 종료 과정의 시작점으로 마무리 과정을 수행한다.
줄 번호 보이기/숨기기1 BOOL CMainFrame::DestroyWindow() 2 { 3 // 마무리 작업 4 m_wndView.FinalCleanup(); 5 6 return CFrameWnd::DestroyWindow(); 7 }
- WM_ACTIVATE 메세지 핸들러 추가
- 창의 포커스 이동 유무에 따라 3D 렌더링 속도를 조절한다. 즉, CPU 점유율을 조절한다.
줄 번호 보이기/숨기기1 void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) 2 { 3 CFrameWnd::OnActivate(nState, pWndOther, bMinimized); 4 5 m_wndView.Pause( nState == WA_INACTIVE ); 6 }
6.4. CMFC4Direct3DApp 클래스 개조하기 [Bottom] [Top]
CMFC4Direct3DApp 클래스는 가장 먼저 실행되므로 실행 순서상으로 본다면 최상위에 위치한다고 볼 수 있다. 주요 기능은 다른 객체들을 생성하고 실행시키는 역활을 한다. 그리고 가장 중요한 기능은 CChildView 클래스에게 3D 렌더링하도록 RenderDirect3D() 함수를 호출한다.
-
InitInstance() 함수 수정
-
프로그램 초기화 과정으로 MFC 응용 프로그램 마법사 에서 생성된 코드에 3D 초기화 과정만 추가한다.
줄 번호 보이기/숨기기1 BOOL CMFC4Direct3DApp::InitInstance() 2 { 3 ... // 생략 4 5 // 창 하나만 초기화되었으므로 이를 표시하고 업데이트합니다. 6 pFrame->ShowWindow(SW_SHOW); 7 pFrame->UpdateWindow(); 8 // 접미사가 있을 경우에만 DragAcceptFiles를 호출합니다. 9 // SDI 응용 프로그램에서는 ProcessShellCommand 후에 이러한 호출이 발생해야 합니다. 10 11 // 초기화 수행 12 pFrame->InitDirect3D(); 13 14 return TRUE; 15 }
-
-
OnIdle() 함수 재정의 (CWinApp 가상 함수)
-
반복 루프로 CChildView::RenderDirect3D() 함수를 호출한다.
줄 번호 보이기/숨기기1 BOOL CMFC4Direct3DApp::OnIdle(LONG lCount) 2 { 3 CWinApp::OnIdle(lCount); 4 5 // 렌더링 수행 6 static_cast< CMainFrame * >( m_pMainWnd )->m_wndView.RenderDirect3D(); 7 8 return TRUE; 9 }
-
7. 다운로드 (준비중) [Bottom] [Top]
-
다운로드: 코드는 준비중 - 준비되면 업로드 예정
'프로그래밍 > MFC' 카테고리의 다른 글
MFC 메모리 릭 체크시 유용한 방법 2개 (0) | 2011.07.20 |
---|---|
MFC 다이얼로그 스크롤 사용 소스 (0) | 2010.10.29 |
[MFC] CWinAppEx::CleanState() (0) | 2010.08.25 |
CView (0) | 2010.05.24 |
차일드 사용 (0) | 2010.05.20 |