├── .clang-tidy ├── .gitignore ├── CMakeLists.txt ├── README.md ├── conanfile.txt ├── imgui.ini ├── result.png └── src └── main.cpp /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-modernize-use-trailing-return-type,-llvm*' 3 | WarningsAsErrors: '*' 4 | HeaderFilterRegex: '' 5 | FormatStyle: none 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build*/ 3 | bindings/ 4 | .vscode/ 5 | *.ini -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | project(OpenGL_ImGUI LANGUAGES CXX) 6 | 7 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 8 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | if(MSVC) 14 | add_compile_options( 15 | $<$:/MT> 16 | $<$:/MTd> 17 | $<$:/MT> 18 | ) 19 | endif() 20 | 21 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 22 | conan_basic_setup() 23 | 24 | set(target example) 25 | 26 | add_executable( ${target} 27 | ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/bindings/imgui_impl_glfw.cpp 29 | ${CMAKE_CURRENT_SOURCE_DIR}/bindings/imgui_impl_opengl3.cpp 30 | ) 31 | target_include_directories(${target} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/bindings) 32 | target_compile_definitions(${target} PUBLIC IMGUI_IMPL_OPENGL_LOADER_GLEW) 33 | 34 | target_link_libraries(${target} ${CONAN_LIBS}) 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An OpenGL Dear ImGui Example 2 | 3 | This example renders a triangle into a Dear ImGui Window with OpenGL/GLFW. I use conan to manage all external libraries. 4 | 5 | Find my blog post to this repository here: https://www.codingwiththomas.com/blog/rendering-an-opengl-framebuffer-into-a-dear-imgui-window 6 | 7 | **Please make sure to use a Conan version below 2.0 (for instance 1.57)** 8 | 9 | To build the project run Conan and cmake: 10 | 11 | ``` 12 | conan install . -if ./build --build missing 13 | cmake -S . -B ./build 14 | cmake --build ./build 15 | ``` 16 | 17 | And then you can run the example: `./build/bin/example` 18 | 19 | ![result](./result.png) -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | imgui/cci.20220621+1.88.docking 3 | glfw/3.3.6 4 | glew/2.2.0 5 | glad/0.1.36 6 | glm/0.9.9.8 7 | 8 | [generators] 9 | cmake 10 | 11 | [imports] 12 | ./res/bindings, imgui_impl_glfw.cpp -> ../bindings 13 | ./res/bindings, imgui_impl_opengl3.cpp -> ../bindings 14 | ./res/bindings, imgui_impl_glfw.h -> ../bindings 15 | ./res/bindings, imgui_impl_opengl3.h -> ../bindings 16 | ./res/bindings, imgui_impl_opengl3_loader.h -> ../bindings -------------------------------------------------------------------------------- /imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=60,60 3 | Size=400,400 4 | Collapsed=0 5 | 6 | [Window][My Scene] 7 | ViewportPos=664,200 8 | ViewportId=0x777CAF58 9 | Size=702,565 10 | Collapsed=0 11 | 12 | [Docking][Data] 13 | 14 | -------------------------------------------------------------------------------- /result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThoSe1990/opengl_imgui/d6d7541a0a07b5430e95086418b776ae3bd7a208/result.png -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "imgui.h" 8 | #include "imgui_impl_glfw.h" 9 | #include "imgui_impl_opengl3.h" 10 | 11 | 12 | const GLint WIDTH = 800; 13 | const GLint HEIGHT = 600; 14 | 15 | GLuint VAO; 16 | GLuint VBO; 17 | GLuint FBO; 18 | GLuint RBO; 19 | GLuint texture_id; 20 | GLuint shader; 21 | 22 | const char* vertex_shader_code = R"*( 23 | #version 330 24 | 25 | layout (location = 0) in vec3 pos; 26 | 27 | void main() 28 | { 29 | gl_Position = vec4(0.9*pos.x, 0.9*pos.y, 0.5*pos.z, 1.0); 30 | } 31 | )*"; 32 | 33 | const char* fragment_shader_code = R"*( 34 | #version 330 35 | 36 | out vec4 color; 37 | 38 | void main() 39 | { 40 | color = vec4(0.0, 1.0, 0.0, 1.0); 41 | } 42 | )*"; 43 | 44 | 45 | void create_triangle() 46 | { 47 | GLfloat vertices[] = { 48 | -1.0f, -1.0f, 0.0f, // 1. vertex x, y, z 49 | 1.0f, -1.0f, 0.0f, // 2. vertex ... 50 | 0.0f, 1.0f, 0.0f // etc... 51 | }; 52 | 53 | glGenVertexArrays(1, &VAO); 54 | glBindVertexArray(VAO); 55 | 56 | glGenBuffers(1, &VBO); 57 | glBindBuffer(GL_ARRAY_BUFFER, VBO); 58 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 59 | 60 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 61 | glEnableVertexAttribArray(0); 62 | 63 | glBindBuffer(GL_ARRAY_BUFFER, 0); 64 | glBindVertexArray(0); 65 | } 66 | 67 | void add_shader(GLuint program, const char* shader_code, GLenum type) 68 | { 69 | GLuint current_shader = glCreateShader(type); 70 | 71 | const GLchar* code[1]; 72 | code[0] = shader_code; 73 | 74 | GLint code_length[1]; 75 | code_length[0] = strlen(shader_code); 76 | 77 | glShaderSource(current_shader, 1, code, code_length); 78 | glCompileShader(current_shader); 79 | 80 | GLint result = 0; 81 | GLchar log[1024] = {0}; 82 | 83 | glGetShaderiv(current_shader, GL_COMPILE_STATUS, &result); 84 | if (!result) { 85 | glGetShaderInfoLog(current_shader, sizeof(log), NULL, log); 86 | std::cout << "Error compiling " << type << " shader: " << log << "\n"; 87 | return; 88 | } 89 | 90 | glAttachShader(program, current_shader); 91 | } 92 | 93 | void create_shaders() 94 | { 95 | shader = glCreateProgram(); 96 | if(!shader) { 97 | std::cout << "Error creating shader program!\n"; 98 | exit(1); 99 | } 100 | 101 | add_shader(shader, vertex_shader_code, GL_VERTEX_SHADER); 102 | add_shader(shader, fragment_shader_code, GL_FRAGMENT_SHADER); 103 | 104 | GLint result = 0; 105 | GLchar log[1024] = {0}; 106 | 107 | glLinkProgram(shader); 108 | glGetProgramiv(shader, GL_LINK_STATUS, &result); 109 | if (!result) { 110 | glGetProgramInfoLog(shader, sizeof(log), NULL, log); 111 | std::cout << "Error linking program:\n" << log << '\n'; 112 | return; 113 | } 114 | 115 | glValidateProgram(shader); 116 | glGetProgramiv(shader, GL_VALIDATE_STATUS, &result); 117 | if (!result) { 118 | glGetProgramInfoLog(shader, sizeof(log), NULL, log); 119 | std::cout << "Error validating program:\n" << log << '\n'; 120 | return; 121 | } 122 | } 123 | 124 | void create_framebuffer() 125 | { 126 | glGenFramebuffers(1, &FBO); 127 | glBindFramebuffer(GL_FRAMEBUFFER, FBO); 128 | 129 | glGenTextures(1, &texture_id); 130 | glBindTexture(GL_TEXTURE_2D, texture_id); 131 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 133 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 134 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); 135 | 136 | glGenRenderbuffers(1, &RBO); 137 | glBindRenderbuffer(GL_RENDERBUFFER, RBO); 138 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT); 139 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO); 140 | 141 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 142 | std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!\n"; 143 | 144 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 145 | glBindTexture(GL_TEXTURE_2D, 0); 146 | glBindRenderbuffer(GL_RENDERBUFFER, 0); 147 | } 148 | 149 | void bind_framebuffer() 150 | { 151 | glBindFramebuffer(GL_FRAMEBUFFER, FBO); 152 | } 153 | 154 | void unbind_framebuffer() 155 | { 156 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 157 | } 158 | 159 | void rescale_framebuffer(float width, float height) 160 | { 161 | glBindTexture(GL_TEXTURE_2D, texture_id); 162 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 163 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 164 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 165 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); 166 | 167 | glBindRenderbuffer(GL_RENDERBUFFER, RBO); 168 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); 169 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO); 170 | } 171 | 172 | 173 | 174 | 175 | int main() 176 | { 177 | if (!glfwInit()) { 178 | std::cout << "GLFW initialisation failed!\n"; 179 | glfwTerminate(); 180 | return 1; 181 | } 182 | 183 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 184 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 185 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 186 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 187 | 188 | // Create the window 189 | GLFWwindow *mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "My Window", NULL, NULL); 190 | if (!mainWindow) 191 | { 192 | std::cout << "GLFW creation failed!\n"; 193 | glfwTerminate(); 194 | return 1; 195 | } 196 | 197 | int bufferWidth, bufferHeight; 198 | glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight); 199 | glfwMakeContextCurrent(mainWindow); 200 | glewExperimental = GL_TRUE; 201 | 202 | if (glewInit() != GLEW_OK) { 203 | std::cout << "glew initialisation failed!\n"; 204 | glfwDestroyWindow(mainWindow); 205 | glfwTerminate(); 206 | return 1; 207 | } 208 | 209 | glViewport(0, 0, bufferWidth, bufferHeight); 210 | 211 | create_triangle(); 212 | create_shaders(); 213 | create_framebuffer(); 214 | 215 | 216 | IMGUI_CHECKVERSION(); 217 | ImGui::CreateContext(); 218 | ImGuiIO& io = ImGui::GetIO(); (void)io; 219 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; 220 | io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; 221 | io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; 222 | 223 | ImGui::StyleColorsDark(); 224 | ImGuiStyle& style = ImGui::GetStyle(); 225 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable){ 226 | style.WindowRounding = 0.0f; 227 | style.Colors[ImGuiCol_WindowBg].w = 1.0f; 228 | } 229 | 230 | ImGui_ImplGlfw_InitForOpenGL(mainWindow, true); 231 | ImGui_ImplOpenGL3_Init("#version 330"); 232 | 233 | while (!glfwWindowShouldClose(mainWindow)) 234 | { 235 | glfwPollEvents(); 236 | 237 | ImGui_ImplOpenGL3_NewFrame(); 238 | ImGui_ImplGlfw_NewFrame(); 239 | 240 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 241 | glClear(GL_COLOR_BUFFER_BIT); 242 | 243 | ImGui::NewFrame(); 244 | ImGui::Begin("My Scene"); 245 | 246 | const float window_width = ImGui::GetContentRegionAvail().x; 247 | const float window_height = ImGui::GetContentRegionAvail().y; 248 | 249 | rescale_framebuffer(window_width, window_height); 250 | glViewport(0, 0, window_width, window_height); 251 | 252 | ImVec2 pos = ImGui::GetCursorScreenPos(); 253 | 254 | ImGui::GetWindowDrawList()->AddImage( 255 | (void *)texture_id, 256 | ImVec2(pos.x, pos.y), 257 | ImVec2(pos.x + window_width, pos.y + window_height), 258 | ImVec2(0, 1), 259 | ImVec2(1, 0) 260 | ); 261 | 262 | ImGui::End(); 263 | ImGui::Render(); 264 | 265 | 266 | bind_framebuffer(); 267 | 268 | glUseProgram(shader); 269 | glBindVertexArray(VAO); 270 | glDrawArrays(GL_TRIANGLES, 0, 3); 271 | glBindVertexArray(0); 272 | glUseProgram(0); 273 | 274 | unbind_framebuffer(); 275 | 276 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 277 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) 278 | { 279 | GLFWwindow* backup_current_context = glfwGetCurrentContext(); 280 | ImGui::UpdatePlatformWindows(); 281 | ImGui::RenderPlatformWindowsDefault(); 282 | glfwMakeContextCurrent(backup_current_context); 283 | } 284 | 285 | glfwSwapBuffers(mainWindow); 286 | } 287 | 288 | ImGui_ImplOpenGL3_Shutdown(); 289 | ImGui_ImplGlfw_Shutdown(); 290 | ImGui::DestroyContext(); 291 | 292 | glDeleteFramebuffers(1, &FBO); 293 | glDeleteTextures(1, &texture_id); 294 | glDeleteRenderbuffers(1, &RBO); 295 | 296 | glfwDestroyWindow(mainWindow); 297 | glfwTerminate(); 298 | 299 | return 0; 300 | } --------------------------------------------------------------------------------