DX9을 이용한 3D Game 프로그래밍 입문 - Part.2 Direct3D 기초 1장

김주현·2021년 10월 30일
0

제 1장 Direct3D 초기화

1.1 Direct3D 개요

Direct3D : 가속 하드웨어를 이용해 3D세계를 표현할 수 있도록 해주는 저수준 그래픽 API이다. 쉽게 말해 애플리케이션과 그래픽 장치를 연결하는 중개자. Direct3D가 애플리케이션/프로그래머에게 공개하는 인터페이스와 함수들로 구성되어 있으며, 이들 인터페이스와 함수는 현재 버전의 Direct3D가 지원하는 모든 기능을 나타낸다. 여기서 주의해야 할 점은 Direct3D가 공개하고 있는 기능이라고 해도 그래픽 하드웨어에서 반드시 지원하리라는 보장은 없다는 것이다.

HAL(하드웨어 추상 층) : 시장에 존재하는 그래픽 카드의 종류는 매우 다양하며 각각의 카드는 다양한 방법으로 작동하기 때문에 Direct3D가 직접 그래픽 장치를 제어하기란 불가능하다.HAL은 장치로 하여금 특별한 작업을 수행하도록 하는 장치 고유의 코드로, 이를 통해 Direct3D가 각 장치의 세부적이 부분을 제어할 필요가 없도록 하고,하드웨어 장치에 독립적인 규약을 확립하는 것이 가능하다.

s/w 에물레이션ㅇ; 가능한 버텍스 처리 작업을 제외하고 HAL에서 구현하지 않는 Direct3D 함수를 호출하면 오류가 발생한다. 따라서 원하는 기능을 장치가 제공하는지의 여부를 확인해야한다.

1.1.1 REF 장치

장치에서 지원하지 않지만 d3d에서 제공하는 기능을 이용하고자 하는경우 d3d는 모든 D3D api를 소프트웨어로 에뮬레이트하는 REF를 제공한다.
REF는 SDK에만 포함되며 최종사용자에게는 배포할수없고 상당히 느리다.

1.1.2 D3DDEVTYPE

HAL 장치는 D3DDEVTYPE 열거형의 멤버인 D3DDEVTYPE_HAL로 지정된다. REF 장치 역시 D3DDEVTYPE 열거형의 멤버인 D3DDEVTYPE_REF로 지정된다. 우리의 장치를 만들 때는 어떤 타입을 원하는지를 지정해야한다.

1.2 COM

COM(컴포넌트 객체 모델)은 DX를 프로그래밍 언어에 독립적으로 만들어주고 하위 호환성을 갖출 수 있게 하는 기술이다. 보통 COM객체를 인터페이스라 부르며 C++클래스와 비슷하게 이용됨
다른 COM 인터페이스의 메서드나 특수한 함수를 통해 COM 인터페이스의 포인터를 얻는다는 것이며,C++ NEW 키워드로 COM인터페이스를 만드는 것이 아니다.

인터페이스 이용작업이 모두 끝나면 우리가 직접 지우는것이 아닌 인터페이스의 Release 메서드를 호출해야 한다. COM 객체는 자신의 메모리 관리를 스스로 수행한다.

1.3 약간의 준비

D3D의 초기화 과정을 수행하기 위해서는 먼저 몇 가지의 기본적인 그래픽 개념과 D3D 형에 대한 이해가 필요하다.

1.3.1 표면

표면 : D3D가 주로 2D 이미지 데이터를 보관하는 데 이용하는 픽셀의 행렬.
그림에서는 표면 데이터를 행렬로 그려냈지만, 실제로 픽셀 데이터는 선형 배열에 보관됨

표면의 너비와 높이는 픽셀 단위로 계산되고 피치는 바이트로 계산되며 H/W 구현에 따라서는 너비보다 길 수도 있다.(무조건 피치 = 너비 * sizeof(pixelFormat)은 아니라는 점)

코드에서 표면을 이용하는 데는 IDirect3DSurface9 인터페이스가 사용된다.

*LockRect - 이 메서드는 표면 메모리로의 포인터를 제공한다. 여기서 약간의 포인터 연산을 거치며 표면 내의 각 픽셀을 읽고 쓸 수 있게 된다.

*UnlockRect - LockRect를 호출하고 표면 메모리에 대한 작업이 끝난 뒤에는 이 메소드를 호출하여 표면의 잠금을 해제해야 한다.

*GetDesc - 표면에 대한 정보를 D3DSURFACE_DESC 구조체를 통해 얻는다.

//_surface가 IDirect3DSurface9 인터페이스로의 포인터라고 가정한다.
// 32-비트 픽셀 포맷을 이용한다고 가정한다.

// 표면  정보를 얻는다.
D3DSURFACE_DESC surfaceDesc;
_surface->GetDesc(&surfaceDesc);

// 표면 픽셀 데이터로의 포인터를 얻는다.
D3DLOCKED_RECT lockedRect;
_surface->LockRect(
	&lockedRect, // 잠근 데이터를 얻을 포인터
    0,			// 전체 표면을 잠근다.
    0);			// 잠금 플래그를 지정하지 않는다.
 // 표면의 각 픽셀을 대상으로 반복하여 픽셀을 빨간색으로 지정한다.
 
 DWORD* imageData = (DWORD*)lockedRect.pBits;
 for(int i = 0 ; i < surfaceDesc.Height; i++)
 {
 	 for(int j = 0 ; j < surfaceDesc.Width; i++)
     // 피치는 바이트 단위이며 DWORD당 4바이트이므로
     // 피치를 4로 나누었음에 주의하자.
     int index = i * lockedRect.Pitch / 4 + j;
     
     imageData[index] = 0ffff0000;
     }
 }
 
 _surface_UnlockRect();

D3DLOCKED_RECT 구조체는 다음과 같이 정의한다.

typedef struct _D3DLOCKED_RECT {
INT Pitch //표면 피치
void *pBits //표면 메모리 시작을 가리키는 포인터
) D3DLOCKED_RECT

이 코드는 픽셀 포맷에 32-비트 픽셀 포맷을 이용한다고 가정하였으므로 32-비트인 DWORD로 혀 변환을 수행하였따. 즉, 하나의 픽셀을 DWORD로 취급할 수 있게 되었다.

1.3.2 멀티 샘플링

멀티 샘플링은 픽셀 매트릭스로 이미지를 표현할 때 나타나는 거친 임지를 부드럽게 만드는 데 이용되는 기술이다. 멀티 샘플링의 가장 일반적인 용도로는 전화면 안티알리아싱을 위한 표면 멀티 샘플링이 있다.

D3DMULTISAMPLE_TYPE 열거형은 표면에 적용할 멀티 샘플링 레벨을 지정할 수 있도록 하는 값들로 구성되어 있다.

D3DMULTISAMPLE_NONE - 멀티 샘플링을 지정하지 않는다.
D3DMULTISAMPLE_1_SAMPLE(1~16까지있다) - 1에서 16까지의 멀티 샘플링 레벨을 지정한다.

멀티 샘플링은 애플리케이션의 속도를 지나치게 떨어뜨린다.

1.3.3 픽셀 포맷

표면이나 텍스처를 만들기 위해서는 D3D 자원의 픽셀 포맷을 지정해야 한다. 픽셀 포맷은 D3DFORMAT 열거형 멤버로 지정되며, 자주 이용되는 포맷은 다음과 같다.

-NOTE-
처음 세개의 포맷은 거의 대부분 H/W에서 지원되지만, 그 밖의 부동소수점 픽셀 포맷이나 나머지 포맷들은 그리 널리 지원되지 않는다.사용하기전 보유한 카드에서 원하는 포맷을 지원하는지를 확인해야 한다.

1.3.4 메모리 풀

표면이나 그 밖의 다양한 D3D 자원들은 여러 가지 종류의 메모리 풀에 보관 할 수 있다. 메모리 풀은 D3DPOOL 열거형의 멤버로 지정되며, 이용할 수 있는 메모리 풀에는 다음과 같은 것들이 있다.

*D3DPOOL_DEFAULT - 디폴트 메모리풀은 자원의 타입과 이용 방식에 가장 적합한 자원들을 메모리에 보관하도록 D3D에 요청한다. 여기서 말하는 자원이란 비디오 메모리나 AGP 메모리, 혹은 시스템 메모리 등을 말하는 것으로, 디폴트 풀 내의 자원은 반드시 IDIRECT3DDEVICE9::Reset 호출 이전에 해제되어야 하며, reset 호출 이후에 다시 초기화되어야 한다.

*D3DPOOL-MANAGED - 관리 풀에 보관된 자원은 D3D에 의해 보관된다(즉, 필요에 따라 자동으로 이를 비디오 메모리에 갱신한다.

*D3DPOOL_SYSTEMMEM - 시스템 메모리 내에 보관될 자원을 지정한다.

*D3DPOOL_SCRATH - 시스템 메모리 내에 보관될 자원을 지정한다. 위 멤버와는 달리 이 풀의 자원은 그래픽 장치의 제한을 따라서는 안 된다. 따라서 장치는 이 풀 내의 자원에 직접 접근할 수 없지만 자원을 두 풀 사이에 서로 복사하는 것은 가능하다.

1.3.5 스왑 체인과 페이지 플리핑

D3D는 보통 두 개나 세 개의 표면을 하나의 컬렉션으로 관리하며, 이를 스왑체인이라 부른다. 스왑 체인은 IDrect3DSwapChain9 인터페이스를 통해 이용할 수 있지만, 대부분의 작업은 Direct3D가 직접 관리하므로 우리가 이 인터페이스를 이용하는 경우는 거의 없다.

스왑체인과 페이징 플리핑 기술은 프레임 간의 부드러운 애니메이션을 제공하기 위한것이다.

전면버퍼 : 이버퍼의 내용물은 현재 모니터에서 보여지고 있는 것이다.

후면버퍼 : 현재 처리중인 프레임이 이 버퍼에 보관된다.

애플이케이션의 프레임율과 모니터의 재생율이 동기화되지 않은 경우
후면 버퍼에 렌더링을 수행하고, 전면 버퍼 표면의 디스플레이가 완료되면 스왑 체인의 끝으로 돌아가 후면 버퍼를 전면 버퍼로 전환하는 방법을 이용한다. 이와 같은 과정을 시연이라 한다.

렌더링 코드의 구조는 다음과 같다.

  1. 후면 버퍼에 렌더링을 한다.
  2. 후면 버퍼와 전면버퍼를 교체한다.(전면 버퍼에는 렌더링이 한번에 완료되는것으로 보임)
  3. 1번으로 돌아간다.

1.3.6 깊이 버퍼

깊이 버퍼는 이미지 데이터가 아닌 특정 픽셀의 깊이 정보를 포함하는 표면을 말하며,깊이 버퍼 내에는 최종 렌더링된 이미지의 각 픽셀에 해당하는 항목들을 포함함. 이미지가 100 100 라면 깊이 버퍼도 100 100

D3D는 물체의 픽셀이 다른 픽셀을 가리는지의 여부를 판단하기 위해 깊이 버퍼링을 이용

깊이 버퍼링은 각 픽셀의 깊이 값을 계산하고 깊이 테스트를 수행함으로서 이루어지며, 깊이 테스트는 특정 픽셀의 위치에서 경쟁하는 픽셀의 깊이를 비교하는 과정이다.

깊이 버퍼의 포맷은 깊이 테스트의 정확도를 결정한다.

D3DFMT_D24S8 24비트깊이 버퍼를 지정하고 8비트 스텐실 버퍼를 예약한다.

1.3.7 버텍스 프로세싱

버텍스는 3D 기하물체를 구성하는 기본단위로 S/W(언제나 가능), H/W(그래픽 카드가 지원하는 경우(카드가 변환과 조명 계산을 하드웨어적으로 처리 가능)에만 가능) 두 가지 방법으로 처리할 수 있다.
H/W가 성능이 우수하고 CPU를 다른계산에 할당할수있기때문에 유리하다.

1.3.8 장치 특성

D3D가 제공하는 모든 기능들은 D3DCAPS9 구조체 내의 비트와 데이터 멤버에 대응된다. 기본적인 방식은 특정 하드웨어의 특성에 따라 D3DCAPS9 인스턴스의 멤버를 초기화하는것(우리는 인스턴스와 대응되는 비트나 데이터 멤버를 확인하여 장치가 해당 기능을 제공하는지 확인할 수 있다.)

1.4 D3D 초기화하기

  1. IDirect3D9 인터페이스로의 포인터를 얻는다. 이 인터페이스는 시스템의 물리적 하드웨어 장치에 대한 정보를 얻고 3D 그래픽을 디스플레이하는 데 이용하는 물리적 하드웨어 장치는 나타내는 C++객체인 IDirect3DDevice9 인터페이스를 만드는 데 이용된다.

  2. 기본 그래픽 카드가 하드웨어 버텍스 프로세싱을 지원하는지 알아보기 위해 장치 특성을 확인한다. IDirect3DDevice9 인터페이스를 만들기 위해서는 하드웨어 지원 여부를 먼저 확인해야 한다.

3.D3DPRESENT_PARAMETERS 구조체 인스턴스를 초기화한다. 이 구조체는 우리가 만들고자하는 IDirect3DDevice9 인스턴스의 특성을 지정하기 위한 몇 가지 데이터 멤버들을 포함한다.

4.초기화된 D3DPRESENT_PARAMETERS에 따라 IDirect3DDevice9 객체를 만들어낸다. 앞서 언급했듯이 IDirect3DDevice9 객체는 3D 그래픽을 디스플레이하는데 이용될 물리 하드웨어 장치를 나타내는 C++객체이다.

1.4.1 IDirect3D9 인터페이스 얻기

Direct3D 초기화는 IDirect3D9 인터페이스로의 포인터를 얻는 것으로 시작한다.

IDirect3D9* _d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

Direct3DCreate9의 단일 인자에는 항상 D3D_SDK_VERSION을 전달해야 한다.
이 인자는 애플리케이션이 올바른 헤더 파일로 만들어졌음을 확인하기 위한 것으로, 만약 함수가 실패하면 null포인터를 리턴한다.

IDirect3D9 객체는 장치 검증과 IDirect3DDevice9 객체 생성의 두 가지 용도로 이용된다.

장치 검증 : 기능,디스플레이 모드,포맷 등과 같이 시스템의 그래픽 장치가 제공하는 특성에 대한 정보를 얻는 과정

1.4.2 하드웨어 버텍스 프로세싱 확인하기

기본 디스플레이 어댑터와 대응되는 IDirect3DDevice9 객체를 생성할 때는 반드시 원하는 버텍스 프로세싱 모드를 지정해야함( 가능하면 하드웨어 버텍스 프로세싱을 선택하는게 좋고 이를 위해 카드의 해당 지원 여부를 파악해야함)

이를 위해 기본 디스플레이 어댑터의 특성에 따라 D3DCAPS9 인스턴스를 초기화해야함

HRESULT iDirect3D9::GetDeviceCaps(
UINT Adaper, - 특성을 얻고자 하는 물리 디스플레이 어댑터를 지정한다.
D3DDEVTYPE DeviceType, -이용할 장치 타입을 지정한다
D3DCAPS9 *pCaps - 초기화된 특성 구조체를 리턴한다.
);

다음 if 문을 통해 (d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)를비교하고 true라면 vp = D3DCREATE HARDWARE VERTEXPROCESSING ;를
false라면 vp = D3DCREATE SOFTWARE VERTEXPROCESSING; 값을 넣는다.

여기서 버텍스 프로세싱의 타입을 변수 VP에 보관했음을 주의하자. 이 정보를 보관하는 이유는 이후에 iDirect3DDevice9 객체를 만들 때 버텍스 프로세싱의 타입을 지정해야 하기 때문이다.

1.4.3 D3DPRESENT_PARAMETERS 구조체 채우기

초기화 과정의 다음 단계는 D3DPRESENT_PARAMETERS 구조체 인스턴스를 채우는것이다. 이 구조체는 IDirect3DDevice9 객체의 성격을 결정한다.

typedef struct _D3DPRESENT_PARAMETERS
UINT BackBufferWidth ; -픽셀 단위의 후면 버퍼 너비
UINT BackBufferHeight ; -픽셀 단위의 후면 버퍼 높이
D3DFORMAT BackBufferFormat; -후면 버퍼의 픽셀 포맷(예를 들어,32-비트 픽셀 포맷:D3DFMT_A8R8G8B8)
UINT BackBufferCount ; -이용할 후면 버퍼의수. 보통 1
D3DMULTISAMPLE TYPE MultiSampleType ;- 후면 버퍼에 이용할 멀티 샘플링 타입
DWORD MultiSampleQuality ; - 멀티 샘플링의 레벨
D3DSWAPEFFECT SwapEffect;-플리핑 체인의 버퍼가 교환되는 방법을 지정 D3DSWAPEFFECT_DISCARD를 지정하는 것이 가장 효과적이다.
HWND hDeviceWindow; -서비스와 연결된 윈도우 핸들,드로잉의 대상이 될 애플리케이션 윈도우를 지정한다.
BOOL Windowed ; -윈도우 모드로 실행줄일때는 TRUE를,전체 화면 모드로 실행중일 떄는 FALSE를 지정
BOOL EnableAutoDepthStencil ; - Direct3D가 자동으로 깊이/스텐실 버퍼를 만들고 관리하길 원한다면 TRUE를 지정한다.
D3DFORMAT AutoDepthStencilFormat; -깊이/스텐실 버퍼의 포맷(예ㅡ 24비트 깊이 버퍼와 스텐실 버퍼로8비트 예약 :D3DFMT_D24S8
DWORD Flags; -몇 가지 부가적인 특성들. 0혹은 D3DPRESENTFLAG집합의 멤버중 하나를 지정
UINT FullScreen RefreshRatelnHz; - 재생율을 지정한다. D3DPRESENT_INTERVAL_DEFAULT를 지정하면 디폴트 재생율을 이용한다.
UINT Presentationlnterval ; - D3DPRESENT_INTERVAL_IMMEDIATE 즉시 시연한다.
} D3DPRESENT PARAMETERS;

1.4.4 IDirect3DDevice9 인터페이스 만들기

D3DPRESENT_PARAMETERS를 채운 뒤에는 다음의 메소드를 이용해 IDirect3DDEvice9 객체를 만들 수 있다.

HRESULT IDirect3D9 : :CreateDevice(
UINT Adapter, - 만들어질 IDirect3DDevice9 객체와 대응될 물리 디스플레이 어댑터를 지정
D3DDEVTYPE DeviceType , - 이용할 장치타입을 지정한다(H/W OR S/W)
HWND hFocusWindow,-장치와 연결될 윈도우 핸들.보통은 장치가 드로잉을 수행할 윈도우가 된다.
DWORD BehaviorFlags, -위에서 구한 vp를 넣어준다.
D3DPRESENT PARAMETERS *pPresentationParameters, -위에서 구한 파라미터 인스턴스를 지정한다.
IDirect3DDevice9** ppReturnedDevicelnterface -생성된 장치를 리턴한다.
);

1.6 요약

  • D3D는 프로그래머와 그래픽 하드웨어를 연결하는 하나의 매개자로 생각할 수 있다. 프로그래머는 D3D 함수를 호출하며, 물리적 하드웨어는 장치의 HAL(하드웨어 추상 레이어)과의 인터페이싱을 통해 프로그래머가 요청한 작업을 수행한다.

  • REF 장치는 개발자의 장치에서 지원하지 않는 기능을 이용하는 D3D 애플리케이션을 테스트할 수 있도록 해준다.

  • 컴객체는 DX를 프로그래밍 언어에 독립적이고 하위 호환성을 가질 수 있도록 해주는 기술이다. D3D프로그래머는 COM의 세부적인 부분이나 작동 원리에 대해 알 필요는 없으며, COM 인터페이스를 얻고 해제하는 방법만을 기억하면된다.

  • 표면은 2D 이미지를 보관하는 데 이용되는 특별한 D3D 인터페이스로, D3DFORMAT 열거형의 멤버를 이용해 표면의 픽셀 포맷을 지정한다. 표면이나 그 밖의 D3D의 자원들은 D3DPOOL 열거형의 멤버로 지정하는 여러 가지 다른 메모리 풀에 보관될 수 있으며, 부가적으로 표면에 멀티 샘플링 처리를 적용하여 좀더 부드러운 이미지를 만들어낼 수 있다.

  • IDirect3D9 인터페이스를 이용하면 시스템의 그래픽 장치에 대한 정보를 얻을 수 있다. 예를 들어,이 인터페이스를 통해 장치 특성에 대한 정보를 얻고 IDirect3DDevice9 인터페이스를 만드는 데 이정보를 이용할 수 있다.

  • IDirect3DDevice9 인터페이스는 그래픽 장치를 제어하기 위한 소프트웨어 인터페이스이다. 예를 들어, IDirect3DDevice9::Clear 메소드를 호출하면 간접적으로 그래픽 장치의 지정된 표면을 소거한다.

1개의 댓글

comment-user-thumbnail
2024년 9월 15일

Sophia had always been meticulous with her routines. One ordinary Wednesday, feeling the weight of daily monotony, she decided to deviate https://casinoozwin1.com/ from her usual evening of reading. Instead, she explored a new online casino game she had heard about from a friend. The game, a colorful and whimsical slot machine featuring mythical creatures, instantly captured her attention. She set a modest budget, not expecting much more than a brief distraction from her routine.

답글 달기

관련 채용 정보