일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 네트워크
- Direct3D12
- 게임 디자인 패턴
- DirectX12
- 동적 색인화
- Frustum Culling
- 게임 프로그래밍
- light
- effective C++
- 큐브 매핑
- Dynamic Indexing
- 노멀 맵핑
- 절두체 컬링
- 장치 초기화
- InputManager
- 조명 처리
- 입방체 매핑
- DirectX
- 디퍼드 렌더링
- TCP/IP
- gitscm
- direct3d
- FrameResource
- Deferred Rendering
- 게임 클래스
- gitlab
- 네트워크 게임 프로그래밍
- Render Target
- 직교 투영
- C++
- Today
- Total
코승호딩의 메모장
[장치 초기화] 본문
이번 포스팅에서는 [DirectX12 입문]에서 배운 내용과 인터넷 강의와 서적등을 참고한 내용을 바탕으로 간단한 3D게임을 만들어보려고 합니다. 함수나 변수들에 대한 자세한 내용은 [DirectX12 입문] 포스팅을 참고하시면 됩니다.
[Direct3D 개요와 초기화]
· DirectX 12를 이용한 3D 게임 프로그래밍 입문 : 네이버 도서 네이버 도서 상세정보를 제공합니다. search.shopping.naver.com 이번 포스팅(3D 게임 프로그래밍 입문)에서는 위 책과 한국공학대학교 게임
suengho2257.tistory.com
첫 번째 목표는 Direct3D를 초기화하는데 필요한 Com 객체들을 클래스화 하고 클라이언트 프로젝트와 정적 엔진 프로젝트를 나눠 엔진에서 클래스화된 Com 객체들을 전체적으로 가지고 있는 방식으로 설계합니다.
정적 라이브러리 추가하기
초기화 작업을 하기에 앞서 가장 핵심이 되는 Direct3D 엔진 기능을 정적 라이브러리로 생성하여 클라이언트 프로젝트에서 엔진 라이브러리를 디렉터리에 포함시키고자 한다. 우선 정적 라이브러리를 생성해 보자.
기존 Client 프로젝트의 솔루션에 [추가] -> [새 프로젝트]를 통해 Engine이라는 이름의 정적 라이브러리를 생성한다. pch 파일은 프로젝트마다 있어서 다른 프로젝트를 참조할 수 없다. 따라서 Engine에 EnginePch라는 새로운 헤더를 추가한다. 따라서 Client 프로젝트에서는 Engine을 사용하기 위해 Engine에 있는 pch 파일이 아닌 EnginePch 파일을 Include 하여 사용한다.
출력 파일을 관리하기 위해서 출력 디렉터리에 원하는 폴더 이름을 넣어주면 이제부터 출력 파일은 해당 폴더에 저장된다.
Engine 프로젝트의 출력 파일도 이 폴더에서 관리하기 위해서 Engine의 속성 페이지에서 같은 작업을 수행한다.
이제 Engine 프로젝트의 소스코드들과 lib파일을 Client 프로젝트가 사용할 수 있도록 포함시켜야 한다. Client 속성 페이지에 들어가서 다음과 같이 추가 포함 디렉터리에는 Engine 폴더에 있는 소스 코드들을 추가시켜 주고 링커의 추가 라이브러리 디렉터리에서는 Output 폴더에 생성된 Engine.lib 파일을 위해 Output을 추가시킨다.
// Client/pch.h
#pragma comment(lib, "Engine.lib")
#include "EnginePch.h"
모두 설정이 완료되면 다음과 같이 Client 프로젝트에서 lib파일을 추가시키고 EnginePch 헤더를 추가할 수 있다.
// Client/Game.h
class Game
{
public:
void Init(const WindowInfo& info);
void Update();
};
// Client/Client.cpp
#include "Game.h"
// Global Value
Game gGame;
WindowInfo gWindowInfo;
gWindowInfo.width = 800;
gWindowInfo.height = 600;
gWindowInfo.windowed = true;
gGame.Init(gWindowInfo);
while (true)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
gGame.Update();
}
Client.cpp 파일에는 Winapi를 포함한 잡다한 코드들이 지저분하게 늘어져 있기 때문에 Game이라는 클래스를 따로 생성하여 Client에서 Game 변수를 업데이트하는 식으로 만들어준다. 이때, Engine에 필요한 정보들을 Game에 함께 넘겨준다.
// Engine/EnginePch.h
extern unique_ptr<class Engine> gEngine;
// Engine/EnginePch.cpp
unique_ptr<Engine> gEngine = make_unique<Engine>();
이제 Engine의 객체를 만들어야 한다. Engine 객체는 응용 프로그램에서 딱 한 개만 존재해야 한다. 따라서 EnginePch 파일에 Engine 객체를 전역 객체로 생성해 준다. 이제 Game에서는 gEngine을 통해 자유롭게 접근이 가능하다.
ComPtr 객체 클래스화
다음 그림은 ComPtr 객체들을 클래스화 하여 그들이 가지고 있는 핵심 함수들과 이 클래스화된 객체들을 스마트 포인터 형태로 가지고 있는 Engine의 모습을 보여준다. 위 클래스들은 각자 ComPtr의 형태로 변수화 되어 있다. 예를 들어서 class CommandQueue에서는 ComPtr<ID3D12CommandQueue> cmdQueue을 들고 있다.
void Engine::Init(const WindowInfo& info)
{
//...
_device = make_shared<Device>();
_cmdQueue = make_shared<CommandQueue>();
_swapChain = make_shared<SwapChain>();
_rootSignature = make_shared<RootSignature>();
_device->Init();
_cmdQueue->Init(_device->GetDevice(), _swapChain);
_swapChain->Init(info, _device->GetDevice(), _device->GetDXGI(), _cmdQueue->GetCmdQueue());
_rootSignature->Init(_device->GetDevice());
}
Init 함수에서는 ComPtr 객체들을 생성하고 필요한 변수들을 초기화하며 Engine의 Init에서 이 함수를 불러주면 된다.
void Engine::Render()
{
RenderBegin();
// TODO : 나머지 물체들 그려준다
RenderEnd();
}
void Engine::RenderBegin()
{
_cmdQueue->RenderBegin(&_viewport, &_scissorRect);
}
void Engine::RenderEnd()
{
_cmdQueue->RenderEnd();
}
이렇게 CommandQueue의 렌더 시작과 끝을 나눠준다면 이 사이에 물체를 편하게 그릴 수 있다. CommandQueue의 RenderBegin에서는 명령 할당자와 명령 리스트를 리셋하고 루트 시그니처를 셋하며 백 버퍼와 깊이 버퍼 등을 클리어해 준다. 그리고 뷰포트와 시저렉트도 설정하며 렌더링을 위해 필요한 요소들을 초기화해 준다. 그리고 이 사이에서 DrawInstanced를 통해 그리기 명령을 제출하고 RenderEnd에서 명령들을 명령 큐로 넘겨준다. 마지막으로 모든 그리기가 완료될 때 WaitSync를 통해 동기화를 수행해야 하는데 이를 사용하지 않는 방법을 다음 글에서 올리도록 한다.
모든 초기화 작업이 끝났다면 다음과 같이 아무것도 그려져 있지 않은 창이 뜰 것이다. 내부 색상은 RenderBegin 함수에서의 ClearRenderTargetView의 인자로 Colors::LightSteelBlue를 넘겨주었기 때문에 이런 색상이 뜨는 것이다.
기존에 책을 통해 예제로 공부할 때는 ComPtr 객체들을 클래스화시키지 않고 프레임 워크에서 한 번에 함수를 통해 만드는 방식이었는데 이번에 프로젝트를 시작하면서 ComPtr 객체들을 클래스화 하여 관리하니 굉장히 가독성이 좋아졌으며 각각 어느 역할을 하는지 확실히 알 수 있어 개발의 효율성을 높일 수 있었다. 앞으로도 다양한 객체들을 추가시킬 생각을 하니 기대가 되었다.
'DirectX12 > DirectX12 응용' 카테고리의 다른 글
[SceneManager] (1) | 2023.10.08 |
---|---|
[Component] (0) | 2023.10.07 |
[InputManager와 GameTimer] (1) | 2023.10.07 |
[Material] (0) | 2023.10.07 |
[Texture Mapping] (0) | 2023.10.07 |
[Mesh] (1) | 2023.10.07 |
[ConstantBuffer와 FrameResource] (0) | 2023.10.07 |