How to Display Images Using ImTextureID in Dear ImGui

ImTextureID is a backend-specific texture handle that you cast from native GPU resources—such as an OpenGL texture name, a Vulkan descriptor set, or a DirectX shader resource view—and pass to ImGui::Image() to render the image within your immediate-mode UI.

Dear ImGui (ocornut/imgui) does not contain any image-loading code. Instead, it relies on your rendering backend to manage GPU textures and exposes a lightweight abstraction called ImTextureID (defined in imgui.h) that references those native resources. During the UI rendering pass, Dear ImGui stores this identifier inside draw commands (ImDrawCmd::TexID) so the backend can bind the correct texture before issuing draw calls.

Understanding ImTextureID

In imgui.h at line 339, ImTextureID is defined as a 64-bit integer type that can hold either a pointer or an integer handle:

typedef ImU64 ImTextureID;    // Default: a 64-bit value for pointer or integer handles
// void* ImTextureID;           // Alternative: if you define ImTextureID as a pointer in imconfig.h

This type is deliberately opaque. Dear ImGui never interprets the value; it merely stores it and passes it back to your backend during rendering. The actual meaning depends entirely on which graphics API you use.

Backend-Specific Texture Types

Each official backend documents what native type it expects for ImTextureID. You must cast your GPU resource handle to ImTextureID using an appropriate cast (typically (ImTextureID)(intptr_t) or direct pointer casts).

  • OpenGL 3 (GLSL): GLuint texture names.
  • Vulkan: VkDescriptorSet obtained via ImGui_ImplVulkan_AddTexture().
  • DirectX 11: ID3D11ShaderResourceView*.
  • DirectX 12: D3D12_GPU_DESCRIPTOR_HANDLE or similar descriptor handles.
  • SDL2/SDL3 Renderer: SDL_Texture*.

Reference the backend headers (e.g., imgui_impl_opengl3.h, imgui_impl_vulkan.h, imgui_impl_dx11.h) to confirm the expected type for your specific rendering backend.

Displaying Images with ImGui::Image

The primary widget for rendering an image is ImGui::Image(), implemented in imgui.cpp. Its signature accepts texture coordinates for UV mapping, tint colors, and border colors:

void ImGui::Image(
    ImTextureID tex_id,
    const ImVec2& size,
    const ImVec2& uv0 = ImVec2(0, 0),
    const ImVec2& uv1 = ImVec2(1, 1),
    const ImVec4& tint_col = ImVec4(1, 1, 1, 1),
    const ImVec4& border_col = ImVec4(0, 0, 0, 0)
);

ImGui::ImageButton() uses an identical signature but additionally handles button input logic. Both functions pack the provided tex_id into an ImDrawCmd structure that the backend reads during ImDrawCmd::TexID processing.

OpenGL 3 Example

Load an image with stb_image, create a texture, then cast the GLuint to ImTextureID:

// Load image data
int w, h, channels;
unsigned char* data = stbi_load("avatar.png", &w, &h, &channels, 4);
if (!data) return;

// Create OpenGL texture
GLuint gl_texture;
glGenTextures(1, &gl_texture);
glBindTexture(GL_TEXTURE_2D, gl_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);

// Cast to ImTextureID
ImTextureID my_texture_id = (ImTextureID)(intptr_t)gl_texture;

// Render in ImGui
ImGui::Begin("Image Viewer");
ImGui::Image(my_texture_id, ImVec2((float)w, (float)h));
ImGui::End();

Vulkan Example

Register your VkImageView and VkSampler with the backend, which returns a VkDescriptorSet that serves as the ImTextureID:

// Create your VkImageView and VkSampler elsewhere...

VkDescriptorSet descriptor_set = ImGui_ImplVulkan_AddTexture(
    sampler, 
    image_view, 
    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
);

ImTextureID tex_id = (ImTextureID)descriptor_set;

ImGui::Begin("Vulkan Texture");
ImGui::Image(tex_id, ImVec2(300, 200));
ImGui::End();

DirectX 11 Example

Pass your ID3D11ShaderResourceView pointer directly as the texture ID:

ID3D11ShaderResourceView* srv = LoadMyTexture("sprite.dds");  // Your loader
ImTextureID tex_id = (ImTextureID)srv;

ImGui::Begin("DX11 Image");
ImGui::Image(tex_id, ImVec2(128, 128));
ImGui::End();

SDL2 Renderer Example

When using the SDL2 rendering backend, cast SDL_Texture*:

SDL_Surface* surface = IMG_Load("icon.png");
SDL_Texture* sdl_texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);

ImTextureID tex_id = (ImTextureID)sdl_texture;

ImGui::Begin("SDL Texture");
ImGui::Image(tex_id, ImVec2(80, 80));
ImGui::End();

Texture Lifetime and Synchronization

The GPU texture referenced by ImTextureID must remain valid for the entire frame. Dear ImGui queues draw commands, so if you destroy a texture immediately after calling ImGui::Image(), the backend will attempt to bind an invalid resource during the subsequent render pass.

Starting with v1.92, Dear ImGui introduces ImTextureData structures that backends can use to request asynchronous texture creation. Check ImTextureData::Status == ImTextureStatus_WantCreate in your backend code to handle textures that need to be created or uploaded before the draw command executes.

ImTextureRef vs. ImTextureID (v1.92+)

From v1.92 onward, most internal APIs accept ImTextureRef, which is a variant type that can hold either an ImTextureID or a pointer to an ImTextureData structure. The public ImGui::Image() and ImageButton() entry points remain backward compatible and still accept raw ImTextureID values, ensuring existing code continues to compile while allowing new implementations to leverage the richer texture data interface.

Summary

  • ImTextureID is defined in imgui.h as a 64-bit value capable of storing pointers or integer handles to native GPU textures.
  • Rendering backends determine the actual type: OpenGL uses GLuint, Vulkan uses VkDescriptorSet, DirectX uses ID3D11ShaderResourceView*, and SDL2 uses SDL_Texture*.
  • Rendering workflow: Create the texture with your graphics API, cast the handle to ImTextureID, pass it to ImGui::Image() or ImageButton(), and ensure the texture lives until the frame is rendered.
  • Implementation location: The packing of texture IDs into draw commands occurs in imgui.cpp, while the binding happens in backend-specific render loops that read ImDrawCmd::TexID (declared in imgui_internal.h).

Frequently Asked Questions

What image formats does Dear ImGui support?

Dear ImGui does not load or decode image files. You must use a separate library (such as stb_image, stb_image.h, or platform-specific APIs) to decode PNG, JPEG, or DDS files into raw pixel buffers, then upload those buffers to your GPU using your graphics API (OpenGL, Vulkan, DirectX, etc.) before obtaining the handle to cast to ImTextureID.

Can I use ImTextureID with custom shaders?

Yes. Since ImTextureID is simply a handle passed through to the backend, you can extend your backend's renderer to interpret additional bits of the identifier for custom shader selection or sampler state. However, the standard ImGui::Image() API only sets the texture ID; any additional shader parameters must be managed by your application before calling ImGui::Render().

Why is my image appearing as a white or black square?

A white square typically indicates that the texture failed to load or the ImTextureID handle is null. Verify that your texture creation succeeded (check OpenGL error codes, validation layers in Vulkan, or HRESULT in DirectX) and confirm you are casting the handle correctly—e.g., (ImTextureID)(intptr_t)gl_tex_id for OpenGL. A black square often indicates incorrect UV coordinates or texture format issues where the alpha channel is zero.

How do I update a texture every frame for video playback?

Create the texture once (e.g., with GL_DYNAMIC_DRAW or D3D11_USAGE_DYNAMIC), then update the subresource or texture data each frame before your ImGui calls. Keep the same ImTextureID handle throughout; only the GPU memory contents need to change. Ensure you upload the new pixel data before the ImGui::Image() call so the backend sees the updated content during rendering.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →