読者です 読者をやめる 読者になる 読者になる

WindowsでOpenGLES --- imgui導入

OpenGL上でGUIを実装するライブラリの一つに、ImGuiがあります。
GitHub - ocornut/imgui: Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies

数ファイル取り込むだけでGUIを実装できるため、簡単なソフトの作成には便利です。

今回はGLFW上でImGuiを動作させてみたいと思います。

GLFWに関しましては下記記事を参照してください。
WindowsでOpenGLES --- GLFW導入 - 何でもプログラミング

Immediate Mode GUI

このライブラリは "Immediate Mode GUI" という方式を採用しており、一般的な、ボタンオブジェクトを作ってテキストを変更して…といった "Retained Mode GUI" とは異なります。

Immediate Modeでは毎回GUIを描き直すため、既にあるGUIツリーを更新していくRetained Modeに比べて、わかりやすいコードになる傾向があります。

ただしライブラリ側でstatic変数に状態を持っていたりするため、Immediate Modeが完全優位というわけでもありません。

個人的には毎回GUIの作り直し(一部のみの更新をしない)が重要だと思いますので、Retained ModeであってもwebのReactのようなフレームワークがあれば変わらないと思います。

ファイル取り込み

GitHubからダウンロードし、下記ファイルをプロジェクトに追加します。

imgui_impl_glfw_gl3はexamples/opengl3_example/以下に入っています。

f:id:any-programming:20170425160757p:plain

imgui_impl_glfw_gl3.cppの修正

元のままではOpenGL3を利用するようになっていますので、OpenGLES3に対応するよう修正します。

// GLES3/gl3.hをincludeするように
-#include <GL/gl3w.h>
+#define GLFW_INCLUDE_ES3
+#define GL_GLEXT_PROTOTYPES

// GL_BLEND_SRCは存在しないのでGL_BLEND_SRC_ALPHAに
-GLint last_blend_src; glGetIntegerv(GL_BLEND_SRC, &last_blend_src);
-GLint last_blend_dst; glGetIntegerv(GL_BLEND_DST, &last_blend_dst);
+GLint last_blend_src; glGetIntegerv(GL_BLEND_SRC_ALPHA, &last_blend_src);
+GLint last_blend_dst; glGetIntegerv(GL_BLEND_DST_ALPHA, &last_blend_dst);

// Vertexシェーダのversion変更
-"#version 330\n"
+"#version 300 es\n"

// Fragmentシェーダのversion変更、precisionの設定
-"#version 330\n"
+"#version 300 es\n"
+"precision mediump float;\n"


アプリケーションコード

ImGui_ImplGlfwGL3_Initで初期化し、描画ごとにImGui_ImplGlfwGL3_NewFrameとImGui::Render()を呼び出します。

今回はImGui::ShowTestWindowを用いてサンプルを描画しています。

毎フレーム描画する必要がない場合はglfwPollEventsの代わりにglfwWaitEventsを利用してください。

フレームレートはglfwSwapInterval(1)でディスプレイと同期するようになるのですが、Windowsでは呼ばなくてもディスプレイと同期しているようです。

#define GLFW_INCLUDE_ES3
#define GL_GLEXT_PROTOTYPES
#include <GLFW\glfw3.h>

#include "imgui.h"
#include "imgui_impl_glfw_gl3.h"

#pragma comment(lib, "glfw3.lib")
#pragma comment(lib, "libGLESv2.lib")

int main()
{
    assert(glfwInit());

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);

    GLFWwindow* window = glfwCreateWindow(650, 350, "ImGui", NULL, NULL);    
    assert(window);
    glfwMakeContextCurrent(window);
       
    assert(ImGui_ImplGlfwGL3_Init(window, true));

    glfwSetWindowSizeCallback(window, [](GLFWwindow*, int w, int h) 
    {  
        glViewport(0, 0, w, h);
    });
        
    while (glfwWindowShouldClose(window) == GL_FALSE)
    {
        glfwPollEvents();

        glClearColor(0, 0, 0, 0);
        glClear(GL_COLOR_BUFFER_BIT);

        ImGui_ImplGlfwGL3_NewFrame();
        ImGui::ShowTestWindow();
        ImGui::Render();

        glfwSwapBuffers(window);
    }

    ImGui_ImplGlfwGL3_Shutdown();
    glfwTerminate();

    return 0;
}

f:id:any-programming:20170425182525p:plain