9장 글꼴
텍스트 출력 방법 ID3DXFont 인터페이스를 이용해서 텍스트 렌더링 CD3DFont 클래스를 이용해서 텍스트 렌더링 D3DXCreateText 함수를 이용해 3D 텍스트를 만들고 렌더링 2010-1학기 컴퓨터게임(DirectX)
ID3DXFont D3DX 라이브러리는 Direct3D 애플리케이션에서 텍스트를 그리는 데 사용할 수 있는 ID3DXFont 인터페이스를 제공 이 인터페이스는 내부적으로 GDI를 이용하기 때문에 약간의 성능 저하가 따르지만, GDI의 도움으로 복잡한 글꼴이나 포맷팅을 이용할 수 있음 ID3DXFont 만들기 D3DXCreateFontIndirect 함수를 이용해 ID3DXFont 인터페이스를 만들 수 있음 2010-1학기 컴퓨터게임(DirectX)
HRESULT D3DXCreateFontIndirect( LPDIRECT3DDEVICE9 pDevice, //글꼴과 연결된 장치 CONST LOGFONT* pLogFont, //글꼴을 나타내는 LOGFONT 구조체 LPD3DXFONT* ppFont //만들어진 글꼴을 리턴 ); 2010-1학기 컴퓨터게임(DirectX)
D3DXCreateFontIndirect 함수 이용 방법 LOGFONT lf; ZeroMemory(&lf, sizeof(LOGFONT)); lf.lfHeight = 25; // in logical units lf.lfWidth = 12; // in logical units lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = 500; // boldness, range 0(light) -1000(bold) lf.lfItalic = false; lf.lfUnderline = false; lf.lfStrikeOut = false; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = 0; 2010-1학기 컴퓨터게임(DirectX)
lf.lfPitchAndFamily = 0; lf.lfClipPrecision = 0; lf.lfQuality = 0; lf.lfPitchAndFamily = 0; strcpy(lf.lfFaceName, "Times New Roman"); // font style ID3DXFont* font=0; D3DXCreateFontIndirect(Device,&lf,&font); 만들고자 하는 글꼴의 특성을 지정하는 LOGFONT 구조체를 먼저 채워야 한다 2010-1학기 컴퓨터게임(DirectX)
텍스트 그리기 ID3DXFont 인터페이스로의 포인터를 얻은 다음에는 ID3DXFont::DrawText 메서드를 호출하여 간단하게 텍스트를 그려낼 수 있음 INT ID3DXFont::DrawText( LPCSTR pString, INT Count, LPRECT pRect, DWORD Format, D3DCOLOR color ); pString – 그리고자 하는 문자열을 가리키는 포인터 Count – 문자열 내에 포함된 문자의 수. 문자열이 null로 끝나는 경우에는 -1을 지정할 수 있음 2010-1학기 컴퓨터게임(DirectX)
pRect – 텍스트가 그려질 화면 상의 영역을 정의하는 RECT 구조체를 가리키는 포인터 Format – 텍스트 포맷을 지정하는 선택적 플래그 Color – 텍스트 컬러 예를 들면 Font->DrawText( “Hello World”, // 그리고자 하는 문자열 -1, // Null 로 끝나는 문자열 &rect, // 텍스트가 그려질 사각형 DT_TOP | DT_LEFT, // 사각형의 왼쪽 상단에 그림 0xff000000); // 검은색 2010-1학기 컴퓨터게임(DirectX)
초당 렌더링 프레임 계산하기 CFont 예제는 초당 렌더링되는 프레임수(FPS) 출력 먼저 다음과 같은 세 개의 전역 변수 인스턴스를 만든다 DWORD FrameCnt; //렌더링된 프레임수 float TimeElapsed; //현재까지 경과된 시간 float FPS; //초당 렌더링 프레임 수 매 초마다 FPS를 계산하여 비교적 정확한 평균을 얻음. 부가적으로 매 초당 일정한 FPS를 유지하여 변경된 값을 다시 계산할 동안 충분한 시간을 제공 각각의 프레임은 FrameCnt를 증가시키며 TimeElapsed에 마지막 프레임부터 현재까지 걸린 시간을 더함 FrameCnt++; TimeElapsed += timeDelta; //timeDelta는 각 프레임당 걸린시간 2010-1학기 컴퓨터게임(DirectX)
1초가 흐른 뒤에는 다음과 같은 식을 이용해 FPS를 계산할 수 있음 FPS=(float) FrameCnt/ TimeElapsed; 이제 FrameCnt와 TimeElapsed를 초기화하고 다음 1초를 위한 FPS 계산을 시작 다음은 완성된 코드임 void CalcFPS(float timeDelta) { FrameCnt++; TimeElapsed += timeDelta; If(TimeElapsed >= 1.0f) FPS=(float)FrameCnt / TimeElapsed; TimeElapsed = 0.0f; FrameCnt=0; } } 2010-1학기 컴퓨터게임(DirectX)
2010-1학기 컴퓨터게임(DirectX)
CD3DFont DirectX SDK는 DXDSK 루트 디렉토리의 /samples/c++/common 폴더에 매우 유용한 유틸리티 코드를 제공 이 코드 중에 CD3DFont 클래스가 포함되어 있으며, 이 클래스는 Direct3D와 텍스처를 입힌 삼각형을 이용해 텍스트를 렌더링함 CD3DFont는 GDI가 아닌 Direct3D를 이용하므로 ID3DXFont에 비해 훨씬 빠른 속도를 가지고 있지만 ID3DXFont의 복잡한 글꼴과 포맷팅은 지원하지 못함 CD3DFont 클래스를 이용하려면 d3dfont.h, d3dfont.cpp, d3dutil.h, dxutil.h, dxutil.cpp 파일을 추가해야 함: common 폴더의 Include 와 src 폴더에 있음 2010-1학기 컴퓨터게임(DirectX)
CD3DFont 구성하기 CD3DFont 인스턴스를 만들기 위해서는 일반적인 C++객체와 마찬가지로 이를 인스턴스화하면 됨 CD3DFont(const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags=0L); strFontName – 글꼴의 이름을 지정하는 null로 끝나는 문자열 dwHeight – 글꼴의 높이 dwFlags – 선택적 생성 플래그들; 이 인자에는 0을 지정하거나 D3DFONT_BOLD, D3DFONT_ITALIC, D3DFONT_ZENABLE 플래그들의 조합을 지정할 수 있음 2010-1학기 컴퓨터게임(DirectX)
CD3DFont 객체를 인스턴스화한 다음에는 다음의 메서드들을 순서대로 호출하여 글꼴을 초기화해야 함 Font = new CD3DFont(“Times New Roman”, 16,0); Font -> InitDeviceObjects(Device); Font -> RestoreDeviceObjects(); 2010-1학기 컴퓨터게임(DirectX)
텍스트 그리기 CD3DFont 객체를 구성하고 초기화하였다면 텍스트를 그릴 준비가 완료된 것이고, 텍스트를 그리는 데는 다음과 같은 메서드가 이용 HRESULT CD3DFont::DrawText(FLOAT x, FLOAT y, DWORD dwColor, const TCHAR* strText, DWORD dwFlags=0L); x - 텍스트 그리기를 시작할 화면의 x축 좌표 y - 텍스트 그리기를 시작할 화면의 y축 좌표 dwColor – 텍스트의 컬러 strText – 그리고자 하는 문자열의 포인터 dwFlags – 선택적 렌더링 플래그들; 이 인자에는 0을 지정하거나 D3DFONT_CENTERED, D3DFONT_TWOSIDED, D3DFONT_FILTERED 플래그들의 조합을 지정할 수 있음 예를 들어 Font->DrawText(20, 20, 0xff000000, “Hello, World”); 2010-1학기 컴퓨터게임(DirectX)
정리 D3DFont 객체를 삭제하기 전에 다음의 코드 조각에서 보여주는 것과 같은 약간의 정리 루틴을 호출해야 함 Font->InvalidateDeviceObjects(); Font->DeleteDeviceObjects(); Delete Font; 2010-1학기 컴퓨터게임(DirectX)
D3DXCreateText 이 함수는 텍스트의 3D 메쉬를 만드는데 이용됨 함수의 프로토타입 HRESULT D3DXCreateText( LPDIRECT3DDEVICE9 pDevice, HDC hDC, LPCTSTR pText, FLOAT Deviation, FLOAT Extrusion, LPD3DXMESH* ppMesh, LPD3DXBUFFER* ppAdjacency, LPGLYPHMETRICSFLOAT pGlyphMetrics ); 2010-1학기 컴퓨터게임(DirectX)
이 함수는 실행이 성공한 경우 D3D_OK를 리턴 pDevice – 메쉬와 연결될 장치 hDC – 메쉬를 생성하는 데 이용될 글꼴 정보를 포함하는 장치 컨텍스트로의 핸들 pText – 메쉬로 만들 텍스트를 포함하는 null로 끝나는 문자열 Deviation – 트루타입 글꼴 외곽선에서의 최대 편차. 이 값은 0이거나 보다 커야 한다. 이 값이 0일 때 편차는 원본 글꼴의 한 디자인 단위와 같게 됨 Extrusion – 음의 z축 방향으로 잰 글꼴의 깊이 ppMesh – 만들어진 메쉬를 리턴 ppAdjacency – 만들어진 메쉬의 근접 정보를 리턴 pGlyphMetrics – 장식 거리 데이터를 포함하는 LPGLYPHMETRICSFLOAT 구조체 배열의 포인터. 장식 거리 데이터에 상관하지 않는 경우에는 0으로 지정 2010-1학기 컴퓨터게임(DirectX)
다음의 예제코드는 이 함수를 이용해 3D 메쉬를 만드는 방법을 보여줌 HDC hdc = CreateCompatibleDC( 0 ); // 장치컨텍스트로의 핸들 HFONT hFont; // 을 얻음 ZeroMemory(&lf, sizeof(LOGFONT)); // LOGFONT 구조체를 채움 lf.lfHeight = 25; // in logical units lf.lfWidth = 12; // in logical units lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold) lf.lfItalic = false; lf.lfUnderline = false; lf.lfStrikeOut = false; lf.lfCharSet = DEFAULT_CHARSET; 2010-1학기 컴퓨터게임(DirectX)
lf.lfPitchAndFamily = 0; lf.lfOutPrecision = 0; lf.lfClipPrecision = 0; lf.lfQuality = 0; lf.lfPitchAndFamily = 0; strcpy(lf.lfFaceName, "Times New Roman"); // font style // 글꼴을 만들고 장치 컨텍스트로 글꼴을 선택 HFONT hFontOld; LOGFONT lf; hFont = CreateFontIndirect(&lf); hFontOld = (HFONT)SelectObject(hdc, hFont); // 텍스트의 3D 메쉬를 만들어낸다. ID3DXMesh* Text = 0; D3DXCreateText(Device, hdc, "Direct3D", 0.001f, 0.4f, &Text, 0, 0); 2010-1학기 컴퓨터게임(DirectX)
SelectObject(hdc, hFontOld); DeleteObject( hFont ); DeleteDC( hdc ); // 다시 이전의 글꼴을 선택하고 자원을 해제 SelectObject(hdc, hFontOld); DeleteObject( hFont ); DeleteDC( hdc ); 마지막으로 메쉬의 DrawSubset 메서드를 호출하여 간단히 3D 텍스트 메쉬를 렌더링할 수 있음 Text->DrawSubset(0); 2010-1학기 컴퓨터게임(DirectX)
2010-1학기 컴퓨터게임(DirectX)
d3dxcreatetext.cpp #include "d3dUtility.h" IDirect3DDevice9* Device = 0; const int Width = 640; const int Height = 480; ID3DXMesh* Text = 0; bool Setup() { HDC hdc = CreateCompatibleDC( 0 ); HFONT hFont; HFONT hFontOld; LOGFONT lf; ZeroMemory(&lf, sizeof(LOGFONT)); 2010-1학기 컴퓨터게임(DirectX)
lf.lfHeight = 25; // in logical units lf.lfWidth = 12; // in logical units lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold) lf.lfItalic = false; lf.lfUnderline = false; lf.lfStrikeOut = false; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = 0; lf.lfClipPrecision = 0; lf.lfQuality = 0; lf.lfPitchAndFamily = 0; strcpy(lf.lfFaceName, "Times New Roman"); // font style hFont = CreateFontIndirect(&lf); hFontOld = (HFONT)SelectObject(hdc, hFont); 2010-1학기 컴퓨터게임(DirectX)
D3DXCreateText(Device, hdc, "Direct3D", 0.001f, 0.4f, &Text, 0, 0); SelectObject(hdc, hFontOld); DeleteObject( hFont ); DeleteDC( hdc ); D3DXVECTOR3 dir(0.0f, -0.5f, 1.0f); D3DXCOLOR col = d3d::WHITE; D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col); Device->SetLight(0, &light); Device->LightEnable(0, true); Device->SetRenderState(D3DRS_NORMALIZENORMALS, true); Device->SetRenderState(D3DRS_SPECULARENABLE, true); 2010-1학기 컴퓨터게임(DirectX)
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 pos(0.0f, 1.5f, -3.3f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX V; D3DXMatrixLookAtLH( &V, &pos, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( 2010-1학기 컴퓨터게임(DirectX)
(float)Width / (float)Height, 0.01f, 1000.0f); &proj, D3DX_PI * 0.25f, // 45 - degree (float)Width / (float)Height, 0.01f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); return true; } void Cleanup() { d3d::Release<ID3DXMesh*>(Text); bool Display(float timeDelta) 2010-1학기 컴퓨터게임(DirectX)
D3DXMatrixRotationY(&yRot, y); y += timeDelta; if( y >= 6.28f ) if( Device ) { D3DXMATRIX yRot, T; static float y = 0.0f; D3DXMatrixRotationY(&yRot, y); y += timeDelta; if( y >= 6.28f ) y = 0.0f; D3DXMatrixTranslation(&T, -1.6f, 0.0f, 0.0f); T = T * yRot; Device->SetTransform(D3DTS_WORLD, &T); 2010-1학기 컴퓨터게임(DirectX)
Device->BeginScene(); Device->SetMaterial(&d3d::WHITE_MTRL); Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0); Device->BeginScene(); Device->SetMaterial(&d3d::WHITE_MTRL); Text->DrawSubset(0); Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) 2010-1학기 컴퓨터게임(DirectX)
::PostQuitMessage(0); break; case WM_KEYDOWN: { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); } return ::DefWindowProc(hwnd, msg, wParam, lParam); int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) 2010-1학기 컴퓨터게임(DirectX)
if(!d3d::InitD3D(hinstance, { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } if(!Setup()) ::MessageBox(0, "Setup() - FAILED", 0, 0); d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); 2010-1학기 컴퓨터게임(DirectX)