imguiのwindowの中で3d cubeの描画をしてやった

#はじめに
久しぶりの投稿になります。pythonでは速度が足りなくなったのでc++に移植作業をしていたのですが、描画をどうしようかということになりまして、imguiなるものをいじっています。公式にはopenglのオブジェクトをimguiのwindowの中で描画をするようなチュートリアルが見当たらなかったのでかなり色々と遠回りしましたが、複数個のcubeをimguiのウィンドウの中に描画することができるようになったので投稿します。下にプログラムを起動したイメージを示します
![Screenshot from 2020-01-12 18-52-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162025/03dc845a-3bc3-0919-e298-a9eed4753ee9.png)


#検証した環境・必要なライブラリ
- ubuntu18.04
- glfw3
- glew
- glm
- imgui

# code

``` main.cpp
// dear imgui: standalone example application for GLFW + OpenGL 3, using programmable pipeline
// If you are new to dear imgui, see examples/README.txt and documentation at the top of imgui.cpp.
// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <iostream> //cerr
#include <fstream>
#include <vector>
#include <array>
#include <GL/glew.h>    // Initialize with glewInit()

// Include glfw3.h after our OpenGL definitions
#include <GLFW/glfw3.h>

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <shader.hpp>

class texture_maker{
public:
    // width,height
    GLint width,height;
    
    // vao/vbo
    std::vector<GLuint> vvao;
    std::vector<std::array<GLuint, 2>> vvbo;

    // shader
    GLuint program;
    GLuint mvp;
    
    // texture
    GLuint framebuffer;
    GLuint depthbuffer;
    GLuint texture;
    GLenum drawbuffer;

    texture_maker() {};
    void config(GLint width, GLint height, std::vector<float> box);
    void render();
private:
};
void texture_maker::config(GLint width, GLint height, std::vector<float> box){
    // set width, height
    this->width = width;
    this->height = height;
    GLfloat triangleColor[] = {
        0.583f,  0.771f,  0.014f,
        0.609f,  0.115f,  0.436f,
        0.327f,  0.483f,  0.844f,
        0.822f,  0.569f,  0.201f,
        0.435f,  0.602f,  0.223f,
        0.310f,  0.747f,  0.185f,
        0.597f,  0.770f,  0.761f,
        0.559f,  0.436f,  0.730f,
        0.359f,  0.583f,  0.152f,
        0.483f,  0.596f,  0.789f,
        0.559f,  0.861f,  0.639f,
        0.195f,  0.548f,  0.859f,
        0.014f,  0.184f,  0.576f,
        0.771f,  0.328f,  0.970f,
        0.406f,  0.615f,  0.116f,
        0.676f,  0.977f,  0.133f,
        0.971f,  0.572f,  0.833f,
        0.140f,  0.616f,  0.489f,
        0.997f,  0.513f,  0.064f,
        0.945f,  0.719f,  0.592f,
        0.543f,  0.021f,  0.978f,
        0.279f,  0.317f,  0.505f,
        0.167f,  0.620f,  0.077f,
        0.347f,  0.857f,  0.137f,
        0.055f,  0.953f,  0.042f,
        0.714f,  0.505f,  0.345f,
        0.783f,  0.290f,  0.734f,
        0.722f,  0.645f,  0.174f,
        0.302f,  0.455f,  0.848f,
        0.225f,  0.587f,  0.040f,
        0.517f,  0.713f,  0.338f,
        0.053f,  0.959f,  0.120f,
        0.393f,  0.621f,  0.362f,
        0.673f,  0.211f,  0.457f,
        0.820f,  0.883f,  0.371f,
        0.982f,  0.099f,  0.879f
    };

    // デプステストを有効にする
    glEnable(GL_DEPTH_TEST);
    // 前のものよりもカメラに近ければ、フラグメントを受け入れる
    glDepthFunc(GL_LESS);

    // vao/vbo
    int size = box.size();
    for(int i=0,j=0;i<size;i+=6,j++){
        GLfloat Vertex[] = {
            box[i]         ,box[i+1]         ,box[i+2]+box[i+5],//1
            box[i]         ,box[i+1]+box[i+4],box[i+2]+box[i+5],
            box[i]+box[i+3],box[i+1]         ,box[i+2]+box[i+5],

            box[i]         ,box[i+1]+box[i+4],box[i+2]+box[i+5],//2
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]+box[i+5],
            box[i]+box[i+3],box[i+1]         ,box[i+2]+box[i+5],

            box[i]         ,box[i+1]         ,box[i+2]         ,//3
            box[i]         ,box[i+1]+box[i+4],box[i+2]         ,
            box[i]+box[i+3],box[i+1]         ,box[i+2]         ,

            box[i]         ,box[i+1]+box[i+4],box[i+2]         ,//4
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]         ,
            box[i]+box[i+3],box[i+1]         ,box[i+2]         ,

            box[i]         ,box[i+1]+box[i+4],box[i+2]+box[i+5],//5
            box[i]         ,box[i+1]+box[i+4],box[i+2]         ,
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]+box[i+5],

            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]+box[i+5],//6
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]         ,
            box[i]         ,box[i+1]+box[i+4],box[i+2]         ,
            
            box[i]         ,box[i+1]         ,box[i+2]         ,//7
            box[i]+box[i+3],box[i+1]         ,box[i+2]+box[i+5],
            box[i]         ,box[i+1]         ,box[i+2]+box[i+5],

            box[i]         ,box[i+1]         ,box[i+2]         ,//8
            box[i]+box[i+3],box[i+1]         ,box[i+2]         ,
            box[i]+box[i+3],box[i+1]         ,box[i+2]+box[i+5],

            box[i]         ,box[i+1]         ,box[i+2]+box[i+5],//9
            box[i]         ,box[i+1]+box[i+4],box[i+2]+box[i+5],
            box[i]         ,box[i+1]         ,box[i+2]         ,

            box[i]         ,box[i+1]+box[i+4],box[i+2]+box[i+5],//10
            box[i]         ,box[i+1]+box[i+4],box[i+2]         ,
            box[i]         ,box[i+1]         ,box[i+2]         ,

            box[i]+box[i+3],box[i+1]         ,box[i+2]+box[i+5],//11
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]+box[i+5],
            box[i]+box[i+3],box[i+1]         ,box[i+2]         ,

            box[i]+box[i+3],box[i+1]         ,box[i+2]         ,//12
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]         ,
            box[i]+box[i+3],box[i+1]+box[i+4],box[i+2]+box[i+5],
        };
        
        this->vvao.push_back((GLuint)0);
        
        std::array<GLuint,2> vbo ={(GLuint)0,(GLuint)0};
        this->vvbo.push_back(vbo);

        GLuint a = this->vvao[j];
        
        std::array<GLuint,2> t_b = this->vvbo[j];
        GLuint b[] = {t_b[0],t_b[1]};

        glGenVertexArrays(1, &a);
        glBindVertexArray(a);
        
        glGenBuffers(2, b);
        glBindBuffer(GL_ARRAY_BUFFER, b[0]);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex), Vertex, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glBindBuffer(GL_ARRAY_BUFFER, b[1]);
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
        glBufferData(GL_ARRAY_BUFFER, sizeof(triangleColor), triangleColor, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        glBindVertexArray(0);
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

        this->vvao[j] = a;

        t_b[0] = b[0];
        t_b[1] = b[1];
        this->vvbo[j] = t_b;
    }
   
    // shader
    this->program = LoadShaders( "point.vert", "point.frag" );
    this->mvp = glGetUniformLocation(this->program, "MVP");
    
    // texture 
    glGenFramebuffers(1, &this->framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, this->framebuffer);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glGenTextures(1, &this->texture);
    glBindTexture(GL_TEXTURE_2D, this->texture);

    glTexImage2D(GL_TEXTURE_2D,    // target
                 0,                // mipmap level 
                 GL_RGB,           // internal format
                 width,            // scene width
                 height,           // scene height
                 0,                // border
                 GL_RGB,           // format
                 GL_UNSIGNED_BYTE, // type 
                 (void*)0);        // image buffer

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    // depthbuffer
    glGenRenderbuffers(1, &this->depthbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, this->depthbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, this->depthbuffer);

    // 出力先のバッファを GL_COLOR_ATTACHMENT0 にする
    this->drawbuffer = GL_COLOR_ATTACHMENT0;

    // 初期化終わり
    glFramebufferTexture(GL_FRAMEBUFFER, this->drawbuffer, this->texture, 0);
    glBindFramebuffer(GL_FRAMEBUFFER,0);
}

void texture_maker::render(){
    glBindFramebuffer(GL_FRAMEBUFFER, this->framebuffer);
    glDrawBuffers(1, &this->drawbuffer);

    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glViewport(0,0, this->width, this->height);

	// Projection matrix : 45� Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
	glm::mat4 Projection = glm::perspective(glm::radians(45.0f), 4.0f / 3.0f, 0.1f, 100.0f);
	// Or, for an ortho camera :
	//glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // In world coordinates
	
	// Camera matrix
	glm::mat4 View       = glm::lookAt(
								glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
								glm::vec3(0,0,0), // and looks at the origin
								glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
						   );
	// Model matrix : an identity matrix (model will be at the origin)
	glm::mat4 Model      = glm::mat4(1.0f);
	// Our ModelViewProjection : multiplication of our 3 matrices
	glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around
    
    glUseProgram(this->program);
    glUniformMatrix4fv(this->mvp, 1, GL_FALSE, &MVP[0][0]);

    for(int i=0;i < this->vvao.size();i++){
        GLuint a = this->vvao[i];
        
        std::array<GLuint,2> t_b = this->vvbo[i];
        GLuint b[] = {t_b[0],t_b[1]};

        glBindVertexArray(a);

        glBindBuffer(GL_ARRAY_BUFFER, b[0]);
        glBindBuffer(GL_ARRAY_BUFFER, b[1]);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        
        glDrawArrays(GL_TRIANGLES, 0, 3*12);

        glBindVertexArray(0);
    }

    glUseProgram(0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

static void glfw_error_callback(int error, const char* description)
{
    fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}

int main(int, char**)
{
    // Setup window
    glfwSetErrorCallback(glfw_error_callback);
    if (!glfwInit())
        return 1;//glfwの初期化に失敗

    // GL 3.0 + GLSL 130
    const char* glsl_version = "#version 330 core";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//opengl3.#を使用する
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);//opengl #.0
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);  // 3.2+ only
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);            // 3.0+ only


    // Create window with graphics context
    GLFWwindow* window = glfwCreateWindow(1280, 720, "hello", NULL, NULL);
    if (window == NULL)
        return 1;
    glfwMakeContextCurrent(window);//作成したウィンドウをopenglの処理対象にする
    glfwSwapInterval(1); // Enable vsync 垂直同期のタイミングを待つ

    // Initialize OpenGL loader glewの初期化
    bool err = glewInit() != GLEW_OK;
    if (err)
    {
        fprintf(stderr, "Failed to initialize OpenGL loader!\n");
        return 1;
    }

	// Enable depth test
	glEnable(GL_DEPTH_TEST);
	// Accept fragment if it closer to the camera than the former one
	glDepthFunc(GL_LESS); 

	// Cull triangles which normal is not towards the camera
	//glEnable(GL_CULL_FACE);

    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
    //ImGui::StyleColorsClassic();

    // Setup Platform/Renderer bindings
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init(glsl_version);

    // Our state
    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

	// Dark blue background
	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

    std::vector<float> _box1 = {
        -1.0,-1.0,-1.0 ,1.0,1.0,1.0,
        -2.0,-2.0,-1.0 ,1.0,1.0,1.0,
         1.0,-1.0,-1.0 ,1.0,1.0,1.0
    };//最小の座標　xyzの幅
    texture_maker tex;
    tex.config((GLint)300,(GLint)300,_box1);

    // Main loop
    while (!glfwWindowShouldClose(window))//windowが閉じられるまで繰り返す
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //glewWaitEvents();//イベントが発生するまで待つ
        // Start the Dear ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        //create the imgui window
        ImGui::Begin("FBO");
        {
            //the position of the window when it gets created is going to be the mouse position
            ImVec2 pos = ImGui::GetCursorScreenPos();
            tex.render();
            ImGui::Image((void *)tex.framebuffer, ImVec2(tex.width,tex.height));
        }
        //end the imgui window
        ImGui::End();

        // Rendering
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwPollEvents();//イベントが発生していれば取り出す。イベントの有無にかかわらず次に進む
        glfwSwapBuffers(window);//カラーバッファーを入れ替える
    }

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();

    glfwDestroyWindow(window);
    glfwTerminate();//終了前に必ず呼ぶ必要のある関数

    return 0;
}

```
#注意事項
shedarのプログラムは[opengl tutorial](https://github.com/opengl-tutorials/ogl)さんのコードを拝借しました。

#参考にしたサイト
- [lambda domain様](http://lambda-domain.com/ryo1blog/imgui-tutorial-2/)
- [opengl tutorial様](http://www.opengl-tutorial.org/jp/intermediate-tutorials/tutorial-14-render-to-texture/)