0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DirectX12でミクさんを躍らせてみよう4-リファクタリング

Last updated at Posted at 2024-10-01

前回

リファクタリング

前の記事では、レンダリングに必要な最小限のものを見ました。

1つのcppファイルのmainメソッドですべてを処理しました。
これからはモデルを読み込んでレンダリングしたり、モデルのタイプによって異なる処理をする必要があるため、これまでに作成したコードを一度整理する必要があります。

どのように構成するかをすべてお見せするのは意味がないので、私がどのように整理したかを大まかにお話しします。別の方法で整理する方が良いと思われる場合は、ぜひ自分でリファクタリングしてみてください。

Application クラス

私はウィンドウの生成とメインループを持つクラスとして、Applicationというクラスを作成しました。
実装部は次のようになっています。

class Application
{
public:
	bool Init();
	void Run();
	void Terminate();
	SIZE GetWindowSize() const;
	~Application();

private:
	WNDCLASSEX mWindowClass;
	HWND mHwnd;
	HINSTANCE mhInstance;

	void CreateGameWindow(HWND& hwnd, WNDCLASSEX& windowClass);

	Application();
	Application(const Application&) = delete;
	void operator=(const Application&) = delete;
};

Initではウィンドウを生成するための作業を行い、その後追加されるオブジェクトの初期化もこの部分で行います。

Runはメインループを持っています。

TerminateはRunを抜けると、プログラムが終了する前に呼び出されます。

mainメソッドを見ると、このようになるでしょう。

int main()
{
	auto& app = Application::Instance();
	if (!app.Init()) {
		return -1;
	}
	app.Run();
	app.Terminate();
	return 0;
}

Dx12Wrapper クラス

このクラスは DirectX オブジェクトを整理したクラスです。

class Dx12Wrapper
{
	template<typename T>
	using ComPtr = Microsoft::WRL::ComPtr<T>;

public:
	Dx12Wrapper(HWND hwnd);
	~Dx12Wrapper();

	void Clear();
	void Update();
	void EndDraw();

	ComPtr<ID3D12Device> Device();
	ComPtr<ID3D12GraphicsCommandList> CommandList();
	ComPtr<IDXGISwapChain4> SwapChain();

	DirectX::XMMATRIX GetViewMatrix() const;
	DirectX::XMMATRIX GetProjectionMatrix() const;

private:
	HRESULT InitializeDXGIDevice();
	HRESULT InitializeCommand();
	HRESULT CreateSwapChain(const HWND& hwnd);

	SIZE mWindowSize;

	ComPtr<IDXGIFactory6> mDXGIFactory = nullptr;
	ComPtr<ID3D12Device> mDevice = nullptr;
	ComPtr<ID3D12CommandAllocator> mCmdAllocator = nullptr;
	ComPtr<ID3D12GraphicsCommandList> mCmdList = nullptr;
	ComPtr<ID3D12CommandQueue> mCmdQueue = nullptr;
	ComPtr<IDXGISwapChain4> mSwapChain = nullptr;
	ComPtr<ID3D12DescriptorHeap> mRtvHeaps = nullptr;
	std::vector<ID3D12Resource*> mBackBuffers;
	std::unique_ptr<D3D12_VIEWPORT> mViewport;
	std::unique_ptr<D3D12_RECT> mScissorRect;
	ComPtr<ID3D12Fence> mFence = nullptr;
	UINT64 mFenceVal = 0;
};

前の記事で生成したDirectXオブジェクトを保持し、必要な時にデバイス、コマンドリスト、スワップチェインを返すことができるようにします。

Clearでは、バックバッファのリソース状態をレンダーターゲットに変更し、レンダーターゲットを設定して黒でクリアします。

Updateは、毎フレーム必要な処理を入れるメソッドです。
EndDrawでは、バックバッファのリソース状態をPresent状態に変更し、命令を実行してフェンスで命令が終わるのを待ち、次にPresentします。

そして、コンストラクタでDirectXの初期化を行う前に、このようなコードを追加してください。

#ifdef _DEBUG
	ID3D12Debug* debugLayer = nullptr;
	auto result = D3D12GetDebugInterface(IID_PPV_ARGS(&debugLayer));

	debugLayer->EnableDebugLayer();
	debugLayer->Release();
#endif 

これはデバッグレイヤーを有効にするコードです。
DirectXプログラミングをしていると、設定ミスや何らかの理由でオブジェクトの作成に失敗することがあります。通常、このような場合、失敗の理由を見つけるのは難しいです。

このようにデバッグレイヤーを有効にすると、VisualStudioの出力ウィンドウにDirectXオブジェクトの作成に失敗した場合、その理由が表示されます。
プリプロセッサを使用して、デバッグ環境でデバッグレイヤーを有効にできるようにしましょう。

Render クラス

私はこのクラスで全体的なレンダリングの順序を管理しようとしています。Dx12WrapperはDirectXのAPIを呼び出す手段として使用し、Dx12Wrapperオブジェクトを持って以降に追加されるモデルレンダリングを含め、全体的な呼び出し順序をここで定義します。

class Render
{
public:
	Render(std::shared_ptr<Dx12Wrapper>& dx);

	void Frame();

private:
	void Update() const;
	void DrawFrame() const;
	void EndOfFrame() const;
}

今は特別なことはありませんが、この次にPMXモデルのレンダリングに関するオブジェクトをここで持つ予定です。

このRenderオブジェクトをApplicationオブジェクトが持つことになり、RunでRenderのFrameが呼ばれる構造になるでしょう。

class Application
{
public:
	bool Init();
	void Run();
	void Terminate();
	SIZE GetWindowSize() const;
	~Application();

private:
	WNDCLASSEX mWindowClass;
	HWND mHwnd;
	HINSTANCE mhInstance;
	std::shared_ptr<Render> mRender = nullptr; //追加

	void CreateGameWindow(HWND& hwnd, WNDCLASSEX& windowClass);

	Application();
	Application(const Application&) = delete;
	void operator=(const Application&) = delete;
};
void Application::Run()
{
	ShowWindow(mHwnd, SW_SHOW);

	while (true)
	{
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if (msg.message == WM_QUIT)
		{
			break;
		}

		if (Input::Instance()->Update() == false)
		{
			break;
		}

		mRender->Frame(); //毎フレームごとに呼ぶ
	}
}

次回

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?