Open andyborrell opened 3 years ago
I'm looking for a widget that allows interactive inspecting of a texture. In a similar way to how ImPlot allows the user to zoom and pan a graph, this would allow the user to zoom and pan a texture in an ImGui window.
Ideally it would have features like:
- Draw grid lines between pixels when the user zooms in more than a certain amount
- Show a tool tip with UV & RGB values of the pixel beneath the cursor
- Auto mapping of float textures to 0-1 range, with the option for the user to tweak the mapping
- A checkbox to show alpha channel as greyscale
- Individual RGB checkboxes to allow individual channels to be inspected
I would love it if something like this exists. If it doesn't exist then I'm thinking of starting it, so let me know if this sounds useful and if you have any other ideas for features.
I'm looking for this widget as you now and found the image inspect tool: https://github.com/CedricGuillemet/imgInspect, but there are something wrong with the magnified pixel's location, i'd be appreciate if you slove this problem and contact me.
imgInspect was curiously missing from our wiki list, added it now: https://github.com/ocornut/imgui/wiki/Useful-Extensions#image-manipulation
Thanks @IceyChiu & @ocornut. This looks useful!
To answer your original post: this would certainly be useful, but some of the features may be hard to implement in a way which makes them easy to share between code bases (but figuring out how to do so would largely increase the value of a widget like that).
That's a good point. I was only really thinking about how to make it work for OpenGL. imgInspect seems to work by taking a pointer to the texture data, thereby avoiding any API-specific code to access the texture data. I could also implement some of the features I described like that. But that won't work for the channel selection, float value mapping, etc.
I was originally planning on just writing a glsl shader to handle all that. But perhaps to make it more backend-agnostic I could have a per-backend function which accepts an ImGui texture handle and a structure describing the required color transformation and returns a new texture handle. It would then be up to this per-backend code to create the required shader and blit the texture to the new texture. Obviously this per-backend code would be smart enough to only build the shader once, and to reuse blit destination textures from frame to frame.
This approach does introduce an extra blit, but I feel like it would be easier to make work for a range of backends since it separates the code to transform the pixels from the code to render the texture in the UI.
Also this would be similar to the way different backends work in ImGui so I guess people wouldn't find it too strange to work with.
Any thoughts on this approach?
Any thoughts on this approach?
Personally I think it'd be easier to just implement a shader per-backend.
I'd actually go as far to say that I probably wouldn't use a widget that asked me to implement a CPU-side texture transformation to accomplish this. (If anything but that this sort of thing becomes pretty messy in the modern low-level APIs. You'd also run into perf issues with bigger textures.)
The CPU-centric approach also wouldn't work well for things like render targets since you'd have to download them from the GPU, perform the transformation, and then reupload. It also wouldn't be great if you're using compressed textures. (For both of these reasons, you should probably consider retrieving the RGB values for a specific coordinate would need to be backend-specific as well.)
If you really want to avoid requiring a custom shader specific to this widget, maybe let the backend expose which features are supported? For example, filtering out color channels is something that can be accomplished with the graphics APIs directly. (glColorMask
, OMSetBlendState.SampleMask
, D3D12_RENDER_TARGET_BLEND_DESC.RenderTargetWriteMask
, and VkPipelineColorBlendStateCreateInfo.blendConstants
to name a few.)
Somewhat besides the point, but with a custom shader you could let it deal with the pixel grid drawing as well.
@PathogenDavid I didn't mean to do it on the CPU. I just meant there would be a CPU-side function that should be implemented per-backend. Then it's up to that function to build its own shader and use it to transform from one texture to another on the GPU. On further thought it might be better to break this interface into a few functions:
Initialize() // implementation can build shader if needed
AllocateTexture (width, height, channels, type) // The widget code can manage reuse of textures so this won't be called very often)
Render (src, dst, transformConfig)
Note that these implementations would be included with the widget code so you wouldn't actually need to write anything to use it. (Not saying I would write them all, but hopefully if people found it useful they would add implementations for popular backends)
Your approach of just using a per-backend shader to render the texture directly sounds easier at first, but if I'm not overlooking something it would be hard to integrate into ImGui because there's no way to render an image in ImGui with a custom shader without changing the backend code.
if I'm not overlooking something it would be hard to integrate into ImGui because there's no way to render an image in ImGui with a custom shader without changing the backend code.
You'll never get a solution that doesn't require at least some backend support, but ImDrawList
has the ability to add arbitrary draw-time callbacks specifically for this sort of thing.
Here's a quick and dirty modification of the imgui_impl_opengl3
backend and example_glfw_opengl3
showing it being used to render ImGui widgets with only the red channel active. (I only modified imgui_impl_opengl3
for the sake of simplicity, The modifications could've been outside of it with a little bit of extra effort.)
ImGui::Begin("GH-4135 Shader Swap Demo");
ImVec2 textureSize = ImVec2((float)io.Fonts->TexWidth, (float)io.Fonts->TexHeight);
ImGui::Text("Before changing the shader...");
ImGui::GetWindowDrawList()->AddCallback(ImGui_ImplOpenGL3_SetRedOnlyShader, nullptr);
ImGui::Text("Now the shader is red-only!");
ImGui::Image(io.Fonts->TexID, textureSize);
ImGui::GetWindowDrawList()->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
ImGui::Text("All back to normal!");
ImGui::Image(io.Fonts->TexID, textureSize);
ImGui::End();
You'll never get a solution that doesn't require at least some backend support
I'm not sure this is true.
The great thing about widgets like imgInspect and ImPlot is that they don't require backend changes (or any changes to ImGui code at all).
Admittedly my proposal is kind of a backend "extension" but it's not a change to ImGui code or its included backends. I don't think your changes would be accepted since in my opinion this isn't something that should be a built-in part of ImGui and would be more appropriate as a separate project.
I don't think your changes would be accepted since in my opinion this isn't something that should be a built-in part of ImGui and would be more appropriate as a separate project.
That is not what I am proposing, that would be ludicrous.
Please re-read my comment, specifically this part:
(I only modified imgui_impl_opengl3 for the sake of simplicity, The modifications could've been outside of it with a little bit of extra effort.)
It’s not an easy thing to solve but you could at least probably provide compile-time opt-in implementations for a few api (eg OpenGL and DX11) that may be used as-is, and maybe your widget can work without them and just have a few features disabled.
At the end of the day better start making something you can use yourself (while keeping sharability in mind) and see where it goes.
@PathogenDavid after rereading I can see that your patch is indeed quite helpful. It seems like the only annoying thing, in the OpenGL implementation at least, is that if that code was outside of imgui_impl_opengl3 it would need to duplicate the code to calculate the projection matrix. I guess that's probably not a big deal though.
it would need to duplicate the code to calculate the projection matrix. I guess that's probably not a big deal though.
If you really wanted to avoid it you could use glGetUniform
before changing the shader. But yeah, it's pretty simple to just re-create it.
FYI, another thing to be aware of with OpenGL specifically is the issue that came up in https://github.com/ocornut/imgui/issues/4174.
Basically if you don't instruct the backend to use the newer OpenGL shaders, the vertex shader uses a driver-defined vertex attribute layout. The easiest solution is to tell the backend to use a newer version of OpenGL, but in your case it might be better to just explicitly call glGetAttribLocation
, glEnableVertexAttribArray
, and glVertexAttribPointer
with your shader for the sake of compatibility.
For @IceyChiu and anyone who might come across this thread in the future, Andy eventually created a fancy new texture inspector widget which is available here: https://github.com/ocornut/imgui/issues/4352
I'm looking for a widget that allows interactive inspecting of a texture. In a similar way to how ImPlot allows the user to zoom and pan a graph, this would allow the user to zoom and pan a texture in an ImGui window.
Ideally it would have features like:
I would love it if something like this exists. If it doesn't exist then I'm thinking of starting it, so let me know if this sounds useful and if you have any other ideas for features.