[StartD2D-3] Direct2D 프로그래밍 시작하기!!!

DirectX 11 2011. 4. 15. 08:00 Posted by 조진현

 

첫 번째 Direct2D 프로그래밍~ 
 

지난 시간을 통해서 Direct2D의 필요성에 대해서, 제가 열심히(?) 언급해 드렸습니다.^^

이번 시간에는 Direct2D 프로그래밍의 세계에 대해서 들어가기 전에,

간단하게 프로그램을 작성해 볼 것입니다.

부끄럽지만, 저는 박식한 이론 내용 없이도 많은 프로그래밍 작업을 했었습니다.^^

그 만큼, 직접 프로그램을 작성하면 쉽게 이해할 수 있는 부분이 많이 있을 것입니다.

기본적으로 Direct2D는 2차원 그래픽을 만들기 위한 API입니다.

기존의 GDI를 이용한 프로그램의 일부분을 Direct2D로 대체를 하는 것만으로도 성능을 향상시킬 수 있습니다.

 

기본적으로 Direct2D로 작업하는 순서는 다음과 같습니다.

  1. Direct2D 팩토리를 생성한다.
  2. 팩토리에서 렌더타겟을 생성한다.
  3. 렌더타겟에서 리소스들을 생성한다.
  4. 생성 되어진 리소스들을 이용해서 그리기 작업을 수행한다.

 

Direct2D의 모든 작업은 위의 순서를 따릅니다.

이제 이들 순서를 어떻게 API로 표현하는지 살펴보겠습니다.

 

화면 작업을 위해 준비하기

 

첫 번째로 작성해볼 프로그램은 특정 색상으로 화면을 채우는 작업을 하는 것입니다.

먼저 마법사로 프로젝트를 생성하고, "stdafx.h" 헤더파일에 Direct2D와 관련된 선언을 추가시켜주기 바랍니다.

위의 내용은 Direct2D와 관련된 라이브러리와 헤더파일을 선언해 준 것입니다.

 

그리고 작업을 수행할 .cpp 파일에 전역 변수를 두 개 선언합니다.

 

Direct2D 프로그래밍을 위해서 가장 먼저 해야 하는 일은 ID2D1Factory 를 생성하는 일입니다.

 

D2D1CreateFactory()의 첫 번째 인자는 멀티 스레드 지원 여부를 설정합니다.

이번 내용에서는 싱글 스레드만을 사용합니다.( 멀티스레드 어려워요~~)

두 번째 인자는 팩토리가 생성되어서 결과를 반환 받을 수 있는 팩토리 포인터를 넘겨줍니다.

이것이 성공하면, Direct2D 와 관련된 작업을 할 수 있게 됩니다.( 참~~ 쉽죠잉!! )

 

우리가 만들려고 하는 프로그램이 화면에 어떤 내용을 그리는 것입니다.

화면에 무엇인가를 그린다는 개념은 하드웨어 입장에서 봤을 때는 메모리에 값을 쓰는 것입니다.

여러분들이 보고 있는 모니터 화면은 거대한 메모리에 색상 값이 기록되어 있는 것입니다.

 

이번에 할 일은 바로 이 메모리 영역을 생성하는 일입니다.

Direct2D의 가장 큰 장점이 바로 이 메모리 영역에 값을 기록하는 작업( 이하 렌더링 )이

GDI를 이용하는 것보다 훨씬 빠르다는 것입니다.

왜냐하면, 바로 이 메모리 영역이 그래픽 카드에 있기 때문입니다.

 

그리기 명령을 수행할 메모리 영역을 생성하기 위해서 다음과 같이 코딩을 합니다.

바로 이 메모리 영역을 렌더타겟( RenderTarget ) 이라 합니다.

 

 

CreateHwndRenderTarget() 의 첫번째 인자는 화면에 대한 정보를 설정합니다.

픽셀 포맷이나 DPI 등의 많은 플래그와 옵션이 있지만, 현재는 디폴트 정보로 넘겨주었습니다.

두 번째 인자는 하드웨어 가속을 받는 렌더링에 대한 옵션을 설정합니다.

간단하게 크기 정보만 넘겨주는 것으로 마무리했습니다.

마지막으로는 이 API 호출이 성공했을 때 리턴되어지는 렌더타겟의 포인터를 저장할 변수를 넣어주었습니다.

이렇게 함으로써 간단하게 우리는 하드웨어 가속을 받을 수 있는 렌더타겟을 생성할 수 있습니다.

 

옵션이나 인자에 대한 설명을 충분히 드리면 좋겠지만, 너무 많습니다.^^

중요한 인자나 옵션에 대해서만 설명 드리는 점 양해 부탁 드립니다.

 

이제 우리는 렌더타겟을 가지고 있으니 이 메모리 영역에 값을 쓰면, 모니터로 결과를 확인할 수 있습니다.

윈도우가 화면에 그리기 위해서 발생하는 메시지가 WM_PAINT 입니다.

저는 이 메시지를 처리해서, 원하는 색상으로 렌더타겟의 색상을 채울 것입니다.

다음과 같이 코딩을 합니다.

 

이번 코드를 실행시키면, 파란색으로 칠해진 윈도우 프로그램을 만나게 될 것입니다.

더 정확하게 얘기하면, 메모리 영역( 렌더타겟 )이 파란색 색상데이터로 채워진 것입니다.

샘플을 올려둡니다.( SimpleDraw.zip )

도움이 되셨으면 좋겠습니다.^^

 


GDI vs Direct2D 비교해 보기.  

제가 아무리 Direct2D가 GDI보다 좋다고 혼자 말하는 것 보다, 여러분들이 직접 결과를 확인하는 것이 좋습니다.

그래서 이번에는 GDI와 Direct2D를 이용해서 타원을 렌더링 할 것입니다.

그리고 결과로 나오는 것을 보고, 여러분들이 직접 확인해 보기 바랍니다.

 

GDI 로 작업하기.

 

GDI를 이용하는 것은 전통적인 윈도우 프로그래밍에서 사용되던 방식입니다.

주변에서 이와 관련한 많은 내용들을 접할 수 있어서, 내용에 대한 자세한 설명은 생략하겠습니다.

Windows 운영체제에서 모든 드로잉(Drawing) 작업은

디바이스 컨텍스트 오브젝트( device-context object )를 통해서 실행이 됩니다.

이를 줄여서 'DC' 라고 줄여서 얘기합니다.( 다 아시죠? ^^ )

DC란, 윈도우즈 운영체제에서 컴퓨터 모니터나 프린터에 그리기 명령을 수행하기 위한

여러 속성 정보들을 구조화한 데이터입니다.

우리는 Windows API를 이용해서 DC를 이용한 그리기 작업을 수행할 수 있습니다.

DC를 이용하면, 우리는 어떠한 하드웨어 장치와는 관련이 없이 공통된 형식으로 화면에 그릴 수 있습니다.

이것이 가능한 이유는 DC는 CPU가 그리기 작업을 처리해 주기 때문입니다.

 

<코드>

HDC hDC;

HBRUSH hBrush,

hOldBrush;

 

if (!(hDC = GetDC (hwnd)))

return;

 

hBrush = CreateSolidBrush (RGB(0, 255, 255));

hOldBrush = SelectObject (hDC, hBrush);

 

Rectangle (hDC, 0, 0, 100, 200);

SelectObject (hDC, hOldBrush);

DeleteObject (hBrush);

ReleaseDC (hwnd, hDC);

</코드>

 

위의 코드는 간단히 DC를 이용해서 사각형을 그리는 코드입니다.

위에서 보는 것과 같이,

GetDC() 라는 API 를 통해서 현재 윈도우 애플리케이션에 대한 DC를 얻어서 작업을 수행합니다.

DC와 관련된 코드를 살펴보면, SelectXXX() 형식을 자주 볼 수 있습니다.

이는 DC가 일종의 상태 정보를 유지하고 있기 때문입니다.

예를 들면, 빨간 펜과 파란 펜으로 두 개 동시에 작업을 할 수는 없습니다.

하나의 펜으로 먼저 작업을 한 후에, 작업한 펜을 제거하고 다음 펜을 선택한 후에 작업을 해야 한다는 얘기입니다.

이점을 잘 고려해서 작업을 해야 하는 것이죠.

 

이것은 DC를 이용하는 하나의 방법일 뿐입니다.

만약 WM_PAINT 메시지 내부에서 처리하고자 한다면, 아래와 같은 구조를 취할 수 있습니다.

<코드>

PAINTSTRUCT ps;

 

case WM_PAINT :

hdc = BeginPaint(hWnd, &ps);

{

DoSomething()

}

EndPaint(hWnd, &ps);

break;

</코드>

 

PAINTSTRUCT 는 내부에 DC 관련 멤버 변수를 가지고 있습니다.

BeginPaint()를 통해서 DC 정보가 채워지게 되는 것입니다.

이러한 DC를 활용하는 것이 GDI 를 이용하는 것의 핵심입니다.

이와 관련된 내용을 더 언급하고 싶지만, 이 정도에서 정리하겠습니다.

이미 많은 분들이 저 보다는 훨씬 많은 지식을 가지고 있을 것이며,

관련 자료들도 이미 훌륭히 찾아 볼 수 있기 때문입니다.^^

 

우리가 지금 하려는 것은 GDI와 Direct2D의 비교입니다.

프로젝트를 만들고, 시스템을 셋팅하시기 바랍니다.( Direct2D 오브젝트도 생성해 주어야 합니다. )

WM_PAINT 메시지에 이 두 가지 방법을 사용해서 렌더링 하는 함수를 호출했습니다.

( 제가 정의한 함수들입니다. )

 

 

먼저, Direct2D를 이용해서 타원을 렌더링 하는 코드를 살펴보겠습니다.

 

이 방법은 뒤에도 설명을 하겠지만, Direct2D에서 DC를 활용하는 렌더링 방법입니다.

몇몇 생소한 개념들이 보이지만, 이들에 대해서는 차후에 설명을 할 것입니다.

 

GDI를 활용해서 타원을 렌더링 하는 코드는 다음과 같습니다.

자, 이제 이 둘의 결과는 예제 코드를 실행시키면, 다음과 같이 확인할 수 있습니다.

 

 

 

이는 타원의 일부를 캡쳐한 화면입니다.

좌측은 Direct2D의 결과이고, 우측은 GDI를 이용한 결과입니다.

겉으로 보기에는 차이가 없어 보이지만,

자세히 보면 우측의 경우는 울퉁불퉁한 계단 현상이 심한 것을 알 수 있습니다.

( 잘 보이지 않는다면, 샘플을 실행시켜보시기 바랍니다.^^ )

분명히 같은 기능을 하는 두 함수이지만, 결과에서는 이렇게 차이가 납니다.

샘플 파일( BasicRender.zip )을 같이 첨부했으니, 도움이 되셨으면 좋겠습니다.^^


댓글을 달아 주세요

  1. 알콜코더 2011.04.15 10:18  댓글주소  수정/삭제  댓글쓰기

    오옷~~ 멋짐!!

  2. 뽀다구 2011.04.15 11:26  댓글주소  수정/삭제  댓글쓰기

    좋은글 잘 봤습니다 ^^ 퇴근 후에 따라해봐야겠어요 ^^

  3. 초보 2011.04.16 23:38  댓글주소  수정/삭제  댓글쓰기

    좋은 글 고맙습니다.

    DC를 Windows API(CPU가 작업 처리)뿐만 아니라 DirectX API (GPU가 작업 처리)로도 다룰 수 있다는 말씀이군요.

    렌더타겟을 하드웨어 가속을 받는 렌더링으로 설정했다면 Direct3D 레이어를 사용하게 되나요?

    Direct2D가 DirectX 10.1 표준의 요구사항으로 알고 있는데 DirectX 11에 와서 달라진 점이 있나요?

    • 흥배 2011.04.17 01:32 신고  댓글주소  수정/삭제

      WDDM 1.1(Windows Display Driver Model 1.1) http://jacking.tistory.com/442 이 글을 참고해보세요

    • 조진현 2011.04.18 10:15 신고  댓글주소  수정/삭제

      흥배형님이 아주 좋은 자료를 링크해주셨네요..^^
      DirectX11 에 와서 딱히 달라진 것은 없습니다.
      DirectX11 자체가 DirectX 10.1 을 더 개선시킨 버전이기에 큰 그림으로 봤을 때는 달라진 것은 없습니다.^^

  4. leafbird 2011.06.03 12:07 신고  댓글주소  수정/삭제  댓글쓰기

    렌더타겟의 EndDraw() 함수가 D2DERR_RECREATE_TARGET 를 리턴하는 경우가 언제인가요? 제가 윈도우 사이즈도 바꿔보고, 화면 해상도도 바꿔봤는데 발생하지 않는 것 같습니다. 풀스크린 / 윈도우 모드를 전환하는 방법이나, D3D9의 디바이스 소실과 같은 경우의 처리도 궁금해요. (이런게 있는지 없는지도 궁금하지만, 그나마 D2DERR_RECREATE_TARGET 에러의 케이스가 디바이스 소실과 가장 비슷한 예외상황인거 같네요.)
    중급자 이상의 내용들도 공유해 주세요 ~

    • 조진현 2011.06.03 12:44 신고  댓글주소  수정/삭제

      기본적으로 최신의 DirectX 에서는 디바이스 소실이라는 것이 존재하지 않습니다.( 예전 버전에는 존재했었지만... )
      문의하신 내용에 대해서는 정확성을 위해서, 조만간 정리를 해서 올려드리겠습니다. ^^

  5. 박상호 2011.06.14 08:34  댓글주소  수정/삭제  댓글쓰기

    제가 단순한 fillrect와 drawline 그리고 drawtext를 GDI를 통해서 그리고 있는데 이 draw에서 프로그램 병목현상이 심하게 생겨서 고심하던중..

    이 블로그에서 GPU, Directx의 글들을 보고 용기를 내어 Directx적용(후엔 TTB적용 예정)해서 속도를 개선해보고자 하였으나 오히려 엄청 더 느려지더군요.
    SetText를 하기 위해서 begindraw와 enddraw이 두 함수에서 엄청 엄청 무지하게 느리더라구요.
    단순 2D Draw시에는 아무 효과가 없나요?

    • 조진현 2011.07.02 16:39 신고  댓글주소  수정/삭제

      어이쿠 제가 너무 늦게 질문을 봤습니다..^^
      언급하신 내용만으로 문제를 예측하기가 힘드네요..
      문제 상황을 간단히 소스로 보내주시면,
      같이 고민해 드릴 수 있을 듯 합니다..^^

    • 김성현 2013.12.20 21:23  댓글주소  수정/삭제

      저도 박상호님과 같은 의견입니다.

      기고자님의 샘플에서, RederForD2D()와 RenderForGDI()
      의 각 함수 엔트리 부분에 걸리는 시간을 체크해 보시면
      RederForD2D() 가 훨씬 시간이 많이 걸립니다.

      어떤면에서 GDI가 더 빨라진건지 궁금합니다.

  6. 박현영 2011.11.01 15:33  댓글주소  수정/삭제  댓글쓰기

    Direct2D를 이용하기 위해서 필요한 언어는 C++인가요?
    그리고 Direct2D를 2D게임 그래픽 출력에 이용할 수 있겠죠? Win32API와 함께 쓰려고 생각중인데요(목적은 기존API의 그래픽 출력보다 속도나 질에서 상향된 성능을 보이는 것)

    • 조진현 2011.11.03 11:00  댓글주소  수정/삭제

      C++ 뿐만 아니라, 코드팩이라는 것을 사용하면 .Net 플랫폼에서도 사용가능합니다.
      C++은 좀 더 저수준 프로그래밍을 하는 것입니다.
      ( 코드팩은 일종의 래퍼입니다.^^ )

      Win32 API 와 함께 사용하는 것도 좋습니다.^^
      조금 더 실용적이고 빠른 제작을 원하시면
      MFC와 함께 사용하는 것도 좋습니다.
      MFC에서는 Direct2D를 조금 더 사용하기 쉽게 클래스화 시켜놓았거든요..^^

      언급하신 성능이나 퀄리티 향상은 당연합니다..^^

  7. 손규호 2014.06.28 03:17  댓글주소  수정/삭제  댓글쓰기

    와 ㅠㅠ 정말 감사합니다...
    msdn reference 보면서 공부하고 있는데 역시 우리 말로 배경 지식까지 언급해 가며 설명한 걸 보니 정말 이해가 잘 되네요...

  8. 이지 2017.08.02 15:58  댓글주소  수정/삭제  댓글쓰기

    감사합니다. direct2D를 처음 공부하고 있는데 설명과 코드가 함께 있어서
    이해하기 조금 더 쉬운 것 같아요 다음 글도 잘 보겠습니다.

  9. 됴타됴아 2017.12.07 06:03  댓글주소  수정/삭제  댓글쓰기

    아이넘나 됴타