Direct3D12でimguiを使ってみる
Direct3Dのアプリを書くには、デバッグ描画が必要です。デバッグ描画とは、例えば、画面上にフレームレートや様々な設定を描画するものです。 最終的な商品では使われないものなので、切り離してコンパイルできるように作りたいです。
この辺のサイトを参考に組み込んでみました。
https://zenn.dev/norainu/articles/10856bfe120aa2
分離できるように別クラスにまとめます。
class ImGuiUtil {
public:
ImGuiUtil() {
}
virtual ~ImGuiUtil() {
}
// 初期化
void Initialize(HWND hWnd, ID3D12Device *pDevice)
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
ImGui::StyleColorsLight();
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 1;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
pDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_srvHeap));
ImGui_ImplWin32_Init(hWnd);
ImGui_ImplDX12_Init(pDevice, FRAME_BUFFER_COUNT,
DXGI_FORMAT_R8G8B8A8_UNORM, m_srvHeap.Get(),
m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
m_srvHeap->GetGPUDescriptorHandleForHeapStart());
}
// 終了処理
void Finalize()
{
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
// フレームの先頭で呼ぶ
void NewFrame()
{
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
}
// 描画の最後に呼ぶ
void Render(ID3D12GraphicsCommandList *pCommandList)
{
ImGui::Render();
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap.Get() };
pCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), pCommandList);
}
private:
ComPtr<ID3D12DescriptorHeap> m_srvHeap;
};
これと、Windowのイベント処理関数の先頭に、Imguiのウィンドウ関数を追加します。
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// ImGuiのウィンドウ処理
if (ImGui_ImplWin32_WndProcHandler(hWnd, message, wParam, lParam)) {
return true;
}
これで、プロジェクトに以下のファイルを追加して、コンパイルしたら動きます。
試しに、フレームレートを表示しました。
// DebugWindow
ImGui::Begin("DebugWindow");
ImGui::Text("fps : %.2f", framerate);
ImGui::End();
便利ですね。
組み込み時にはまった点をメモしておきます。
(1) 描画時にimgui用のヒープを設定せずにエラーになった
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap.Get() };
pCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), pCommandList);
(2) ウィンドウサイズを間違って設定して、ドットバイドットで描画されていなかった。
ウィンドウを作成するときのサイズが、枠とかも入れたサイズで、描画領域のサイズとずれていたために、imguiの計算とずれていました。
こんな感じでバッファのサイズを取得して修正しました。
// バッファサイズからウィンドウのサイズを求める
DWORD windowStyle = WS_OVERLAPPEDWINDOW;
RECT rc = { 0, 0, INITIAL_WIDTH, INITIAL_HEIGHT };
AdjustWindowRect(&rc, windowStyle, FALSE);
HWND hWnd = CreateWindow(wcex.lpszClassName, "Direct3D 12 Sample", windowStyle, 100, 100, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, wcex.hInstance, nullptr);
// ウィンドウの描画領域のサイズをバッファのサイズに設定します
RECT rect;
GetClientRect(hWnd, &rect);
int sw = rect.right - rect.left;
int sh = rect.bottom - rect.top;