Using OpenGL 4.6 with OpenGL ES 3.2 (according to glxinfo -B) on Arch Linux.
While using the official wiki snippet code for rendering an image as a texture in imgui, I ran into the issue of the image being rendered as a completely black texture. Here is my (slightly) modified helper function from the wiki that loads an image using STB and puts it into a texture.
// Simple helper function to load an image into a OpenGL texture with common settings
bool load_texture_from_file(const char* filename, GLuint* out_texture, unsigned char **out_raw_image,
int* out_width, int* out_height, int* out_channels) {
unsigned char* image_data = stbi_load(filename, out_width, out_height, out_channels, 4);
if (image_data == NULL) {
printf("Failed to load image: %s\n", stbi_failure_reason());
return false;
}
// Create a OpenGL texture identifier
GLuint image_texture;
glGenTextures(1, &image_texture);
glBindTexture(GL_TEXTURE_2D, image_texture);
// Setup filtering parameters for display
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non power-of-two textures
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same
GLenum format;
if (*out_channels == 3)
format = GL_RGB;
else if (*out_channels == 4)
format = GL_RGBA;
else {
// Handle unsupported channel count
printf("Unsupported number of channels: %d\n", *out_channels);
stbi_image_free(image_data);
return false;
}
// Upload pixels into texture
#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, format, *out_width, *out_height, 0, format, GL_UNSIGNED_BYTE, image_data);
GLenum error = glGetError();
if (error != GL_NO_ERROR)
{
printf("OpenGL error after glTexImage2D: %x\n", error);
stbi_image_free(image_data);
return false;
}
*out_raw_image = image_data;
*out_texture = image_texture;
assert(image_texture != 0 && *out_texture != 0);
return true;
}
I am somewhat convinced that my issue lies somewhere in how I'm handling converting the image to a texture so imgui can render it, because I am not getting any errors loading the image using stb_image. Surprisingly, I'm not getting any OpenGL errors either from my load_texture_from_file
function. In my code, load_texture_from_file
is called within display_ui
which does the following:
void display_ui(const GLFWvidmode *mode) {
static int width, height, channels;
static char input[256] = "";
static bool show_original = false;
static bool show_preview = false;
static unsigned char *image_data = NULL;
static GLuint texture = 0;
ImGui::Begin("Workshop", nullptr, ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
ImVec2 main_panel_size = ImVec2(2 * ImGui::GetContentRegionAvail().x / 3,
ImGui::GetContentRegionAvail().y - 75);
ImVec2 side_panel_size = ImVec2(ImGui::GetContentRegionAvail().x / 3,
(ImGui::GetContentRegionAvail().y - 80) / 2);
ImGui::SetWindowSize(main_panel_size);
ImVec2 parent_cursor_start = ImGui::GetCursorPos();
ImGui::BeginChild("Main panel", main_panel_size, true);
ImGui::SetNextItemWidth(200.0f);
if (ImGui::BeginTabBar("tab_bar", ImGuiTabBarFlags_none))
{
if (ImGui::BeginTabItem("Original image"))
{
if(show_original) {
ImGui::Text("pointer = %p", texture);
ImGui::Text("size = %d x %d", width, height);
ImGui::Image((void*)(intptr_t)&texture, ImVec2(width, height));
}
ImGui::EndTabItem();
}
}
// ... irrelevant code is omitted for brevity
// mostly just unrelated gui stuff
if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) {
if (ImGuiFileDialog::Instance()->IsOk()) {
std::string file_path_name = ImGuiFileDialog::Instance()->GetFilePathName();
std::string file_path = ImGuiFileDialog::Instance()->GetCurrentPath();
sprintf(input, "%s", file_path_name.c_str());
}
ImGuiFileDialog::Instance()->Close();
}
// process file path image if user clicks button
if(ImGui::Button("Open input file")) {
if(!load_texture_from_file(input, &texture, &image_data, &width, &height, &channels)) {
ImGui::OpenPopup("Error loading image");
show_original = false;
} else {
show_original = true;
}
}
if(ImGui::BeginPopup("Error loading image")) {
ImGui::Text("Error loading image, select a valid path");
if(ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::End();
}
In case this might be related to some improper setup of OpenGL or something along those lines, note that I call display_ui()
inside the main imgui rendering while loop shown below:
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
void render_gui() {
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return;
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char* glsl_version = "#version 100";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(__APPLE__)
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
// Create window with graphics context
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "Pixelify", nullptr, nullptr);
if (window == nullptr)
return;
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSwapInterval(1); // Enable vsync
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
// Setup Dear ImGui style
ImGui::StyleColorsDark();
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
// Main loop
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(mode->width, mode->height));
display_ui(mode);
// Rendering
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
GLFWwindow* backup_current_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_current_context);
}
glfwSwapBuffers(window);
}
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
return;
}
For context, heres the result I get while using this sample image as a test case for rendering it as a texture:
You are not supposed to pass &texture
to ImGui::Image
but texture
:
ImGui::Image((void*)(intptr_t)texture, ImVec2(width, height));
The backend code undoes this conversion without trying to dereference it as a pointer:
GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()));