ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.46k stars 10.34k forks source link

How to add shader effects to `ImGui::Image()`? #8179

Closed MubinMuhammad closed 5 hours ago

MubinMuhammad commented 6 days ago

Version/Branch of Dear ImGui:

Version 1.91.5, Branch: master

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

archlinux 6.6.56-1-lts + g++ 14.2.1

Full config/build information:

Used python script to build the project.

Details:

Is there a way to add shader effect to a texture who's ID is passed to ImGui::Image()? I've tried this by creating vertex shader who doesn't do anything and fragment shader which changes the pixel to be grayscale. The result is there is no change to Image displayed by ImGui::Image().

Then, I added a vertex buffer, made changes to vertex shader and displayed the image on to the OpenGL viewport, and I see that the image is grayscale. So, my question is why doesn't the image get changed in the ImGui::Image()? Tough they are using the same Texture ID.

How do add post-processing effects to image shown by ImGui::Image()?

Thanks.

Screenshots/Video:

image_effect_problem

Minimal, Complete and Verifiable Example code:

Requires a lot of code.

GamingMinds-DanielC commented 4 days ago

Since there is no code, I can only guess that you switched shaders, called ImGui::Image() and switched shaders back. That doesn't work since the function doesn't issue a draw command directly. It just constructs draw data that is rendered later. To switch shaders at the right time, you need to inject that shader switch into the draw commands. You can do that with ImDrawList::AddCallback().

MubinMuhammad commented 3 days ago

Thanks, for clarity, a pseudo code for the problem output will be:

...
const char *vertex_shader = R"
#version 330
// take input for color, texture_coords, colors
out vec2 v_tex_coords
void main() {
    v_texture_coords = texture_coords;
}
"

const char *fragment_shader = R"
#version 330

out vec4 out_color;
uniform sampler2D tex;

in vec4 v_tex_coords;

void main() {
    // calculate grayscale
    vec4  _o = texture(tex, v_tex_coords);
    float y = (_o.x + _o.y + _o.z) / 3;
    out_color = vec4(y, y, y, _o.w);
}
"
vertices[] = {
    //colors // texture coords  // colors
    {{},         {0.0f, 1.0f},       {}},
    {{},         {0.0f, 0.0f},       {}},
    {{},         {1.0f, 0.0f},       {}},
    {{},         {1.0f, 1.0f},       {}},
}

shader = shaderInit(vertex_shader, fragment_shader);
buffer = sendBufferToGPU(vertices);
unsigned int tex_id = textureInit("path/to/image.jpg");

while (window_is_open)  {
    ...
    bindBuffer(buffer.vao);
    bindTexture(tex_id);
    bindShader(shader);
    ImGui::Image(tex_id, {tex_width, tex_height));
    ...
}
...

As I could find any helpful info on ImDrawList::AddCallback(), it will be helpful if you can provide me a pseudo code or a good resource.

MubinMuhammad commented 8 hours ago

Since, my last comment, I have tested with many stuff and ended up using a framebuffer to store texture data, change the texture in the viewport and render that in the ImGui::Image().

Though it's solvable I will still keep this issue open. Just because to know if there any other(better) solutions exist. Thanks.

ocornut commented 5 hours ago

The other solution is already outlined in Daniel’s answer, it is to use AddCallback() to change the render state or shader for a given render.

The callback is then called in the actual render loop, and you can call bindShader() in the callback. If you search for AddCallback() in this repo you’ll find many references.

Calling bindShader() before ImGui::Image() has no effect since all rendering commands are queued and processed in the RenderDrawData() call.