LoginSignup
4
3

More than 1 year has passed since last update.

[Vulkan+GLFW] ImGUIの導入

Posted at

ImGuiとは

C++のGUIを作成するライブラリ(OSS)です。
例えばRenderingで何かを描画する際のデバッグに重宝し、すこしパラメータを変えるためにわざわざコンパイルし直さなくても、実行中に値を直接変えられるので便利です。その他にも、C++でのGUI開発環境を必要としている人には刺さりそうな機能だと思われます。
後にリンクを貼りますが、GitHubからインストールします。ただ導入にすこし癖があり、自分でwindowやRendererを用意しなければなりません。
たとえば(DirectX12+win32)や(Vulkan+GLFW)などの組み合わせが考えられます。
< ImGui GitHubより >
・Renderers: DirectX9, DirectX10, DirectX11, DirectX12, Metal, OpenGL/ES/ES2, Vulkan, WebGPU.
・Platforms: GLFW, SDL2, Win32, Glut, OSX, Android.
・Frameworks: Emscripten, Allegro5, Marmalade.

出力例

準備

ImGuiのインストール

以下のGitHubからdownload/cloneをしてインストールします。
https://github.com/ocornut/imgui

インクルードとリンク

インストールが完了すればパスを通し、自分のプログラムにincludeします。私の環境では以下をincludeしました。
基本ヘッダー : imgui.h
glfw用ヘッダー : backends/imgui_impl_glfw.h
vulkan用ヘッダー : backends/imgui_impl_vulkan.h

同じ名前のソースファイル(.cpp)もあるので、リンクしておきます。

README

インストールしたフォルダにexampleフォルダがあります。
さらにその配下のexample_Platform_Rendererのフォルダ内にmain.cppという公式の具体例が用意されているので、基本的にはそれを参照します。(例えばexample_glfw_vulkan/main.cpp)
以降の記事ではこうすれば動いたというのを紹介します。

環境

ImGui自体はWindows/Linux/Macなどで動作させることができます。この記事では以下の私の環境で初期化するまでに必要だったことを書きます。この環境でない方は、先程の公式例のmain.cppを参考に導入してみてください。
・OS : Linux
・Platforms : GLFW
・Renderers : Vulkan

前提知識

この記事の目的はImGuiの導入の記事であるため、VulkanやGLFWなどの基本的なGraphics APIの知識がある、つまりVulkan Instanceなどは作成できることを前提とします。Vulkanについては公式Khronosのtutorialのページを見るか、他のQiitaの記事などをご参考ください。

Vulkan+GLFWでのコード

Vulkan module

私はmain windowに描画したいgraphicsを出力させ、別のsub windowsにデバッグ用のImGuiを描画させるという目的で使用しています。ですのでこの場合、いくつかのVulkan moduleはmain windowとsub windowで共有できます。
< 共有したmodule >
・Vulkan Instance
・Vulkan Physical Device
・Vulkan Logical Device
・Renderer Queue
・Present Queue

< ImGui用に新たに作成したmodule >
・Window
・Surface
・Swapchain
・Depth Image
・Render Pass
・Frame Buffer
・Descriptor Pool
・Semaphores
・Command pool
・Command buffer

初期化処理

初期化処理はrendering用のメインループの外で行います。

init imgui

mContext = ImGui::CreateContext();
ImGui::SetCurrentContext(mContext);
ImGui_ImplGlfw_InitForVulkan(mWindow, true);
ImGui::StyleColorsDark();
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = instance;
init_info.PhysicalDevice = mPhysicalDevice;
init_info.Device = mDevice;
init_info.QueueFamily = mQueueFamilyIndex;
init_info.Queue = mQueue;
init_info.PipelineCache = VK_NULL_HANDLE;
init_info.DescriptorPool = mPool;
init_info.Allocator = VK_NULL_HANDLE;
init_info.MinImageCount = 2;
init_info.ImageCount = mSwapchainSize;
init_info.CheckVkResultFn = nullptr;
ImGui_ImplVulkan_Init(&init_info, mRenderPass);

upload fonts

//command begin
VulkanCommand::BeginCommand(mCommandBuffer);
//impl fonts
ImGui_ImplVulkan_CreateFontsTexture(mCommandBuffer);
//command end
VulkanCommand::EndCommand(mCommandBuffer);
VkSubmitInfo end_info = {};
end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
end_info.commandBufferCount = 1;
end_info.pCommandBuffers = &mCommandBuffer;
vkQueueSubmit(mQueue, 1, &end_info, VK_NULL_HANDLE);
vkDeviceWaitIdle(mDevice);
ImGui_ImplVulkan_DestroyFontUploadObjects();

メインループ内で行う処理

以下のrender, presentの関数をメインループ内で呼び出します。

render

void Render(uint32_t index)
{
    ImGui::Render();
    ImDrawData* drawData = ImGui::GetDrawData();
    VulkanCommand::BeginCommand(mCommandBuffer);
    VulkanCommand::BeginRenderPass(index, mCommandBuffer, mFrameBuffers[index]);
    ImGui_ImplVulkan_RenderDrawData(drawData,mCommandBuffer);
    VulkanCommand::EndRenderPass(mCommandBuffer);
    VulkanCommand::EndCommand(mCommandBuffer);
}

present

void Present(uint32_t index)
{
    uint32_t imageIndex;
    VkResult result = vkAcquireNextImageKHR(mDevice, mSwapchain,std::numeric_limits<uint64_t>::max(), 
                       mImageSemaphores[index], VK_NULL_HANDLE, &imageIndex);
    //submit info
    VkSemaphore waitSemaphores[] = {mImageSemaphores[index]};
    VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
    VkSemaphore signalSemaphores[] = { mRenderSemaphores[index]};
    VkSubmitInfo submitInfo0 = {};
    submitInfo0.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo0.waitSemaphoreCount = 1;
    submitInfo0.pWaitSemaphores = waitSemaphores;
    submitInfo0.pWaitDstStageMask = waitStages;
    submitInfo0.commandBufferCount = 1;
    submitInfo0.pCommandBuffers = mCommandBuffer;
    submitInfo0.signalSemaphoreCount = 1;
    submitInfo0.pSignalSemaphores = signalSemaphores;
    VkSubmitInfo submitInfo[] = {submitInfo0};
    vkResetFences(*mDevice->GetDevice(), 1, mFences[index]);
    if (vkQueueSubmit(mQueue, 1, submitInfo, mFences[index]) != VK_SUCCESS)
        std::runtime_error("failed to submit draw command buffer");
    //present info
    VkPresentInfoKHR info = {};
    info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    info.waitSemaphoreCount = 1;
    info.pWaitSemaphores = signalSemaphores;
    info.swapchainCount = 1;
    info.pSwapchains = mSwapchain;
    info.pImageIndices = &imageIndex;
    result = vkQueuePresentKHR(mQueuePresent, &info);
}

メインループ

while(/*your condition*/){
    glfwPollEvents();
    : //other functions
    ImGui_ImplVulkan_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
    ImGui::Begin("Parameters");
    if (ImGui::Button("reset time"))     
    {
        startTime = lastTime;
        *passedTime = 0.0f;
    }
    if (ImGui::Button("pause"))   
        paused = !paused;
    ImGui::SameLine();
    ImGui::Text("time = %.3f", *passedTime);
    ImGui::InputFloat("wave speed", waterSurface->GetWaveSpeed());
    ImGui::InputFloat("wave freq", waterSurface->GetWaveFreq());
    ImGui::InputFloat("wave amp", waterSurface->GetWaveAmp());
    ImGui::InputFloat("wave dz", waterSurface->GetWaveDz());
    ImGui::End();
    Render(index);
    Present(index);
    :  //other functions
}

参考

以下のような波のrenderingのデバッグ用にImGuiを導入したので、設定パラメータにwave speedなどが入っています。

4
3
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
4
3