Closed sirlis closed 1 year ago
You can render the scene into a texture and use the ImGui::Image()
or ImGui::GetWindowDrawList()->AddImage()
function to draw it onto the window
You can render the scene into a texture and use the
ImGui::Image()
orImGui::GetWindowDrawList()->AddImage()
function to draw it onto the window
Thanks for the suggestion, that is exactly the part I don't know how to achieve. I read the FAQs and get to know the two functions, but I don't know how to transform my existing rendered scene into a texture
and called by ImGui::GetWindowDrawList()->AddImage()
. That's why I provide so many code snipps, thinking someone may show me the way.
I'm not familiar with OpenGL but I found this, along with plenty of other examples: https://gamedev.stackexchange.com/questions/140693/how-can-i-render-an-opengl-scene-into-an-imgui-window
Have you tried that? What particular part are you having trouble with?
I'm not familiar with OpenGL but I found this, along with plenty of other examples: https://gamedev.stackexchange.com/questions/140693/how-can-i-render-an-opengl-scene-into-an-imgui-window
Have you tried that? What particular part are you having trouble with?
Thanks! I will try it immediately. I wonder if I can leave u a message if any further progress is made.
Sure, I'm open to learning OpenGL as well so leave your discord or telegram and I'll send a message
Sure, I'm open to learning OpenGL as well so leave your discord or telegram and I'll send a message
Thank you. I am working on downloading and using discord (plz search for : lihongjue
as my username, and I don't know if it is since I am new to discord). Currently I succesfully put my opengl rendered scene into the correct panel.
I used:
ImGui::Begin("GameWindow");
// Using a Child allow to fill all the space of the window.
// It also alows customization
if (ImGui::BeginChild("GameRender"))
// Get the size of the child (i.e. the whole draw size of the windows).
ImVec2 wsize = ImGui::GetWindowSize();
// Because I use the texture from OpenGL, I need to invert the V from the UV.
ImGui::Image((void*)(intptr_t)tex, wsize, ImVec2(0, 1), ImVec2(1, 0)); //<---warning here
ImGui::EndChild();
}
ImGui::End();
In addition, there are still some bugs: 1) the earth is not rendered at the center of the scene, which is should be. And there is deformation, and blurry (UNSOLVED, I raised another issue #6894 about this); 2) mouse drag/keybord callbacks only linstens to the old main window instead of the current imgui panel window (solved).
The second bug is solved by disable the callbacks (maybe disable only the framebuffersizecallback will work):
glfwSetErrorCallback(error_callback);
glfwSetKeyCallback(MainWindow, key_callback);
glfwSetFramebufferSizeCallback(MainWindow, framebuffer_size_callback);
glfwSetCursorPosCallback(MainWindow, mouse_callback);
glfwSetScrollCallback(MainWindow, scroll_callback);
glfwSetMouseButtonCallback(MainWindow, mouse_button_callback);
Please note that the part about rendering your scene into a texture is not really an Dear ImGui question but nevertheless is covered in our wiki for many graphics librairies: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
Please note that the part about rendering your scene into a texture is not really an Dear ImGui question but nevertheless is covered in our wiki for many graphics librairies: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
Thank you. There is one issue, when I draw a texture into a imgui window, and when the window is floating, double click it's title bar will cause error, like the last image in the above response.
Assertion failed: (g.CurrentWindowStack.Size == 1) && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"
However, I checked and confirmed that I had matched Begin/BeginChild
in the following code:
if (ImGui::Begin("Render Panel"))
{
ImVec2 pos = ImGui::GetCursorScreenPos();
// Using a Child allow to fill all the space of the window.
// It also alows customization
ImGui::BeginChild("GameRender");
// Get the size of the child (i.e. the whole draw size of the windows).
ImVec2 wsize = ImGui::GetWindowSize();
SCREEN_WIDTH_CURRENT = wsize.x;
SCREEN_HEIGHT_CURRENT = wsize.y;
// Because I use the texture from OpenGL, I need to invert the V from the UV.
// glViewport(0, 0, wsize.x, wsize.y);
ImGui::Image((ImTextureID)FBO_textureColorBuffer, wsize, ImVec2(0, 1), ImVec2(1, 0));
ImGui::EndChild();
ImGui::End();
}
Update
after test, if initialized with e.g. ImGui::Begin("Render Panel", &window_renderpanel_visible, 0);
will have no problem.
if initialized with e.g. ImGui::Begin("Render Panel");
will have program crash problem.
You ImGui::End() call is misplaced.
You ImGui::End() call is misplaced.
Got it. Thank you !
I used two other methods, and the one I ended up using is, in my opinion, superior to the officially suggested one, with the exception that you can't easily decouple the game and UI framerate and resolution as easily as with the image/FrameBuffer method. For full detail, see: https://gamedev.stackexchange.com/a/207560/174820
Essentially, you use a callback to render the code at exactly the right point in the UI (in your window code, before the UI that's supposed to overlay it, and it all works fine. The advantage over the image method is that you don't render parts that might be outside the window, and there's less hassle and overhead, so multiple simple viewports are much more feasible.
Here's some example code using Eigen (superfluous) with glfw, for OpenGL, but it should work similarly for any setup:
// Needs to be accessible in the callback, but specific to this callback
static ImVec2 renderSize;
static float renderScale;
static ImRect viewRect;
auto viewWin = ImGui::GetCurrentWindowRead();
renderScale = ImGui::GetIO().DisplayFramebufferScale.x;
renderSize = viewWin->Viewport->Size * renderScale;
viewRect = viewWin->InnerRect;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddCallback([&](const ImDrawList* dl, const ImDrawCmd* dc)
{
auto view = viewRect.ToVec4() * renderScale;
auto clip = dc->ClipRect * renderScale;
glViewport(view.x, renderSize.y-view.w, view.z-view.x, view.w-view.y);
glScissor(clip.x, renderSize.y-clip.w, clip.z-clip.x, clip.w-clip.y);
glClearColor(0.2f, 0.0, 0.2f, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable Depth for 3D scene
glDepthMask(GL_TRUE);
glClearDepth(0);
glDepthFunc(GL_GEQUAL);
glEnable(GL_DEPTH_TEST);
// TODO: Render 3D scene
}, nullptr);
draw_list->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
I'd recommend to put something like this in the official docs, or at least a reference to it as a better approach in the section here. Yes it's out of scope, but a common user case that I haven't seen solved this well before.
See my reply here for my current solution that I'll be using in my code from now on.
Thanks @Seneral! This is exactly what I was looking for. The key part I was missing was including imgui_internal.h for the GetCurrentWindowRead() function.
I'm doing something slightly different to get the framebuffer height. I get the ImGui::GetWindowViewport() and send it to my callback. I can then get the DrawData and use DrawData.DisplaySize.y * DrawData.FramebufferScale.y. I also use the DrawData.FramebufferScale instead of pixelRatio.
I imagine using the DrawData instead of glfwGetFramebufferSize() may be more resilient to using multiple viewports, although I haven't got that far yet.
Thanks for the improvements @gkoreman - my code had many errors due to hastily extracting it from my project, sorry about that. I'll edit it. I personally don't use multiple viewports feature because wayland (linux compositor) doesn't support such an use case yet and that's what I use lol
One warning though, DrawData is NULL for the first frame, so better use ImGui::GetWindowViewport().Size and one of ImGui::GetIO().DisplayFramebufferScale, ImGui::GetWindowDpiScale() or ImGui::GetWindowViewport().DpiScale - I don't know if DPI scale is the same
Edit 2: But be careful, current window/viewport WILL be NULL in the draw function, since it's not executed between a BeginFrame and Render!
Ok, updated the code and verified that it works just the same, at least on X11 and Wayland. Though scaling only worked properly on wayland, when in theory it should also work on X11, but I guess something is not implemented there. No matter, as long as wayland works:)
The code is now available here: https://gist.github.com/Seneral/b4b34a283539938869cd10b2d065a88c And it works quite nicely to integrate OpenGL views or smaller renders like icons into ImGui. You can also optionally apply the patch and then you can render only those views without updating and rendering the whole UI.
Version/Branch of Dear ImGui:
Version: Latest Branch: docking
Back-end/Renderer/Compiler/OS
Back-ends: imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp Compiler: gcc-13.1.0 Operating System: windows
My Issue/Question:
I followed the https://learnopengl.com/ step by step to produce a 3D scene. Now I want to place it into a imgui window.
After some code work I managed to arrange the window but I don't know how to put the background rendered scene into the imgui window (e.g. the Render Panel )
I read FAQs and get to know that I can render the scene into a texture and use the
ImGui::Image()
orImGui::GetWindowDrawList()->AddImage()
function to draw it onto the window. But I am confused about how to achieve render to texture according to my existing codes, especially that my model, shader, etc., is constructed in classes.What is actually rendered:
What I expected to see (I fake it, want the scene rendered fullscreen in the render panel):
Related Codes
I write a
Sphere
class like this:Then I wrote a
Shader
class like this:After that , at initialization:
and the code in the main render loop is like:
And finally, the framebuffer size callback :
Standalone, minimal, complete and verifiable example: (see https://github.com/ocornut/imgui/issues/2261)
Sorry that the project is too big that I don't know how to provide a minimal example. I think the above code snipps may be enough.