본문 바로가기
[ Program ]/CC++MFCAPI

MFC에서 D3D 8.0 사용하기(툴만들때 용이)

by 관이119 2012. 9. 14.
출처 balbari882님의 블로그 | 재준
원문 http://blog.naver.com/balbari882/140013140047

 

안녕하세요~ 이준곤(LeeChen) 입니다.

참고로 이강좌는 외국사이트를 번역기로 번역해서 옮긴것이라 문맥이 앞뒤가 어색할수 있습니다.
이강좌는 저에게 개인적으로 "MFC에서 D3D를 붙일려구 하는데 어떻게 해야 하나요?"라는 관련질문을 많이 해오셔서 이내용을 시간이 없는 관계로 그냥 캡쳐해서 올립니다.
개인적으로 시간이 많다면 강좌도 쓰고 책도 쓰고 싶지만... 이래저래 핑계로 못하고 있네요~ ^^;
그럼.도움이 되셨으면 합니다.
--------------------------------------------------------------------------------

Direct3D를 MFC로 이용하는 방법

 Direct3D를 이용한 툴을 작성하는 경우에는, Win32SDK를 이용해도 할 수 있습니다만, MFC를 이용하는 편이 무엇인가 편리한 일이 많다고 생각합니다. 게다가 드큐먼트뷰아키테크체와 공존할 수 있으면(자) 더욱더 툴이 만들기 쉬워지겠지요. 또, MFC를 사용해 Direct3D를 이용하는 해설은 어디를 찾아도 눈에 띄지 않기 때문에, MFC를 사용한 Direct3D의 초기화 방법을 소개합니다.
조금이라도 여러분의 참고가 되면 다행입니다.

또, 여기에서는 MFC의 간단한 지식이나 Direct3D의 초기화 방법은 기존의 것으로 해 해설해 갈 것입니다. MFC나 Direct3D의 초기화 방법은, 여러가지 곳에서 해설되고 있기 때문에, 그 쪽을 봐 주세요.

이쪽으로부터샘플을 다운로드할 수 있습니다. 샘플에서는, Direct3D를 초기화하는 클래스 CDirect3D 클래스를 작성해, CView 클래스의 base class로 하고 있습니다. 이 방법은, DirectXSDK 부속의MFCFog샘플로, CD3DApplication 클래스를 CFormView 클래스의 base class로 하고 있으므로, 똑같이 하고 있습니다. CDirect3D 클래스는, CD3DApplication 클래스의 MFC 대응 간이판이라고 생각해 주세요.


--------------------------------------------------------------------------------

1 어플리케이션 체제를 작성

우선 처음에, 체제를 작성합시다.
MFC AppWizard로,작성하는 어플리케이션의 종류를 SDI,문서/뷰아키테크체를 서포트에 체크를 넣어 후는 적당하게 옵션을 붙여 작성해 주세요.

이 시점에서 이하의 클래스가 완성됩니다.

CxxxApp
CMainFrame
CxxxDoc
CxxxView
여기로부터, Direct3D를 초기화해 나가는 것입니다만, Direct3D의 표시를 맡는 클래스, 즉, Direct3DDevice8와 관련짓는 클래스는 어떤 것이 좋을까요? 디바이스는, 윈도우와 관련지으므로, 이 안에서는,CMainFrame 클래스, 혹은CxxxView 클래스라는 것이 됩니다. 또, 드큐먼트뷰아키테크체를 채용하고 있으므로, 디바이스와 관련짓는 클래스는, 표시를 맡는 클래스, 즉CxxxView 클래스라는 것이 됩니다. CMainFrame 클래스와 디바이스를 관련지을 수 있습니다만, 드큐먼트뷰를 이용한 툴을 작성이라고 하는 것이므로, 여기에서는 의미가 없을 것입니다.


--------------------------------------------------------------------------------

2 Direct3D를 초기화한다

그럼,CxxxView 클래스에 Direct3D의 초기화 처리를 추가해 나갑시다. 그 전에, 어디에서 Direct3D의 초기화 처리를 실시하면 좋은 것일까요? 디바이스를 작성할 때는, 유효한 윈도우 핸들이 필요하게 됩니다. 그 말은, 벌써 윈도우는 작성 끝난 상태로 없으면 안됩니다. 어플리케이션의 초기화 처리는,CxxxApp::InitInstance 함수가 담당하고 있습니다. 이 함수 중(안)에서 Direct3D의 초기화 처리를 혼자서 맡아 처리해도 될 것입니다. 여기에서는,CxxxView 클래스가 Direct3D의 초기화를 담당하기로 하겠습니다.

CxxxView 클래스에서 초기화를 실시하는 경우,CView::OnInitialUpdate 함수를 오버라이드(override) 해, Direct3D의 초기화를 실시하는 것이 좋을 것입니다. DirectXSDK 샘플에서도 그렇게 하고 있습니다 해···. 초기화 코드는 이하와 같이 될까요.

void CxxxView::OnInitialUpdate()
{
CView::OnInitialUpdate();

HWND hDevice, hFocus;
HRESULT hr;

// 윈도우 핸들의 취득
hDevice = GetSafeHWnd();
hFocus = GetTopLevelParent() ->GetSafeHwnd();

// 윈도우의 클라이언트 영역을 취득
GetClientRect(&m_rcClient);

// Direct3D 오브젝트의 작성
m_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

// 디스플레이 모드의 취득
D3DDISPLAYMODE d3ddm;
m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

// D3DPRESENT_PARAMETERS 구조체의 설정
::ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));
m_d3dpp.Windowed = TRUE;
m_d3dpp.BackBufferCount = 1;
m_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
m_d3dpp.EnableAutoDepthStencil = TRUE;
m_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
m_d3dpp.hDeviceWindow = hDevice;
m_d3dpp.BackBufferWidth = m_rcClient.Width();
m_d3dpp.BackBufferHeight = m_rcClient.Height();
m_d3dpp.BackBufferFormat = m_d3ddm.Format;

// 디바이스 작성
hr = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hFocus,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&m_d3dpp,
&m_pd3dDevice);

/* 중략··· */

// 초기화 완료
m_bReady = TRUE;
}


툴이므로, 윈도우 모드로 초기화하고 있습니다. 또, 포커스 윈도우를 톱 레벨 윈도우, 디바이스 윈도우를 CxxxView, 백 버퍼의 사이즈는 디바이스 윈도우의 클라이언트 영역 전체적으로 초기화하고 있습니다.

그런데 여담이 됩니다만, 포커스 윈도우란 누구일까요?
헬프에는 이하와 같이 쓰여져 있습니다. 이것은, IDirect3D8::CreateDevice 함수의 인수, hFocusWindow의 설명입니다.

hFocusWindow
[in] 이 Microsoft Direct3D 디바이스로 포커스를 설정하는 윈도우 핸들. 지정하는 윈도우는, 풀 스크린의 최상정도 윈도우가 아니면 안된다.


작성하는 디바이스의 포커스-(영) focus (n) 1, 초점 2, 중심 :다이슈칸 「GENIUS」보다-즉, 작성하는 디바이스의 중심이 되는 윈도우라고 하는 것인가.

이것으로, Direct3D의 초기화는 완료입니다. 후는, 묘화 메세지에 응해 백 버퍼의 내용을 카피하면 OK입니다.
묘화 메세지에 대응한다CxxxView::OnDraw 함수는 이하와 같이 기분이 듭니다.

void CxxxView::OnDraw(CDC* pDC)
{
CxxxDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(m_bReady)
{
m_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0, 1.0f, 0);
if(SUCCEEDED(m_pd3dDevice->BeginScene()))
{
// 여기에 장면을 묘화 합니다
m_pd3dDevice->EndScene();
}
m_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
}


이것으로, 빌드 해 실행해 보세요. 잘 실행할 수 있을까요.
여기에는 쓰고 있지 않습니다만, CxxxView가 파기될 때에서도, Direct3D의 종료 처리-Direct3DDevice8, Direct3D8 오브젝트의 해방-를 잊지 않고 가 주세요.
제대로 동작할 것입니다. 그러나, 유감스럽지만 이 상태에서는 터무니없게 동작이 늦어집니다. 그 원인은 무엇인가?

자세한 내부 사정은 모릅니다만, 아마, 작성하는 백 버퍼와 윈도우의 클라이언트 영역의 사이즈에 차이가 있는 것 같습니다. 이러한 사이즈가 다르면(자), Direct3D는, Present 함수를 실행하면(자) 마음대로 확대 축소 처리를 해 윈도우에 카피합니다. 이 처리가 터무니없고 늦기 때문에, 전체의 동작이 늦어집니다.
GDI 묘화 때에, DIB 화상의 폭을 DWORD 경계에 갖추고 있었습니다만, 그것과 닮은 것 같은 것일까요?

그렇다 치더라도 이 늦음은, 견딜 수 있는 것이 아닙니다. DirectXSDK 샘플을 실행해, 윈도우를 적당한 크기에 변경해도 동작에 지장을 초래하는 일은 없기 때문에, 더좋은 방법은 있을 것입니다.


관련 참고사이트
http://www.gamedev.net/reference/articles/article1778.asp
http://www.devx.com/premier/mgznarch/vcdj/2000/07jul00/mf0007/mf0007.asp
http://codeguru.earthweb.com/directx/index.shtml
http://www.hackorama.com/d3dview/

 

댓글