How to Display Custom Images Using ImTextureID in ImGui

ImTextureID is an opaque, backend-specific handle (such as an OpenGL GLuint, DirectX ID3D11ShaderResourceView*, or Vulkan VkDescriptorSet) that you cast from your native GPU texture and pass to ImGui::Image() to render custom images uploaded with your own graphics code.

In the ocornut/imgui immediate-mode GUI library, displaying custom images requires bridging your renderer's native texture objects with ImGui's drawing API. Since ImGui is graphics-API agnostic, it relies on ImTextureID to store references to GPU textures without knowing the underlying implementation details. This article explains how to obtain, cast, and use these identifiers across major rendering backends.

What is ImTextureID?

In imgui.h, the default type definition is typedef ImU64 ImTextureID;, creating a 64-bit value capable of storing either an integer or a pointer depending on the backend's requirements. Each official backend in the backends/ directory reinterprets this value according to its native resource type:

  • OpenGL 3: Uses a GLuint texture name
  • DirectX 11: Expects an ID3D11ShaderResourceView* pointer
  • Vulkan: Stores a VkDescriptorSet handle
  • SDL Renderer: Uses SDL_Texture*

The newer ImTextureRef type provides additional safety wrappers, but the underlying principle remains identical: ImGui treats the value as an opaque token and passes it directly to the backend's rendering implementation.

The Three-Step Workflow

To display a custom image, you must handle GPU resource management yourself before involving ImGui:

  1. Upload: Load your image data and create a GPU texture using your renderer's API (OpenGL, DirectX, Vulkan, etc.)
  2. Cast: Convert the native texture handle to ImTextureID using a simple cast
  3. Draw: Pass the ID to ImGui::Image() or ImGui::ImageButton()

During the render loop, ImGui stores this ID in an ImDrawCmd structure. The backend's RenderDrawData() function retrieves it via ImDrawCmd::GetTexID() and binds the corresponding GPU resource before issuing the draw call.

Backend-Specific Implementation Examples

The exact casting depends on your graphics API. Here are minimal implementations for three major backends.

OpenGL 3

After uploading your image with glTexImage2D or similar, you receive a GLuint texture ID. Cast this directly to ImTextureID:

// Your OpenGL upload code produces: GLuint myTexture;
ImTextureID tex_id = (ImTextureID)myTexture;

ImGui::Begin("Custom Image");
ImGui::Image(tex_id,
             ImVec2(256, 256),                // display size
             ImVec2(0, 0), ImVec2(1, 1),     // UV coordinates (full texture)
             ImVec4(1,1,1,1),                // tint color (white = no tint)
             ImVec4(0,0,0,0));               // border color (transparent)
ImGui::End();

As documented in backends/imgui_impl_opengl3.h, the backend expects ImTextureID to contain a valid OpenGL texture name.

DirectX 11

For DirectX 11, you create an ID3D11ShaderResourceView from your texture and cast the pointer:

// Your D3D11 code produces: ID3D11ShaderResourceView* srv;
ImTextureID tex_id = (ImTextureID)srv;

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

The DirectX 11 backend implementation in backends/imgui_impl_dx11.h dereferences this pointer to set the shader resource view before drawing.

Vulkan

Vulkan requires descriptor sets for binding textures. Use the descriptor set returned by ImGui_ImplVulkan_AddTexture() or your own allocation:

// VkDescriptorSet ds = ... (created via ImGui_ImplVulkan_AddTexture or manual)
ImTextureID tex_id = (ImTextureID)ds;

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

According to backends/imgui_impl_vulkan.h, the backend binds this descriptor set during command buffer recording.

General Pattern

Regardless of backend, the ImGui API remains consistent:

ImTextureID myTexID = /* your native handle */;
ImGui::Begin("Image Window");
ImGui::Image(myTexID, ImVec2(width, height));
ImGui::End();

Only the method of obtaining and casting the native handle changes between renderers.

Rendering Pipeline Details

Internally, ImGui stores texture references in ImDrawCmd structures defined in imgui_internal.h. When you call ImGui::Image(), the library creates a draw command containing the ImTextureID you provided.

During the backend's RenderDrawData() implementation (found in files like imgui_impl_opengl3.cpp or imgui_impl_dx11.cpp), the code iterates through draw commands and retrieves the texture ID:

// Conceptual backend code
ImTextureID tex_id = draw_cmd->GetTexID();
// Backend binds the appropriate GPU resource using tex_id

This abstraction allows ImGui to remain graphics-API neutral while supporting hardware-accelerated texture rendering.

Safety Conventions and Invalid Textures

By default, ImGui considers zero (0 or nullptr) as an invalid texture ID. If you store texture indices, offsets, or custom handles in ImTextureID, define ImTextureID_Invalid in your imconfig.h file to avoid collisions with this convention:

// In imconfig.h
#define ImTextureID_Invalid ((ImTextureID)-1)

This prevents ImGui from interpreting your valid texture index 0 as "no texture."

Summary

  • ImTextureID is a 64-bit opaque value defined in imgui.h that stores backend-specific texture handles
  • Cast your native GPU handle (OpenGL GLuint, DirectX ID3D11ShaderResourceView*, Vulkan VkDescriptorSet, etc.) to ImTextureID before passing it to ImGui::Image()
  • ImGui does not load images; you must upload image data to the GPU using your own renderer code
  • Backends consume the ID during RenderDrawData() by calling ImDrawCmd::GetTexID() and binding the corresponding resource
  • Define ImTextureID_Invalid in imconfig.h if you use non-pointer values to avoid the zero-is-invalid convention

Frequently Asked Questions

What type should I cast my texture handle to?

Cast your native texture handle directly to ImTextureID (which is ImU64 by default). For OpenGL, cast your GLuint to ImTextureID; for DirectX 11, cast your ID3D11ShaderResourceView* pointer; for Vulkan, cast the VkDescriptorSet. The backend reinterprets the 64-bit value appropriately.

Does ImGui load images from disk for me?

No. ImGui is a GUI library only and does not handle image loading, decoding, or GPU upload. You must load image files (PNG, JPG, etc.) using a library like stb_image, upload the data to your GPU with OpenGL, DirectX, Vulkan, or other APIs, then pass the resulting native handle to ImGui as an ImTextureID.

Why is my texture showing as black or white in ImGui?

If your texture appears solid black or white, ensure your image data uploaded successfully to the GPU and that you are passing the correct native handle. Also verify that your UV coordinates (the third and fourth parameters to ImGui::Image()) map to valid regions of your texture—using (0,0) to (1,1) displays the full texture.

Can I use multiple different textures in one ImGui window?

Yes. You can call ImGui::Image() with different ImTextureID values as many times as needed within a single window. ImGui automatically generates separate draw commands with the appropriate texture IDs, and the backend handles binding the correct texture for each draw call 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 →